uakep 0.1.0__py3-none-any.whl
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/__init__.py +54 -0
- uakep/asn1.py +406 -0
- uakep/cades.py +400 -0
- uakep/certificate.py +210 -0
- uakep/curve.py +285 -0
- uakep/dstu4145.py +127 -0
- uakep/gf2m.py +161 -0
- uakep/gost28147.py +163 -0
- uakep/gost34311.py +188 -0
- uakep/keystore.py +377 -0
- uakep/kupyna.py +130 -0
- uakep/py.typed +0 -0
- uakep/tsp.py +105 -0
- uakep-0.1.0.dist-info/METADATA +106 -0
- uakep-0.1.0.dist-info/RECORD +18 -0
- uakep-0.1.0.dist-info/WHEEL +4 -0
- uakep-0.1.0.dist-info/licenses/LICENSE +21 -0
- uakep-0.1.0.dist-info/licenses/NOTICE +58 -0
uakep/__init__.py
ADDED
|
@@ -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
|
+
]
|
uakep/asn1.py
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""ASN.1 structures for ДСТУ 4145 keys, parameters and key containers (on asn1crypto).
|
|
2
|
+
|
|
3
|
+
Curve parameters follow ДСТУ 4145-2002, Appendix Б: a named curve OID or an explicit binary
|
|
4
|
+
curve, optionally with a substitution table (ДКЕ). In certificates and private keys the
|
|
5
|
+
coefficient ``b`` and the compressed base point are little-endian octet strings; inside IIT's
|
|
6
|
+
key attributes they are big-endian.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from asn1crypto import core, tsp, x509
|
|
12
|
+
|
|
13
|
+
from .curve import NAMED_CURVES, Curve
|
|
14
|
+
|
|
15
|
+
# Algorithms
|
|
16
|
+
DSTU4145_LE = "1.2.804.2.1.1.1.1.3.1.1"
|
|
17
|
+
DSTU4145_KUPYNA = "1.2.804.2.1.1.1.1.3.6.1.1"
|
|
18
|
+
GOST34311 = "1.2.804.2.1.1.1.1.2.1"
|
|
19
|
+
GOST34311_HMAC = "1.2.804.2.1.1.1.1.1.2"
|
|
20
|
+
GOST28147_CFB = "1.2.804.2.1.1.1.1.1.1.3"
|
|
21
|
+
PBES2 = "1.2.840.113549.1.5.13"
|
|
22
|
+
PBKDF2 = "1.2.840.113549.1.5.12"
|
|
23
|
+
|
|
24
|
+
# IIT key containers (Key-6.dat) and the attributes carrying a second key
|
|
25
|
+
IIT_STORE = "1.3.6.1.4.1.19398.1.1.1.2"
|
|
26
|
+
IIT_KEY_CURVE = "1.3.6.1.4.1.19398.1.1.2.2"
|
|
27
|
+
IIT_KEY_BITS = "1.3.6.1.4.1.19398.1.1.2.3"
|
|
28
|
+
|
|
29
|
+
# Named curves: the ten curves of ДСТУ 4145-2002 under the ГОСТ 34.311 and Купина arcs.
|
|
30
|
+
_CURVE_SIZES = (163, 167, 173, 179, 191, 233, 257, 307, 367, 431)
|
|
31
|
+
CURVE_OIDS: dict[str, str] = {}
|
|
32
|
+
for _arc in ("1.2.804.2.1.1.1.1.3.1.1.2", "1.2.804.2.1.1.1.1.3.6.1.1.2"):
|
|
33
|
+
for _i, _m in enumerate(_CURVE_SIZES):
|
|
34
|
+
CURVE_OIDS[f"{_arc}.{_i}"] = f"DSTU_PB_{_m}"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class UnsupportedError(ValueError):
|
|
38
|
+
"""Valid input that this version of uakep cannot handle (an unbundled curve, a cipher)."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Pentanomial(core.Sequence):
|
|
42
|
+
_fields = [("k", core.Integer), ("j", core.Integer), ("l", core.Integer)]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Polynomial(core.Choice):
|
|
46
|
+
_alternatives = [("trinomial", core.Integer), ("pentanomial", Pentanomial)]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FieldSpec(core.Sequence):
|
|
50
|
+
_fields = [("m", core.Integer), ("poly", Polynomial)]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ECBinary(core.Sequence):
|
|
54
|
+
_fields = [
|
|
55
|
+
("version", core.Integer, {"explicit": 0, "default": 0}),
|
|
56
|
+
("field", FieldSpec),
|
|
57
|
+
("a", core.Integer),
|
|
58
|
+
("b", core.OctetString),
|
|
59
|
+
("n", core.Integer),
|
|
60
|
+
("bp", core.OctetString),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CurveSpec(core.Choice):
|
|
65
|
+
_alternatives = [("explicit", ECBinary), ("named", core.ObjectIdentifier)]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class DstuParams(core.Sequence):
|
|
69
|
+
"""Algorithm parameters of a ДСТУ 4145 key: the curve and the optional ДКЕ."""
|
|
70
|
+
|
|
71
|
+
_fields = [
|
|
72
|
+
("curve", CurveSpec),
|
|
73
|
+
("dke", core.OctetString, {"optional": True}),
|
|
74
|
+
("dke2", core.OctetString, {"optional": True}),
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class DstuAlgorithm(core.Sequence):
|
|
79
|
+
_fields = [("algorithm", core.ObjectIdentifier), ("parameters", DstuParams, {"optional": True})]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class KeyAttribute(core.Sequence):
|
|
83
|
+
_fields = [("type", core.ObjectIdentifier), ("values", core.SetOf, {"spec": core.Any})]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class KeyAttributes(core.SetOf):
|
|
87
|
+
_child_spec = KeyAttribute
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DstuPrivateKey(core.Sequence):
|
|
91
|
+
"""PKCS#8 PrivateKeyInfo as ДСТУ 4145 key containers hold it; d is little-endian."""
|
|
92
|
+
|
|
93
|
+
_fields = [
|
|
94
|
+
("version", core.Integer),
|
|
95
|
+
("algorithm", DstuAlgorithm),
|
|
96
|
+
("private_key", core.OctetString),
|
|
97
|
+
("attributes", KeyAttributes, {"implicit": 0, "optional": True}),
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class IitStoreParams(core.Sequence):
|
|
102
|
+
_fields = [("mac", core.OctetString), ("pad", core.OctetString, {"optional": True})]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class IitStoreHeader(core.Sequence):
|
|
106
|
+
_fields = [("type", core.ObjectIdentifier), ("params", IitStoreParams)]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class IitStore(core.Sequence):
|
|
110
|
+
"""IIT Key-6.dat: a private key encrypted with ГОСТ 28147-89 in ECB mode."""
|
|
111
|
+
|
|
112
|
+
_fields = [("header", IitStoreHeader), ("data", core.OctetString)]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class AlgorithmIdentifier(core.Sequence):
|
|
116
|
+
_fields = [("algorithm", core.ObjectIdentifier), ("parameters", core.Any, {"optional": True})]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class SubjectPublicKeyInfo(core.Sequence):
|
|
120
|
+
"""X.509 public key info with the parameters left raw (asn1crypto rejects ДСТУ keys)."""
|
|
121
|
+
|
|
122
|
+
_fields = [("algorithm", AlgorithmIdentifier), ("public_key", core.BitString)]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class TbsCertificate(core.Sequence):
|
|
126
|
+
_fields = [
|
|
127
|
+
("version", x509.Version, {"explicit": 0, "default": "v1"}),
|
|
128
|
+
("serial_number", core.Integer),
|
|
129
|
+
("signature", AlgorithmIdentifier),
|
|
130
|
+
("issuer", x509.Name),
|
|
131
|
+
("validity", x509.Validity),
|
|
132
|
+
("subject", x509.Name),
|
|
133
|
+
("subject_public_key_info", SubjectPublicKeyInfo),
|
|
134
|
+
("issuer_unique_id", core.BitString, {"implicit": 1, "optional": True}),
|
|
135
|
+
("subject_unique_id", core.BitString, {"implicit": 2, "optional": True}),
|
|
136
|
+
("extensions", x509.Extensions, {"explicit": 3, "optional": True}),
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class Certificate(core.Sequence):
|
|
141
|
+
_fields = [
|
|
142
|
+
("tbs_certificate", TbsCertificate),
|
|
143
|
+
("signature_algorithm", AlgorithmIdentifier),
|
|
144
|
+
("signature_value", core.BitString),
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class DirectoryAttribute(core.Sequence):
|
|
149
|
+
_fields = [("type", core.ObjectIdentifier), ("values", core.SetOf, {"spec": core.Any})]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class DirectoryAttributes(core.SequenceOf):
|
|
153
|
+
"""subjectDirectoryAttributes, where Ukrainian CAs put РНОКПП and ЄДРПОУ."""
|
|
154
|
+
|
|
155
|
+
_child_spec = DirectoryAttribute
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class AccessDescription(core.Sequence):
|
|
159
|
+
_fields = [("access_method", core.ObjectIdentifier), ("access_location", x509.GeneralName)]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class InfoAccess(core.SequenceOf):
|
|
163
|
+
"""authorityInfoAccess and subjectInfoAccess."""
|
|
164
|
+
|
|
165
|
+
_child_spec = AccessDescription
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# CMS (RFC 5652) as Ukrainian software writes it. The signer identifier is kept raw: the tax
|
|
169
|
+
# service's samples encode subjectKeyIdentifier as [0] EXPLICIT OCTET STRING, where RFC 5652
|
|
170
|
+
# has [0] IMPLICIT.
|
|
171
|
+
|
|
172
|
+
ID_DATA = "1.2.840.113549.1.7.1"
|
|
173
|
+
ID_SIGNED_DATA = "1.2.840.113549.1.7.2"
|
|
174
|
+
ID_TST_INFO = "1.2.840.113549.1.9.16.1.4"
|
|
175
|
+
ID_CONTENT_TYPE = "1.2.840.113549.1.9.3"
|
|
176
|
+
ID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4"
|
|
177
|
+
ID_SIGNING_TIME = "1.2.840.113549.1.9.5"
|
|
178
|
+
ID_SIGNING_CERTIFICATE_V2 = "1.2.840.113549.1.9.16.2.47"
|
|
179
|
+
ID_CONTENT_TIMESTAMP = "1.2.840.113549.1.9.16.2.20"
|
|
180
|
+
ID_SIGNATURE_TIMESTAMP = "1.2.840.113549.1.9.16.2.14"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class Attribute(core.Sequence):
|
|
184
|
+
_fields = [("type", core.ObjectIdentifier), ("values", core.SetOf, {"spec": core.Any})]
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class Attributes(core.SetOf):
|
|
188
|
+
_child_spec = Attribute
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class IssuerAndSerialNumber(core.Sequence):
|
|
192
|
+
_fields = [("issuer", x509.Name), ("serial_number", core.Integer)]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class SignerIdExplicit(core.Choice):
|
|
196
|
+
"""The signer id as the tax samples write it: [0] EXPLICIT OCTET STRING for a key id."""
|
|
197
|
+
|
|
198
|
+
_alternatives = [
|
|
199
|
+
("issuer_and_serial_number", IssuerAndSerialNumber),
|
|
200
|
+
("subject_key_identifier", core.OctetString, {"explicit": 0}),
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class SignerIdImplicit(core.Choice):
|
|
205
|
+
"""The signer id as RFC 5652 defines it: [0] IMPLICIT for a key id."""
|
|
206
|
+
|
|
207
|
+
_alternatives = [
|
|
208
|
+
("issuer_and_serial_number", IssuerAndSerialNumber),
|
|
209
|
+
("subject_key_identifier", core.OctetString, {"implicit": 0}),
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class SignerInfo(core.Sequence):
|
|
214
|
+
_fields = [
|
|
215
|
+
("version", core.Integer),
|
|
216
|
+
("sid", core.Any),
|
|
217
|
+
("digest_algorithm", AlgorithmIdentifier),
|
|
218
|
+
("signed_attrs", Attributes, {"implicit": 0, "optional": True}),
|
|
219
|
+
("signature_algorithm", AlgorithmIdentifier),
|
|
220
|
+
("signature", core.OctetString),
|
|
221
|
+
("unsigned_attrs", Attributes, {"implicit": 1, "optional": True}),
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class SignerInfos(core.SetOf):
|
|
226
|
+
_child_spec = SignerInfo
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class DigestAlgorithms(core.SetOf):
|
|
230
|
+
_child_spec = AlgorithmIdentifier
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class CertificateSet(core.SetOf):
|
|
234
|
+
_child_spec = core.Any
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class EncapsulatedContentInfo(core.Sequence):
|
|
238
|
+
_fields = [
|
|
239
|
+
("content_type", core.ObjectIdentifier),
|
|
240
|
+
("content", core.OctetString, {"explicit": 0, "optional": True}),
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class SignedData(core.Sequence):
|
|
245
|
+
_fields = [
|
|
246
|
+
("version", core.Integer),
|
|
247
|
+
("digest_algorithms", DigestAlgorithms),
|
|
248
|
+
("encap_content_info", EncapsulatedContentInfo),
|
|
249
|
+
("certificates", CertificateSet, {"implicit": 0, "optional": True}),
|
|
250
|
+
("crls", CertificateSet, {"implicit": 1, "optional": True}),
|
|
251
|
+
("signer_infos", SignerInfos),
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class ContentInfo(core.Sequence):
|
|
256
|
+
_fields = [
|
|
257
|
+
("content_type", core.ObjectIdentifier),
|
|
258
|
+
("content", core.Any, {"explicit": 0, "optional": True}),
|
|
259
|
+
]
|
|
260
|
+
_oid_pair = ("content_type", "content")
|
|
261
|
+
_oid_specs = {ID_SIGNED_DATA: SignedData}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# Password-protected keys: PKCS#8 EncryptedPrivateKeyInfo with PBES2 (RFC 8018), PKCS#12 (RFC 7292)
|
|
265
|
+
|
|
266
|
+
ID_ENCRYPTED_DATA = "1.2.840.113549.1.7.6"
|
|
267
|
+
KEY_BAG = "1.2.840.113549.1.12.10.1.1"
|
|
268
|
+
SHROUDED_KEY_BAG = "1.2.840.113549.1.12.10.1.2"
|
|
269
|
+
CERT_BAG = "1.2.840.113549.1.12.10.1.3"
|
|
270
|
+
SAFE_CONTENTS_BAG = "1.2.840.113549.1.12.10.1.6"
|
|
271
|
+
X509_CERTIFICATE = "1.2.840.113549.1.9.22.1"
|
|
272
|
+
JKS_KEY_PROTECTOR = "1.3.6.1.4.1.42.2.17.1.1"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class EncryptedPrivateKeyInfo(core.Sequence):
|
|
276
|
+
_fields = [("encryption_algorithm", AlgorithmIdentifier), ("encrypted_data", core.OctetString)]
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class Pbes2Params(core.Sequence):
|
|
280
|
+
_fields = [
|
|
281
|
+
("key_derivation_func", AlgorithmIdentifier),
|
|
282
|
+
("encryption_scheme", AlgorithmIdentifier),
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class Pbkdf2Params(core.Sequence):
|
|
287
|
+
_fields = [
|
|
288
|
+
("salt", core.OctetString),
|
|
289
|
+
("iteration_count", core.Integer),
|
|
290
|
+
("key_length", core.Integer, {"optional": True}),
|
|
291
|
+
("prf", AlgorithmIdentifier, {"optional": True}),
|
|
292
|
+
]
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class Gost28147Params(core.Sequence):
|
|
296
|
+
_fields = [("iv", core.OctetString), ("dke", core.OctetString, {"optional": True})]
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class SafeBag(core.Sequence):
|
|
300
|
+
_fields = [
|
|
301
|
+
("bag_id", core.ObjectIdentifier),
|
|
302
|
+
("bag_value", core.Any, {"explicit": 0}),
|
|
303
|
+
("bag_attributes", Attributes, {"optional": True}),
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class SafeContents(core.SequenceOf):
|
|
308
|
+
_child_spec = SafeBag
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class CertBag(core.Sequence):
|
|
312
|
+
_fields = [
|
|
313
|
+
("cert_id", core.ObjectIdentifier),
|
|
314
|
+
("cert_value", core.OctetString, {"explicit": 0}),
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class EncryptedContentInfo(core.Sequence):
|
|
319
|
+
_fields = [
|
|
320
|
+
("content_type", core.ObjectIdentifier),
|
|
321
|
+
("content_encryption_algorithm", AlgorithmIdentifier),
|
|
322
|
+
("encrypted_content", core.OctetString, {"implicit": 0, "optional": True}),
|
|
323
|
+
]
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class EncryptedData(core.Sequence):
|
|
327
|
+
_fields = [("version", core.Integer), ("encrypted_content_info", EncryptedContentInfo)]
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class AuthenticatedSafe(core.SequenceOf):
|
|
331
|
+
_child_spec = ContentInfo
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class Pfx(core.Sequence):
|
|
335
|
+
_fields = [
|
|
336
|
+
("version", core.Integer),
|
|
337
|
+
("auth_safe", ContentInfo),
|
|
338
|
+
("mac_data", core.Any, {"optional": True}),
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class GeneralNames(core.SequenceOf):
|
|
343
|
+
_child_spec = x509.GeneralName
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class IssuerSerial(core.Sequence):
|
|
347
|
+
_fields = [("issuer", GeneralNames), ("serial_number", core.Integer)]
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class ESSCertIDv2(core.Sequence):
|
|
351
|
+
"""RFC 5035; the hash algorithm is always written, as SHA-256 is never used here."""
|
|
352
|
+
|
|
353
|
+
_fields = [
|
|
354
|
+
("hash_algorithm", AlgorithmIdentifier, {"optional": True}),
|
|
355
|
+
("cert_hash", core.OctetString),
|
|
356
|
+
("issuer_serial", IssuerSerial, {"optional": True}),
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class ESSCertIDsV2(core.SequenceOf):
|
|
361
|
+
_child_spec = ESSCertIDv2
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class SigningCertificateV2(core.Sequence):
|
|
365
|
+
_fields = [("certs", ESSCertIDsV2), ("policies", core.Any, {"optional": True})]
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class TimeStampResp(core.Sequence):
|
|
369
|
+
"""RFC 3161 TimeStampResp with the token kept raw: it is CMS signed data with ДСТУ keys."""
|
|
370
|
+
|
|
371
|
+
_fields = [
|
|
372
|
+
("status", tsp.PKIStatusInfo),
|
|
373
|
+
("time_stamp_token", core.Any, {"optional": True}),
|
|
374
|
+
]
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def curve_from_spec(spec: CurveSpec, *, little_endian: bool = True) -> Curve:
|
|
378
|
+
"""The curve a parameter block names or spells out."""
|
|
379
|
+
if spec.name == "named":
|
|
380
|
+
oid = spec.chosen.dotted
|
|
381
|
+
name = CURVE_OIDS.get(oid)
|
|
382
|
+
if name is None:
|
|
383
|
+
raise UnsupportedError(f"unknown curve OID {oid}")
|
|
384
|
+
if name not in NAMED_CURVES:
|
|
385
|
+
raise UnsupportedError(f"curve {name} is not bundled with uakep")
|
|
386
|
+
return NAMED_CURVES[name]
|
|
387
|
+
ec = spec.chosen
|
|
388
|
+
poly = ec["field"]["poly"]
|
|
389
|
+
if poly.name == "trinomial":
|
|
390
|
+
ks: tuple[int, ...] = (poly.chosen.native,)
|
|
391
|
+
else:
|
|
392
|
+
p = poly.chosen
|
|
393
|
+
ks = (p["k"].native, p["j"].native, p["l"].native)
|
|
394
|
+
order = "little" if little_endian else "big"
|
|
395
|
+
curve = Curve.from_params(
|
|
396
|
+
ec["field"]["m"].native,
|
|
397
|
+
ks,
|
|
398
|
+
ec["a"].native,
|
|
399
|
+
int.from_bytes(ec["b"].native, order),
|
|
400
|
+
ec["n"].native,
|
|
401
|
+
int.from_bytes(ec["bp"].native, order),
|
|
402
|
+
)
|
|
403
|
+
for named in NAMED_CURVES.values():
|
|
404
|
+
if named == curve:
|
|
405
|
+
return named
|
|
406
|
+
return curve
|