2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2021 Sergey Matveev <stargrave@stargrave.org>
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, version 3 of the License.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 from base64 import b64decode
18 from hmac import new as hmac_new
19 from unittest import skipIf
20 from unittest import TestCase
22 from pygost import gost3410
23 from pygost.gost28147 import cfb_decrypt
24 from pygost.gost34112012256 import GOST34112012256
25 from pygost.gost34112012512 import GOST34112012512
26 from pygost.gost34112012512 import pbkdf2 as gost34112012_pbkdf2
27 from pygost.gost3412 import GOST3412Kuznechik
28 from pygost.gost3412 import GOST3412Magma
29 from pygost.gost3412 import KEYSIZE
30 from pygost.gost3413 import ctr_acpkm
31 from pygost.gost3413 import mac as omac
32 from pygost.kdf import kdf_tree_gostr3411_2012_256
33 from pygost.kdf import keg
34 from pygost.utils import hexdec
35 from pygost.wrap import kimp15
39 from pyderasn import OctetString
41 from pygost.asn1schemas.cms import EncryptedData
42 from pygost.asn1schemas.cms import EnvelopedData
43 from pygost.asn1schemas.cms import SignedAttributes
44 from pygost.asn1schemas.cms import SignedData
45 from pygost.asn1schemas.oids import id_data
46 from pygost.asn1schemas.oids import id_envelopedData
47 from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm
48 from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15
49 from pygost.asn1schemas.oids import id_messageDigest
50 from pygost.asn1schemas.oids import id_pbes2
51 from pygost.asn1schemas.oids import id_pkcs12_bagtypes_certBag
52 from pygost.asn1schemas.oids import id_pkcs12_bagtypes_keyBag
53 from pygost.asn1schemas.oids import id_pkcs12_bagtypes_pkcs8ShroudedKeyBag
54 from pygost.asn1schemas.oids import id_pkcs9_certTypes_x509Certificate
55 from pygost.asn1schemas.oids import id_signedData
56 from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_256
57 from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256
58 from pygost.asn1schemas.pfx import CertBag
59 from pygost.asn1schemas.pfx import KeyBag
60 from pygost.asn1schemas.pfx import OctetStringSafeContents
61 from pygost.asn1schemas.pfx import PBES2Params
62 from pygost.asn1schemas.pfx import PFX
63 from pygost.asn1schemas.pfx import PKCS8ShroudedKeyBag
64 from pygost.asn1schemas.pfx import SafeContents
65 from pygost.asn1schemas.x509 import Certificate
67 pyderasn_exists = False
69 pyderasn_exists = True
72 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
73 class TestPFX(TestCase):
74 """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf)
76 pfx_raw = b64decode("""
77 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
109 password = u"Пароль для PFX"
111 def test_shrouded_key_bag(self):
112 private_key_info_expected = b64decode(b"""
113 MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG
114 G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY=
117 pfx, tail = PFX().decode(self.pfx_raw)
118 self.assertSequenceEqual(tail, b"")
119 _, outer_safe_contents = pfx["authSafe"]["content"].defined
120 safe_contents, tail = OctetStringSafeContents().decode(
121 bytes(outer_safe_contents[0]["bagValue"]),
123 self.assertSequenceEqual(tail, b"")
124 safe_bag = safe_contents[0]
125 shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode(
126 bytes(safe_bag["bagValue"]),
128 self.assertSequenceEqual(tail, b"")
129 _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
130 _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
131 _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
133 key = gost34112012_pbkdf2(
134 password=self.password.encode("utf-8"),
135 salt=bytes(pbkdf2_params["salt"]["specified"]),
136 iterations=int(pbkdf2_params["iterationCount"]),
139 # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8")
140 self.assertSequenceEqual(
143 bytes(shrouded_key_bag["encryptedData"]),
144 iv=bytes(enc_scheme_params["iv"]),
145 sbox="id-tc26-gost-28147-param-Z",
147 private_key_info_expected,
150 def test_encrypted_data(self):
151 cert_bag_expected = b64decode(b"""
152 MIIDSjCCA0YGCyqGSIb3DQEMCgEDoIIDHjCCAxoGCiqGSIb3DQEJFgGgggMKBIIDBjCCAwIwggKt
153 oAMCAQICEAHQaF8xH5bAAAAACycJAAEwDAYIKoUDBwEBAwIFADBgMQswCQYDVQQGEwJSVTEVMBMG
154 A1UEBwwM0JzQvtGB0LrQstCwMQ8wDQYDVQQKDAbQotCaMjYxKTAnBgNVBAMMIENBIGNlcnRpZmlj
155 YXRlIChQS0NTIzEyIGV4YW1wbGUpMB4XDTE1MDMyNzA3MjUwMFoXDTIwMDMyNzA3MjMwMFowZDEL
156 MAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MS0wKwYD
157 VQQDDCRUZXN0IGNlcnRpZmljYXRlIDEgKFBLQ1MjMTIgZXhhbXBsZSkwZjAfBggqhQMHAQEBATAT
158 BgcqhQMCAiMBBggqhQMHAQECAgNDAARA1xzymkpvr2dYJT8WTOX3Dt96/+hGsXNytUQpkWB5ImJM
159 4tg9AsC4RIUwV5H41MhG0uBRFweTzN6AsAdBvhTClYEJADI3MDkwMDAxo4IBKTCCASUwKwYDVR0Q
160 BCQwIoAPMjAxNTAzMjcwNzI1MDBagQ8yMDE2MDMyNzA3MjUwMFowDgYDVR0PAQH/BAQDAgTwMB0G
161 A1UdDgQWBBQhWOsRQ68yYN2Utg/owHoWcqsVbTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
162 AwQwDAYDVR0TAQH/BAIwADCBmQYDVR0jBIGRMIGOgBQmnc7Xh5ykb5t/BMwOkxA4drfEmqFkpGIw
163 YDELMAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MSkw
164 JwYDVQQDDCBDQSBjZXJ0aWZpY2F0ZSAoUEtDUyMxMiBleGFtcGxlKYIQAdBoXvL8TSAAAAALJwkA
165 ATAMBggqhQMHAQEDAgUAA0EA9oq0Vvk8kkgIwkp0x0J5eKtia4MNTiwKAm7jgnCZIx3O98BThaTX
166 3ZQhEo2RL9pTCPr6wFMheeJ+YdGMReXvsjEVMBMGCSqGSIb3DQEJFTEGBAQBAAAA
169 pfx, tail = PFX().decode(self.pfx_raw)
170 self.assertSequenceEqual(tail, b"")
171 _, outer_safe_contents = pfx["authSafe"]["content"].defined
172 _, encrypted_data = outer_safe_contents[1]["bagValue"].defined
173 _, pbes2_params = encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"].defined
174 _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
175 _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
176 key = gost34112012_pbkdf2(
177 password=self.password.encode("utf-8"),
178 salt=bytes(pbkdf2_params["salt"]["specified"]),
179 iterations=int(pbkdf2_params["iterationCount"]),
182 # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc")
183 self.assertSequenceEqual(
186 bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]),
187 iv=bytes(enc_scheme_params["iv"]),
188 sbox="id-tc26-gost-28147-param-Z",
194 pfx, tail = PFX().decode(self.pfx_raw)
195 self.assertSequenceEqual(tail, b"")
196 _, outer_safe_contents = pfx["authSafe"]["content"].defined
197 mac_data = pfx["macData"]
198 mac_key = gost34112012_pbkdf2(
199 password=self.password.encode("utf-8"),
200 salt=bytes(mac_data["macSalt"]),
201 iterations=int(mac_data["iterations"]),
204 # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5")
205 self.assertSequenceEqual(
208 msg=SafeContents(outer_safe_contents).encode(),
209 digestmod=GOST34112012512,
211 bytes(mac_data["mac"]["digest"]),
215 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
216 class TestPFX2020(TestCase):
217 """PFX test vectors from newer PKCS#12 update
219 ca_prv_raw = hexdec("092F8D059E97E22B90B1AE99F0087FC4D26620B91550CBB437C191005A290810")
220 ca_curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"]
221 ca_cert = Certificate().decod(b64decode(b"""
222 MIIB+TCCAaagAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
223 cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
224 MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx
225 5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMHAQEBATALBgkq
226 hQMHAQIBAQEDQwAEQBpKgpyPDnhQAJyLqy8Qs0XQhgxEhby6tSypqYimgbjpcKqtU6
227 4jpDXc3h3BxGxtl2oHJ/4YLZ/ll87dto3ltMqjgZgwgZUwYwYDVR0jBFwwWoAUrGwO
228 TERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHk
229 NBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUrGwO
230 TERmokKW4p8JOyVm88ukUyowDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNBAB
231 Gg3nhgQ5oCKbqlEdVaRxH+1WX4wVkawGXuTYkr1AC2OWw3ZC14Vvg3nazm8UMWUZtk
234 ca_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
235 ca_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
237 password = u"Пароль для PFX".encode("utf-8")
238 cert_test = Certificate().decod(b64decode(b"""
239 MIICLjCCAdugAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
240 cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
241 MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYDVQQDEy
242 FPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaAwFwYIKoUDBwEBAQIw
243 CwYJKoUDBwECAQIBA4GEAASBgLSLt1q8KQ4YZVxioU+1LV9QhE7MHR9gBEh7S1yVNG
244 lqt7+rNG5VFqmrPM74rbUsOlhV8M+zZKprXdk35Oz8lSW/n2oIUHZxikXIH/SSHj4r
245 v3K/Puvz7hYTQSZl/xPdp78nUmjrEa6d5wfX8biEy2z0dgufFvAkMw1Ua4gdXqDOo4
246 GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0wCwYD
247 VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaX
248 SCBAGMuoEwHQYDVR0OBBYEFH4GVwmYDK1rCKhX7nkAWDrJ16CkMAoGCCqFAwcBAQMC
249 A0EACl6p8dAbpi9Hk+3mgMyI0WIh17IrlrSp/mB0F7ZzMt8XUD1Dwz3JrrnxeXnfMv
250 OA5BdUJ9hCyDgMVAGs/IcEEA==
252 prv_test_raw = b64decode("""
253 MIHiAgEBMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQRAEWkl+eblsHWs86SNgRKq
254 SxMOgGhbvR/uZ5/WWfdNG1axvUwVhpcXIxDZUmzQuNzqJBkseI7f5/JjXyTFRF1a
255 +YGBgQG0i7davCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO
256 +K21LDpYVfDPs2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0Em
257 Zf8T3ae/J1Jo6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzg==
260 def test_cert_and_encrypted_key(self):
261 pfx_raw = b64decode(b"""
262 MIIFKwIBAzCCBMQGCSqGSIb3DQEHAaCCBLUEggSxMIIErTCCAswGCSqGSIb3DQEH
263 AaCCAr0EggK5MIICtTCCArEGCyqGSIb3DQEMCgEDoIICSjCCAkYGCiqGSIb3DQEJ
264 FgGgggI2BIICMjCCAi4wggHboAMCAQICBAGMuoQwCgYIKoUDBwEBAwIwODENMAsG
265 A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt
266 Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME
267 VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiA1MTItYml0
268 MIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQOBhAAEgYC0i7davCkOGGVcYqFP
269 tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts
270 /JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4
271 hMts9HYLnxbwJDMNVGuIHV6gzqOBhzCBhDBjBgNVHSMEXDBagBSsbA5MRGaiQpbi
272 nwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsy
273 NjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBR+BlcJmAyt
274 awioV+55AFg6ydegpDAKBggqhQMHAQEDAgNBAApeqfHQG6YvR5Pt5oDMiNFiIdey
275 K5a0qf5gdBe2czLfF1A9Q8M9ya658Xl53zLzgOQXVCfYQsg4DFQBrPyHBBAxVDAj
276 BgkqhkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkU
277 MSAeHgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTCCAdkGCSqGSIb3DQEH
278 AaCCAcoEggHGMIIBwjCCAb4GCyqGSIb3DQEMCgECoIIBVzCCAVMwWQYJKoZIhvcN
279 AQUNMEwwKQYJKoZIhvcNAQUMMBwECKf4N7NMwugqAgIIADAMBggqhQMHAQEEAgUA
280 MB8GCSqFAwcBAQUCAjASBBAlmt2WDfaPJlsAs0mLKglzBIH1DMvEacbbWRNDVSnX
281 JLWygYrKoipdOjDA/2HEnBZ34uFOLNheUqiKpCPoFpbR2GBiVYVTVK9ibiczgaca
282 EQYzDXtcS0QCZOxpKWfteAlbdJLC/SqPurPYyKi0MVRUPROhbisFASDT38HDH1Dh
283 0dL5f6ga4aPWLrWbbgWERFOoOPyh4DotlPF37AQOwiEjsbyyRHq3HgbWiaxQRuAh
284 eqHOn4QVGY92/HFvJ7u3TcnQdLWhTe/lh1RHLNF3RnXtN9if9zC23laDZOiWZplU
285 yLrUiTCbHrtn1RppPDmLFNMt9dJ7KKgCkOi7Zm5nhqPChbywX13wcfYxVDAjBgkq
286 hkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkUMSAe
287 HgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTBeME4wCgYIKoUDBwEBAgME
288 QAkBKw4ihn7pSIYTEhu0bcvTPZjI3WgVxCkUVlOsc80G69EKFEOTnObGJGSKJ51U
289 KkOsXF0a7+VBZf3BcVVQh9UECIVEtO+VpuskAgIIAA==
291 pfx = PFX().decod(pfx_raw)
292 _, outer_safe_contents = pfx["authSafe"]["content"].defined
294 safe_contents = OctetStringSafeContents().decod(bytes(
295 outer_safe_contents[0]["bagValue"]
297 safe_bag = safe_contents[0]
298 self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
299 cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
300 self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
301 _, cert = cert_bag["certValue"].defined
302 self.assertEqual(Certificate(cert), self.cert_test)
304 safe_contents = OctetStringSafeContents().decod(bytes(
305 outer_safe_contents[1]["bagValue"]
307 safe_bag = safe_contents[0]
308 self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag)
309 shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"]))
310 _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
311 _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
312 _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
313 ukm = bytes(enc_scheme_params["ukm"])
314 key = gost34112012_pbkdf2(
315 password=self.password,
316 salt=bytes(pbkdf2_params["salt"]["specified"]),
317 iterations=int(pbkdf2_params["iterationCount"]),
320 # key = hexdec("4b7ae649ca31dd5fe3243a91a5188c03f1d7049bec8e0d241c0e1e8c39ea4c1f")
321 key_enc, key_mac = kdf_tree_gostr3411_2012_256(
322 key, b"kdf tree", ukm[GOST3412Kuznechik.blocksize // 2:], 2,
324 ciphertext = bytes(shrouded_key_bag["encryptedData"])
325 plaintext = ctr_acpkm(
327 GOST3412Kuznechik(key_enc).encrypt,
328 section_size=256 * 1024,
329 bs=GOST3412Kuznechik.blocksize,
331 iv=ukm[:GOST3412Kuznechik.blocksize // 2],
333 mac_expected = plaintext[-GOST3412Kuznechik.blocksize:]
334 plaintext = plaintext[:-GOST3412Kuznechik.blocksize]
336 GOST3412Kuznechik(key_mac).encrypt,
337 GOST3412Kuznechik.blocksize,
340 self.assertSequenceEqual(mac, mac_expected)
341 self.assertSequenceEqual(plaintext, self.prv_test_raw)
343 mac_data = pfx["macData"]
344 mac_key = gost34112012_pbkdf2(
345 password=self.password,
346 salt=bytes(mac_data["macSalt"]),
347 iterations=int(mac_data["iterations"]),
350 # mac_key = hexdec("a81d1bc91a4a5cf1fd7320f92dda7e5b285816c3b20826a382d7ed0cbf3a9bf4")
351 self.assertSequenceEqual(
354 msg=SafeContents(outer_safe_contents).encode(),
355 digestmod=GOST34112012512,
357 bytes(mac_data["mac"]["digest"]),
359 self.assertTrue(gost3410.verify(
362 GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1],
363 bytes(cert["signatureValue"]),
366 def test_encrypted_cert_and_key(self):
367 pfx_raw = b64decode(b"""
368 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
399 pfx = PFX().decod(pfx_raw)
400 _, outer_safe_contents = pfx["authSafe"]["content"].defined
402 encrypted_data = EncryptedData().decod(bytes(
403 outer_safe_contents[0]["bagValue"]
405 eci = encrypted_data["encryptedContentInfo"]
406 self.assertEqual(eci["contentEncryptionAlgorithm"]["algorithm"], id_pbes2)
407 pbes2_params = PBES2Params().decod(bytes(
408 eci["contentEncryptionAlgorithm"]["parameters"]
410 _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
411 _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
412 ukm = bytes(enc_scheme_params["ukm"])
413 key = gost34112012_pbkdf2(
414 password=self.password,
415 salt=bytes(pbkdf2_params["salt"]["specified"]),
416 iterations=int(pbkdf2_params["iterationCount"]),
419 # key = hexdec("d066a96fb326ba896a2352d3f40240a4ded6e7e7bd5b4db6b5241d631c8c381c")
420 key_enc, key_mac = kdf_tree_gostr3411_2012_256(
421 key, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2,
423 ciphertext = bytes(eci["encryptedContent"])
424 plaintext = ctr_acpkm(
426 GOST3412Magma(key_enc).encrypt,
427 section_size=8 * 1024,
428 bs=GOST3412Magma.blocksize,
430 iv=ukm[:GOST3412Magma.blocksize // 2],
432 mac_expected = plaintext[-GOST3412Magma.blocksize:]
433 plaintext = plaintext[:-GOST3412Magma.blocksize]
435 GOST3412Magma(key_mac).encrypt,
436 GOST3412Magma.blocksize,
439 self.assertSequenceEqual(mac, mac_expected)
441 safe_contents = SafeContents().decod(plaintext)
442 safe_bag = safe_contents[0]
443 self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
444 cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
445 self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
446 _, cert = cert_bag["certValue"].defined
447 self.assertEqual(Certificate(cert), self.cert_test)
449 safe_contents = OctetStringSafeContents().decod(bytes(
450 outer_safe_contents[1]["bagValue"]
452 safe_bag = safe_contents[0]
453 self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag)
454 shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"]))
455 _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
456 _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
457 _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
458 ukm = bytes(enc_scheme_params["ukm"])
459 key = gost34112012_pbkdf2(
460 password=self.password,
461 salt=bytes(pbkdf2_params["salt"]["specified"]),
462 iterations=int(pbkdf2_params["iterationCount"]),
465 # key = hexdec("f840d001fd11441e0fb7ccf48f471915e5bf35275309dbe7ade9da4fe460ba7e")
466 ciphertext = bytes(shrouded_key_bag["encryptedData"])
467 plaintext = ctr_acpkm(
469 GOST3412Magma(key).encrypt,
470 section_size=8 * 1024,
471 bs=GOST3412Magma.blocksize,
473 iv=ukm[:GOST3412Magma.blocksize // 2],
475 self.assertSequenceEqual(plaintext, self.prv_test_raw)
477 mac_data = pfx["macData"]
478 mac_key = gost34112012_pbkdf2(
479 password=self.password,
480 salt=bytes(mac_data["macSalt"]),
481 iterations=int(mac_data["iterations"]),
484 # mac_key = hexdec("084f81782af1534ffd67e3c579c14cb45d7a6f659f46fdbb51a552e874e66fb2")
485 self.assertSequenceEqual(
488 msg=SafeContents(outer_safe_contents).encode(),
489 digestmod=GOST34112012512,
491 bytes(mac_data["mac"]["digest"]),
495 curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"]
496 # sender_prv_raw = hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8")
497 sender_cert = Certificate().decod(b64decode("""
498 MIIB6zCCAZigAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2
499 MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw
500 MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD
501 VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMH
502 AQEBATALBgkqhQMHAQIBAQEDQwAEQJYpDRNiWWqDgaZje0EmLLOldQ35o5X1ZuZN
503 SKequYQc/soI3OgDMWD7ThJJCk01IelCeb6MsBmG4lol+pnpVtOjgYcwgYQwYwYD
504 VR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRL
505 MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6
506 gTAdBgNVHQ4EFgQUPx5RgcjkifhlJm4/jQdkbm30rVQwCgYIKoUDBwEBAwIDQQA6
507 8x7Vk6PvP/8xOGHhf8PuqaXAYskSyJPuBu+3Bo/PEj10devwc1J9uYWIDCGdKKPy
510 recipient_prv_raw = hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1]
511 recipient_prv = gost3410.prv_unmarshal(recipient_prv_raw)
512 recipient_cert = Certificate().decod(b64decode("""
513 MIIB6jCCAZegAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2
514 MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw
515 MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD
516 VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBeMBcGCCqFAwcB
517 AQEBMAsGCSqFAwcBAgEBAQNDAARAvyeCGXMsYwpYe5aE0w8w3m4vpKQapGInqpnF
518 lv7h08psFP0s1W80q3BR534F4TmR+o5+iU+AW6ycvWuc73JEQ6OBhzCBhDBjBgNV
519 HSMEXDBagBSsbA5MRGaiQpbinwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsy
520 NjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqB
521 MB0GA1UdDgQWBBQ35gHPN1bx8l2eEMTbrtIg+5MU0TAKBggqhQMHAQEDAgNBABF2
522 RHDaRqQuBS2yu7yGIGFgA6c/LG4GKjSOwYsRVmXJNNkQ4TB7PB8j3q7gx2koPsVB
525 self.assertTrue(gost3410.verify(
528 GOST34112012256(sender_cert["tbsCertificate"].encode()).digest()[::-1],
529 bytes(sender_cert["signatureValue"]),
531 self.assertTrue(gost3410.verify(
534 GOST34112012256(recipient_cert["tbsCertificate"].encode()).digest()[::-1],
535 bytes(recipient_cert["signatureValue"]),
538 pfx_raw = b64decode("""
539 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
596 pfx = PFX().decod(pfx_raw)
597 self.assertEqual(pfx["authSafe"]["contentType"], id_signedData)
599 sd = SignedData().decod(bytes(pfx["authSafe"]["content"]))
600 self.assertEqual(sd["certificates"][0]["certificate"], sender_cert)
601 si = sd["signerInfos"][0]
603 si["digestAlgorithm"]["algorithm"],
604 id_tc26_gost3411_2012_256,
607 bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"]
608 if attr["attrType"] == id_messageDigest
610 sender_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
611 sender_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
613 content = bytes(sd["encapContentInfo"]["eContent"])
614 self.assertSequenceEqual(digest, GOST34112012256(content).digest())
615 self.assertTrue(gost3410.verify(
619 SignedAttributes(si["signedAttrs"]).encode()
621 bytes(si["signature"]),
624 outer_safe_contents = SafeContents().decod(content)
626 safe_bag = outer_safe_contents[0]
627 self.assertEqual(safe_bag["bagId"], id_data)
628 safe_contents = OctetStringSafeContents().decod(bytes(safe_bag["bagValue"]))
629 safe_bag = safe_contents[0]
630 self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
631 cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
632 self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
633 _, cert = cert_bag["certValue"].defined
634 self.assertEqual(Certificate(cert), self.cert_test)
636 safe_bag = outer_safe_contents[1]
637 self.assertEqual(safe_bag["bagId"], id_envelopedData)
638 ed = EnvelopedData().decod(bytes(safe_bag["bagValue"]))
639 kari = ed["recipientInfos"][0]["kari"]
640 ukm = bytes(kari["ukm"])
642 kari["keyEncryptionAlgorithm"]["algorithm"],
643 id_gostr3412_2015_kuznyechik_wrap_kexp15,
646 kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"],
647 id_tc26_agreement_gost3410_2012_256,
649 kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"])
650 keymat = keg(curve, recipient_prv, sender_pub, ukm)
651 kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:]
653 GOST3412Kuznechik(kek).encrypt,
654 GOST3412Kuznechik(kim).encrypt,
655 GOST3412Kuznechik.blocksize,
657 ukm[24:24 + GOST3412Kuznechik.blocksize // 2],
659 eci = ed["encryptedContentInfo"]
661 eci["contentEncryptionAlgorithm"]["algorithm"],
662 id_gostr3412_2015_kuznyechik_ctracpkm,
665 eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"]
669 GOST3412Kuznechik(cek).encrypt,
671 GOST3412Kuznechik.blocksize,
672 bytes(eci["encryptedContent"]),
673 eci_ukm[:GOST3412Kuznechik.blocksize // 2],
676 safe_contents = SafeContents().decod(content)
677 safe_bag = safe_contents[0]
678 self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_keyBag)
679 KeyBag().decod(bytes(safe_bag["bagValue"]))
680 self.assertSequenceEqual(bytes(safe_bag["bagValue"]), self.prv_test_raw)