uakep 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
uakep-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anton Butsyk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
uakep-0.1.0/NOTICE ADDED
@@ -0,0 +1,58 @@
1
+ uakep
2
+ Copyright (c) 2026 Anton Butsyk. Licensed under the MIT License (see LICENSE).
3
+
4
+ uakep contains code ported from the projects below. Their notices are kept here.
5
+
6
+ -------------------------------------------------------------------------------
7
+ jkurwa — https://github.com/dstucrypt/jkurwa
8
+ Author: Ilya Petrov (Illya Petrov)
9
+ License: BSD, as declared in the project's package.json. The repository has no
10
+ LICENSE file; which BSD text applies is being confirmed with the author.
11
+
12
+ Ported into uakep: GF(2^m) arithmetic (lib/gf2m.js, lib/field.js), binary curves
13
+ and named curves (lib/curve.js, lib/point.js, lib/standard.js), ДСТУ 4145 signing
14
+ and verification including the fixed-length signing scalar (lib/models/Priv.js,
15
+ lib/models/Pub.js), key containers (lib/spec/keystore.js, lib/spec/pbes.js,
16
+ lib/spec/pfx.js), certificates and CMS/CAdES structures (lib/models/Certificate.js,
17
+ lib/models/Message.js, lib/spec/*.js).
18
+
19
+ -------------------------------------------------------------------------------
20
+ gost89 — https://github.com/dstucrypt/gost89
21
+ Author: Ilya Petrov (Illya Petrov)
22
+ License: BSD, as declared in the project's package.json.
23
+
24
+ Ported into uakep: the ГОСТ 28147-89 block cipher and its ECB, CFB and MAC modes
25
+ (lib/gost89.js), the ГОСТ 34.311-95 hash (lib/hash.js), the s-box tables
26
+ (lib/dstu.js) and the password-based key derivation (lib/util.js).
27
+
28
+ -------------------------------------------------------------------------------
29
+ ppolib — https://github.com/leon-mbs/ppolib
30
+ The four ДСТУ 7564:2014 substitution tables in src/uakep/kupyna.py were taken
31
+ from ppolib's src/algo/dstu7564.php. ppolib's license:
32
+
33
+ MIT License
34
+
35
+ Copyright (c) 2021 Leon
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ of this software and associated documentation files (the "Software"), to deal
39
+ in the Software without restriction, including without limitation the rights
40
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ copies of the Software, and to permit persons to whom the Software is
42
+ furnished to do so, subject to the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be included in
45
+ all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
53
+ SOFTWARE.
54
+
55
+ -------------------------------------------------------------------------------
56
+ Test data (repository only, not part of the package)
57
+ tests/data/*/ holds third-party test files. Each folder's README.md names the
58
+ source, version and license; tests/data/dstu-engine/LICENSE is Apache-2.0.
uakep-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: uakep
3
+ Version: 0.1.0
4
+ Summary: Ukrainian qualified electronic signatures (КЕП, ДСТУ 4145-2002) in pure Python
5
+ Keywords: dstu4145,kep,cades,prro,ukraine
6
+ Author: Anton Butsyk
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ License-File: NOTICE
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Security :: Cryptography
15
+ Requires-Dist: asn1crypto>=1.5.1
16
+ Requires-Python: >=3.11
17
+ Project-URL: Repository, https://github.com/butsyk/uakep
18
+ Project-URL: Changelog, https://github.com/butsyk/uakep/blob/main/CHANGELOG.md
19
+ Description-Content-Type: text/markdown
20
+
21
+ # uakep: Ukrainian КЕП signatures in pure Python
22
+
23
+ ## What this is
24
+
25
+ uakep is a pure-Python library for Ukrainian qualified electronic signatures (КЕП) on ДСТУ 4145-2002, ported from [jkurwa](https://github.com/dstucrypt/jkurwa). Its first job is to sign documents for the State Tax Service's fiscal server (ПРРО) in the exact format that server accepts, using the key files sellers already have.
26
+
27
+ No native binaries, no Java, no Node: the goal is `pip install` and sign.
28
+
29
+ **Українською.** uakep — бібліотека на чистому Python для кваліфікованого електронного підпису за ДСТУ 4145-2002, порт бібліотеки jkurwa. Вона читатиме ключі (Key-6.dat, .jks, .pfx) і підписуватиме документи у форматі CAdES з позначкою часу, як цього вимагає фіскальний сервер ДПС для програмних РРО.
30
+
31
+ ## Status
32
+
33
+ Pre-alpha. Milestone 0.1 is code-complete: uakep reads key files and certificates, and the tax service's samples and ЦЗО's examples verify in it. Milestone 0.2 is in progress: signature time-stamps are built and checked against the samples. Next is a document signed by uakep and accepted by the tax service's fiscal server in its test mode, the 0.2 gate (see the [roadmap](https://github.com/butsyk/uakep/blob/main/ROADMAP.md)).
34
+
35
+ ## Why it exists
36
+
37
+ Python has no open-source library that signs the way the fiscal server requires. The tax service publishes the protocol, XSD schemas and examples, but no client library in any language ([official description](https://tax.gov.ua/baneryi/programni-rro/opis-ari-fiskalnogo-servera/opis-ari--kontrolyuyuchogo-organu/), [Markdown mirror](https://github.com/VSydorenko/prro_docs)). Sending a document is a plain HTTP POST; signing it is the hard part.
38
+
39
+ | Library | Language | Source | Notes |
40
+ | --- | --- | --- | --- |
41
+ | [IIT signing library](https://iit.com.ua/downloads) | C, with a Python interface | Closed, native binary | Its downloads page publishes no license terms |
42
+ | [jkurwa](https://github.com/dstucrypt/jkurwa) | JavaScript | Open, BSD | Maintained since 2014; reads Key-6.dat, .jks and .pfx; adds signature time-stamps |
43
+ | [ppolib](https://github.com/leon-mbs/ppolib) | PHP | Open, MIT | Port of jkurwa, written for a CRM's ПРРО |
44
+ | [uacrypto](https://github.com/h0rn3t/uacrypto) | Go | Open | First published in November 2026 |
45
+
46
+ A Python service today needs a native binary or a second runtime beside it. uakep removes both.
47
+
48
+ ## First release: scope and non-goals
49
+
50
+ The first release does what the fiscal server needs and nothing more. The server's rules are in its [API description](https://github.com/VSydorenko/prro_docs), section «Порядок засвідчення повідомлень».
51
+
52
+ - **Keys:** read the private key from IIT Key-6.dat, PrivatBank .jks and PKCS#12 .pfx files, given the password.
53
+ - **Signature:** sign and verify with ДСТУ 4145-2002 on binary-field curves, named or given in the certificate, with the ГОСТ 34.311-95 hash (ДСТУ 7564 «Купина» where a certificate needs it).
54
+ - **Container:** build CAdES-E-T signed data with the document attached and the signer's certificate inside, and without issuer certificates, CRLs or OCSP answers.
55
+ - **Time-stamp:** add a signature time-stamp (RFC 3161) from the server named in the signer's certificate for online documents, and none for offline ones; never a content time-stamp, which the fiscal server refuses.
56
+ - **Certificates:** read owner name, РНОКПП or ЄДРПОУ, validity and key usage.
57
+ - **Answers:** unwrap the fiscal server's signed answers and verify its signature.
58
+
59
+ Not in the first release: encryption (EnvelopedData), XAdES, PAdES and ASiC, cloud signatures such as Дія.Підпис, hardware tokens, certificate lookup over CMP, OCSP and CRL checks, and a ПРРО client (receipt XML, shifts, offline mode).
60
+
61
+ ## API
62
+
63
+ A sketch of the intended use; names and arguments may change before 1.0.
64
+
65
+ ```python
66
+ from uakep import load_key, sign, unwrap
67
+
68
+ # Key-6.dat holds no certificate, so pass the .cer file; .jks and .pfx carry their own
69
+ key = load_key("Key-6.dat", password="…", certificate="signer.cer")
70
+
71
+ # CAdES-E-T: document attached, signer's certificate inside, and a signature
72
+ # time-stamp from the server named in that certificate. uakep makes no network
73
+ # calls: your post(url, request) sends the request and returns the answer.
74
+ signed = sign(receipt_xml, key, timestamp=post)
75
+ # async code: signed = await sign_async(receipt_xml, key, timestamp=async_post)
76
+ # offline documents: sign(receipt_xml, key), without a time-stamp
77
+
78
+ # POST `signed` to https://fs.tax.gov.ua:8643/fs/doc as application/octet-stream
79
+ ticket_xml = unwrap(response_body) # checks the fiscal server's signature
80
+ ```
81
+
82
+ ## How correctness is proven
83
+
84
+ Every layer is checked against ground truth that uakep did not produce, from the curve arithmetic up to the live fiscal server.
85
+
86
+ | Check | What it proves | Ground truth |
87
+ | --- | --- | --- |
88
+ | Standard test vectors | Field and curve arithmetic, ДСТУ 4145 signatures, ГОСТ 34.311 and Купина hashes, ГОСТ 28147 match the standards | Worked examples in each standard |
89
+ | Official test data | Real certificates parse; real CAdES-BES, -T, -C and -X signatures verify; key files decrypt to the right keys | [ЦЗО test examples](https://czo.gov.ua/testexamples), CA certificates, key files with known passwords |
90
+ | The tax service's signed samples | A signed receipt, Z-report and command, as the fiscal server sees them, verify and unwrap | «Приклади з КЕП» published with the [API description](https://github.com/VSydorenko/prro_docs) |
91
+ | Fuzzing | Malformed key files, certificates and server answers raise clean errors, never crash or hang | Property-based and fuzz tests |
92
+ | Fiscal server, test mode | A whole shift (open, sale, Z-report, close) is accepted with a real key; test documents carry no tax effect | `<TESTING>true</TESTING>` documents on the live server |
93
+ | Real use | Keys from many CAs and real receipts, every day | Production users |
94
+
95
+ ## Security and legal notes
96
+
97
+ - **Keys stay with the caller.** uakep never writes a key or password to disk or logs, and never sends either over the network.
98
+ - **Timing.** Python integers are not constant-time. uakep keeps jkurwa's countermeasure, a signing scalar of fixed bit length, and is meant for a server signing its own documents, not for hardware an attacker can measure.
99
+ - **Not a certified signature tool.** uakep has no expert conclusion from Держспецзв'язку. Nothing in the fiscal server's protocol names the software that made a signature, but whether your use needs certified software is a question for your lawyer.
100
+ - **Reporting a vulnerability.** Use GitHub's private vulnerability reporting on this repository, not a public issue.
101
+
102
+ ## Credits, license and contributing
103
+
104
+ - **Credits.** The cryptography is ported from [jkurwa](https://github.com/dstucrypt/jkurwa) by Ilya Petrov. [ppolib](https://github.com/leon-mbs/ppolib) by leon-mbs is a second reference. The protocol notes come from the tax service's [published description](https://tax.gov.ua/baneryi/programni-rro/opis-ari-fiskalnogo-servera/opis-ari--kontrolyuyuchogo-organu/), mirrored as Markdown in [VSydorenko/prro_docs](https://github.com/VSydorenko/prro_docs).
105
+ - **License.** MIT for uakep's own code. Ported parts keep jkurwa's copyright notice and BSD terms, collected in NOTICE.
106
+ - **Contributing.** The most useful help is test data: a document signed with a key from a CA not yet covered, or one the fiscal server rejected, with its error. Never send a private key or its password. A signed document carries your certificate, so share only what you are fine making public.
uakep-0.1.0/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # uakep: Ukrainian КЕП signatures in pure Python
2
+
3
+ ## What this is
4
+
5
+ uakep is a pure-Python library for Ukrainian qualified electronic signatures (КЕП) on ДСТУ 4145-2002, ported from [jkurwa](https://github.com/dstucrypt/jkurwa). Its first job is to sign documents for the State Tax Service's fiscal server (ПРРО) in the exact format that server accepts, using the key files sellers already have.
6
+
7
+ No native binaries, no Java, no Node: the goal is `pip install` and sign.
8
+
9
+ **Українською.** uakep — бібліотека на чистому Python для кваліфікованого електронного підпису за ДСТУ 4145-2002, порт бібліотеки jkurwa. Вона читатиме ключі (Key-6.dat, .jks, .pfx) і підписуватиме документи у форматі CAdES з позначкою часу, як цього вимагає фіскальний сервер ДПС для програмних РРО.
10
+
11
+ ## Status
12
+
13
+ Pre-alpha. Milestone 0.1 is code-complete: uakep reads key files and certificates, and the tax service's samples and ЦЗО's examples verify in it. Milestone 0.2 is in progress: signature time-stamps are built and checked against the samples. Next is a document signed by uakep and accepted by the tax service's fiscal server in its test mode, the 0.2 gate (see the [roadmap](https://github.com/butsyk/uakep/blob/main/ROADMAP.md)).
14
+
15
+ ## Why it exists
16
+
17
+ Python has no open-source library that signs the way the fiscal server requires. The tax service publishes the protocol, XSD schemas and examples, but no client library in any language ([official description](https://tax.gov.ua/baneryi/programni-rro/opis-ari-fiskalnogo-servera/opis-ari--kontrolyuyuchogo-organu/), [Markdown mirror](https://github.com/VSydorenko/prro_docs)). Sending a document is a plain HTTP POST; signing it is the hard part.
18
+
19
+ | Library | Language | Source | Notes |
20
+ | --- | --- | --- | --- |
21
+ | [IIT signing library](https://iit.com.ua/downloads) | C, with a Python interface | Closed, native binary | Its downloads page publishes no license terms |
22
+ | [jkurwa](https://github.com/dstucrypt/jkurwa) | JavaScript | Open, BSD | Maintained since 2014; reads Key-6.dat, .jks and .pfx; adds signature time-stamps |
23
+ | [ppolib](https://github.com/leon-mbs/ppolib) | PHP | Open, MIT | Port of jkurwa, written for a CRM's ПРРО |
24
+ | [uacrypto](https://github.com/h0rn3t/uacrypto) | Go | Open | First published in November 2026 |
25
+
26
+ A Python service today needs a native binary or a second runtime beside it. uakep removes both.
27
+
28
+ ## First release: scope and non-goals
29
+
30
+ The first release does what the fiscal server needs and nothing more. The server's rules are in its [API description](https://github.com/VSydorenko/prro_docs), section «Порядок засвідчення повідомлень».
31
+
32
+ - **Keys:** read the private key from IIT Key-6.dat, PrivatBank .jks and PKCS#12 .pfx files, given the password.
33
+ - **Signature:** sign and verify with ДСТУ 4145-2002 on binary-field curves, named or given in the certificate, with the ГОСТ 34.311-95 hash (ДСТУ 7564 «Купина» where a certificate needs it).
34
+ - **Container:** build CAdES-E-T signed data with the document attached and the signer's certificate inside, and without issuer certificates, CRLs or OCSP answers.
35
+ - **Time-stamp:** add a signature time-stamp (RFC 3161) from the server named in the signer's certificate for online documents, and none for offline ones; never a content time-stamp, which the fiscal server refuses.
36
+ - **Certificates:** read owner name, РНОКПП or ЄДРПОУ, validity and key usage.
37
+ - **Answers:** unwrap the fiscal server's signed answers and verify its signature.
38
+
39
+ Not in the first release: encryption (EnvelopedData), XAdES, PAdES and ASiC, cloud signatures such as Дія.Підпис, hardware tokens, certificate lookup over CMP, OCSP and CRL checks, and a ПРРО client (receipt XML, shifts, offline mode).
40
+
41
+ ## API
42
+
43
+ A sketch of the intended use; names and arguments may change before 1.0.
44
+
45
+ ```python
46
+ from uakep import load_key, sign, unwrap
47
+
48
+ # Key-6.dat holds no certificate, so pass the .cer file; .jks and .pfx carry their own
49
+ key = load_key("Key-6.dat", password="…", certificate="signer.cer")
50
+
51
+ # CAdES-E-T: document attached, signer's certificate inside, and a signature
52
+ # time-stamp from the server named in that certificate. uakep makes no network
53
+ # calls: your post(url, request) sends the request and returns the answer.
54
+ signed = sign(receipt_xml, key, timestamp=post)
55
+ # async code: signed = await sign_async(receipt_xml, key, timestamp=async_post)
56
+ # offline documents: sign(receipt_xml, key), without a time-stamp
57
+
58
+ # POST `signed` to https://fs.tax.gov.ua:8643/fs/doc as application/octet-stream
59
+ ticket_xml = unwrap(response_body) # checks the fiscal server's signature
60
+ ```
61
+
62
+ ## How correctness is proven
63
+
64
+ Every layer is checked against ground truth that uakep did not produce, from the curve arithmetic up to the live fiscal server.
65
+
66
+ | Check | What it proves | Ground truth |
67
+ | --- | --- | --- |
68
+ | Standard test vectors | Field and curve arithmetic, ДСТУ 4145 signatures, ГОСТ 34.311 and Купина hashes, ГОСТ 28147 match the standards | Worked examples in each standard |
69
+ | Official test data | Real certificates parse; real CAdES-BES, -T, -C and -X signatures verify; key files decrypt to the right keys | [ЦЗО test examples](https://czo.gov.ua/testexamples), CA certificates, key files with known passwords |
70
+ | The tax service's signed samples | A signed receipt, Z-report and command, as the fiscal server sees them, verify and unwrap | «Приклади з КЕП» published with the [API description](https://github.com/VSydorenko/prro_docs) |
71
+ | Fuzzing | Malformed key files, certificates and server answers raise clean errors, never crash or hang | Property-based and fuzz tests |
72
+ | Fiscal server, test mode | A whole shift (open, sale, Z-report, close) is accepted with a real key; test documents carry no tax effect | `<TESTING>true</TESTING>` documents on the live server |
73
+ | Real use | Keys from many CAs and real receipts, every day | Production users |
74
+
75
+ ## Security and legal notes
76
+
77
+ - **Keys stay with the caller.** uakep never writes a key or password to disk or logs, and never sends either over the network.
78
+ - **Timing.** Python integers are not constant-time. uakep keeps jkurwa's countermeasure, a signing scalar of fixed bit length, and is meant for a server signing its own documents, not for hardware an attacker can measure.
79
+ - **Not a certified signature tool.** uakep has no expert conclusion from Держспецзв'язку. Nothing in the fiscal server's protocol names the software that made a signature, but whether your use needs certified software is a question for your lawyer.
80
+ - **Reporting a vulnerability.** Use GitHub's private vulnerability reporting on this repository, not a public issue.
81
+
82
+ ## Credits, license and contributing
83
+
84
+ - **Credits.** The cryptography is ported from [jkurwa](https://github.com/dstucrypt/jkurwa) by Ilya Petrov. [ppolib](https://github.com/leon-mbs/ppolib) by leon-mbs is a second reference. The protocol notes come from the tax service's [published description](https://tax.gov.ua/baneryi/programni-rro/opis-ari-fiskalnogo-servera/opis-ari--kontrolyuyuchogo-organu/), mirrored as Markdown in [VSydorenko/prro_docs](https://github.com/VSydorenko/prro_docs).
85
+ - **License.** MIT for uakep's own code. Ported parts keep jkurwa's copyright notice and BSD terms, collected in NOTICE.
86
+ - **Contributing.** The most useful help is test data: a document signed with a key from a CA not yet covered, or one the fiscal server rejected, with its error. Never send a private key or its password. A signed document carries your certificate, so share only what you are fine making public.
@@ -0,0 +1,88 @@
1
+ [project]
2
+ name = "uakep"
3
+ version = "0.1.0"
4
+ description = "Ukrainian qualified electronic signatures (КЕП, ДСТУ 4145-2002) in pure Python"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = [
8
+ "LICENSE",
9
+ "NOTICE",
10
+ ]
11
+ requires-python = ">=3.11"
12
+ dependencies = ["asn1crypto>=1.5.1"]
13
+ keywords = [
14
+ "dstu4145",
15
+ "kep",
16
+ "cades",
17
+ "prro",
18
+ "ukraine",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 2 - Pre-Alpha",
22
+ "Intended Audience :: Developers",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3 :: Only",
25
+ "Topic :: Security :: Cryptography",
26
+ ]
27
+
28
+ [[project.authors]]
29
+ name = "Anton Butsyk"
30
+
31
+ [project.urls]
32
+ Repository = "https://github.com/butsyk/uakep"
33
+ Changelog = "https://github.com/butsyk/uakep/blob/main/CHANGELOG.md"
34
+
35
+ [build-system]
36
+ requires = ["uv_build>=0.13.7,<0.14.0"]
37
+ build-backend = "uv_build"
38
+
39
+ [dependency-groups]
40
+ dev = [
41
+ "pytest>=8",
42
+ "pytest-cov>=6",
43
+ "ruff>=0.14",
44
+ ]
45
+
46
+ [tool.ruff]
47
+ line-length = 100
48
+ target-version = "py311"
49
+
50
+ [tool.ruff.lint]
51
+ select = [
52
+ "E",
53
+ "F",
54
+ "W",
55
+ "I",
56
+ "B",
57
+ "UP",
58
+ "SIM",
59
+ "RUF",
60
+ ]
61
+ ignore = [
62
+ "RUF001",
63
+ "RUF002",
64
+ "RUF003",
65
+ ]
66
+
67
+ [tool.ruff.lint.per-file-ignores]
68
+ "src/uakep/asn1.py" = ["RUF012"]
69
+
70
+ [tool.pytest.ini_options]
71
+ testpaths = ["tests"]
72
+ addopts = "-ra"
73
+ markers = [
74
+ "interop: compatibility cross-checks with the JavaScript peer in interop/",
75
+ "slow: long-running randomized tests",
76
+ ]
77
+
78
+ [tool.coverage.run]
79
+ source = ["uakep"]
80
+ branch = true
81
+
82
+ [tool.coverage.report]
83
+ show_missing = true
84
+ skip_covered = false
85
+ exclude_also = [
86
+ "if TYPE_CHECKING:",
87
+ "raise NotImplementedError",
88
+ ]
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "uakep"
3
+ version = "0.1.0"
4
+ description = "Ukrainian qualified electronic signatures (КЕП, ДСТУ 4145-2002) in pure Python"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE", "NOTICE"]
8
+ authors = [{ name = "Anton Butsyk" }]
9
+ requires-python = ">=3.11"
10
+ dependencies = ["asn1crypto>=1.5.1"]
11
+ keywords = ["dstu4145", "kep", "cades", "prro", "ukraine"]
12
+ classifiers = [
13
+ "Development Status :: 2 - Pre-Alpha",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Topic :: Security :: Cryptography",
18
+ ]
19
+
20
+ [project.urls]
21
+ Repository = "https://github.com/butsyk/uakep"
22
+ Changelog = "https://github.com/butsyk/uakep/blob/main/CHANGELOG.md"
23
+
24
+ [build-system]
25
+ requires = ["uv_build>=0.13.7,<0.14.0"]
26
+ build-backend = "uv_build"
27
+
28
+ [dependency-groups]
29
+ dev = ["pytest>=8", "pytest-cov>=6", "ruff>=0.14"]
30
+
31
+ [tool.ruff]
32
+ line-length = 100
33
+ target-version = "py311"
34
+
35
+ [tool.ruff.lint]
36
+ select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF"]
37
+ # Ukrainian text in docstrings and messages is intentional.
38
+ ignore = ["RUF001", "RUF002", "RUF003"]
39
+
40
+ [tool.ruff.lint.per-file-ignores]
41
+ # asn1crypto declares structures as class-level lists.
42
+ "src/uakep/asn1.py" = ["RUF012"]
43
+
44
+ [tool.pytest.ini_options]
45
+ testpaths = ["tests"]
46
+ addopts = "-ra"
47
+ markers = [
48
+ "interop: compatibility cross-checks with the JavaScript peer in interop/",
49
+ "slow: long-running randomized tests",
50
+ ]
51
+
52
+ [tool.coverage.run]
53
+ source = ["uakep"]
54
+ branch = true
55
+
56
+ [tool.coverage.report]
57
+ show_missing = true
58
+ skip_covered = false
59
+ exclude_also = ["if TYPE_CHECKING:", "raise NotImplementedError"]
@@ -0,0 +1,54 @@
1
+ """uakep: Ukrainian qualified electronic signatures (КЕП, ДСТУ 4145-2002) in pure Python.
2
+
3
+ from uakep import load_key, sign, unwrap
4
+
5
+ key = load_key("Key-6.dat", password="…", certificate="signer.cer")
6
+ signed = sign(receipt_xml_cp1251, key, timestamp=post) # CAdES with a signature time-stamp
7
+ content = unwrap(signed) # checks the signature, returns the data
8
+
9
+ ``post(url, request) -> bytes`` is yours: it POSTs the time-stamp request to the TSA named in
10
+ the signer's certificate. Async callers use ``await sign_async(..., timestamp=async_post)``.
11
+ Offline documents are signed without ``timestamp``.
12
+ """
13
+
14
+ from importlib.metadata import PackageNotFoundError, version
15
+
16
+ from .asn1 import UnsupportedError
17
+ from .cades import SignatureError, SigningKey, Verified, sign, sign_async, verify
18
+ from .certificate import Certificate, CertificateError
19
+ from .keystore import KeyFileError, WrongPasswordError, load_key
20
+ from .tsp import TimeStamp, TimeStampError
21
+
22
+ try:
23
+ __version__ = version("uakep")
24
+ except PackageNotFoundError: # running from a source tree that is not installed
25
+ __version__ = "0+unknown"
26
+
27
+
28
+ def unwrap(signed: bytes, *, detached_content: bytes | None = None) -> bytes:
29
+ """Check the ДСТУ 4145 signature on CAdES signed data and return the signed data.
30
+
31
+ Raises SignatureError when the signature, or a signature time-stamp, does not verify.
32
+ Certificate chains and revocation are not checked.
33
+ """
34
+ return verify(signed, detached_content=detached_content).content
35
+
36
+
37
+ __all__ = [
38
+ "Certificate",
39
+ "CertificateError",
40
+ "KeyFileError",
41
+ "SignatureError",
42
+ "SigningKey",
43
+ "TimeStamp",
44
+ "TimeStampError",
45
+ "UnsupportedError",
46
+ "Verified",
47
+ "WrongPasswordError",
48
+ "__version__",
49
+ "load_key",
50
+ "sign",
51
+ "sign_async",
52
+ "unwrap",
53
+ "verify",
54
+ ]