]> Cypherpunks.ru repositories - pygost.git/blob - pygost/test_pfx.py
Draft update PKCS#12 test vectors
[pygost.git] / pygost / test_pfx.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2021 Sergey Matveev <stargrave@stargrave.org>
4 #
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.
8 #
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.
13 #
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/>.
16
17 from base64 import b64decode
18 from hmac import new as hmac_new
19 from unittest import skipIf
20 from unittest import TestCase
21
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
36
37
38 try:
39     from pyderasn import OctetString
40     from pygost.asn1schemas.cms import EncryptedData
41     from pygost.asn1schemas.cms import EnvelopedData
42     from pygost.asn1schemas.cms import SignedAttributes
43     from pygost.asn1schemas.cms import SignedData
44     from pygost.asn1schemas.oids import id_data
45     from pygost.asn1schemas.oids import id_envelopedData
46     from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm
47     from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15
48     from pygost.asn1schemas.oids import id_messageDigest
49     from pygost.asn1schemas.oids import id_pbes2
50     from pygost.asn1schemas.oids import id_pkcs12_bagtypes_certBag
51     from pygost.asn1schemas.oids import id_pkcs12_bagtypes_keyBag
52     from pygost.asn1schemas.oids import id_pkcs12_bagtypes_pkcs8ShroudedKeyBag
53     from pygost.asn1schemas.oids import id_pkcs9_certTypes_x509Certificate
54     from pygost.asn1schemas.oids import id_signedData
55     from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_256
56     from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256
57     from pygost.asn1schemas.pfx import CertBag
58     from pygost.asn1schemas.pfx import KeyBag
59     from pygost.asn1schemas.pfx import OctetStringSafeContents
60     from pygost.asn1schemas.pfx import PBES2Params
61     from pygost.asn1schemas.pfx import PFX
62     from pygost.asn1schemas.pfx import PKCS8ShroudedKeyBag
63     from pygost.asn1schemas.pfx import SafeContents
64     from pygost.asn1schemas.x509 import Certificate
65 except ImportError:
66     pyderasn_exists = False
67 else:
68     pyderasn_exists = True
69
70
71 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
72 class TestPFX(TestCase):
73     """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf)
74     """
75     pfx_raw = b64decode("""
76 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
106 mwcl1xQV2G5/fgICB9A=
107     """)
108     password = u"Пароль для PFX"
109
110     def test_shrouded_key_bag(self):
111         private_key_info_expected = b64decode(b"""
112 MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG
113 G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY=
114         """)
115
116         pfx, tail = PFX().decode(self.pfx_raw)
117         self.assertSequenceEqual(tail, b"")
118         _, outer_safe_contents = pfx["authSafe"]["content"].defined
119         safe_contents, tail = OctetStringSafeContents().decode(
120             bytes(outer_safe_contents[0]["bagValue"]),
121         )
122         self.assertSequenceEqual(tail, b"")
123         safe_bag = safe_contents[0]
124         shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode(
125             bytes(safe_bag["bagValue"]),
126         )
127         self.assertSequenceEqual(tail, b"")
128         _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
129         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
130         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
131
132         key = gost34112012_pbkdf2(
133             password=self.password.encode("utf-8"),
134             salt=bytes(pbkdf2_params["salt"]["specified"]),
135             iterations=int(pbkdf2_params["iterationCount"]),
136             dklen=32,
137         )
138         # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8")
139         self.assertSequenceEqual(
140             cfb_decrypt(
141                 key,
142                 bytes(shrouded_key_bag["encryptedData"]),
143                 iv=bytes(enc_scheme_params["iv"]),
144                 sbox="id-tc26-gost-28147-param-Z",
145             ),
146             private_key_info_expected,
147         )
148
149     def test_encrypted_data(self):
150         cert_bag_expected = b64decode(b"""
151 MIIDSjCCA0YGCyqGSIb3DQEMCgEDoIIDHjCCAxoGCiqGSIb3DQEJFgGgggMKBIIDBjCCAwIwggKt
152 oAMCAQICEAHQaF8xH5bAAAAACycJAAEwDAYIKoUDBwEBAwIFADBgMQswCQYDVQQGEwJSVTEVMBMG
153 A1UEBwwM0JzQvtGB0LrQstCwMQ8wDQYDVQQKDAbQotCaMjYxKTAnBgNVBAMMIENBIGNlcnRpZmlj
154 YXRlIChQS0NTIzEyIGV4YW1wbGUpMB4XDTE1MDMyNzA3MjUwMFoXDTIwMDMyNzA3MjMwMFowZDEL
155 MAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MS0wKwYD
156 VQQDDCRUZXN0IGNlcnRpZmljYXRlIDEgKFBLQ1MjMTIgZXhhbXBsZSkwZjAfBggqhQMHAQEBATAT
157 BgcqhQMCAiMBBggqhQMHAQECAgNDAARA1xzymkpvr2dYJT8WTOX3Dt96/+hGsXNytUQpkWB5ImJM
158 4tg9AsC4RIUwV5H41MhG0uBRFweTzN6AsAdBvhTClYEJADI3MDkwMDAxo4IBKTCCASUwKwYDVR0Q
159 BCQwIoAPMjAxNTAzMjcwNzI1MDBagQ8yMDE2MDMyNzA3MjUwMFowDgYDVR0PAQH/BAQDAgTwMB0G
160 A1UdDgQWBBQhWOsRQ68yYN2Utg/owHoWcqsVbTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
161 AwQwDAYDVR0TAQH/BAIwADCBmQYDVR0jBIGRMIGOgBQmnc7Xh5ykb5t/BMwOkxA4drfEmqFkpGIw
162 YDELMAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MSkw
163 JwYDVQQDDCBDQSBjZXJ0aWZpY2F0ZSAoUEtDUyMxMiBleGFtcGxlKYIQAdBoXvL8TSAAAAALJwkA
164 ATAMBggqhQMHAQEDAgUAA0EA9oq0Vvk8kkgIwkp0x0J5eKtia4MNTiwKAm7jgnCZIx3O98BThaTX
165 3ZQhEo2RL9pTCPr6wFMheeJ+YdGMReXvsjEVMBMGCSqGSIb3DQEJFTEGBAQBAAAA
166         """)
167
168         pfx, tail = PFX().decode(self.pfx_raw)
169         self.assertSequenceEqual(tail, b"")
170         _, outer_safe_contents = pfx["authSafe"]["content"].defined
171         _, encrypted_data = outer_safe_contents[1]["bagValue"].defined
172         _, pbes2_params = encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"].defined
173         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
174         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
175         key = gost34112012_pbkdf2(
176             password=self.password.encode("utf-8"),
177             salt=bytes(pbkdf2_params["salt"]["specified"]),
178             iterations=int(pbkdf2_params["iterationCount"]),
179             dklen=32,
180         )
181         # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc")
182         self.assertSequenceEqual(
183             cfb_decrypt(
184                 key,
185                 bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]),
186                 iv=bytes(enc_scheme_params["iv"]),
187                 sbox="id-tc26-gost-28147-param-Z",
188             ),
189             cert_bag_expected,
190         )
191
192     def test_mac(self):
193         pfx, tail = PFX().decode(self.pfx_raw)
194         self.assertSequenceEqual(tail, b"")
195         _, outer_safe_contents = pfx["authSafe"]["content"].defined
196         mac_data = pfx["macData"]
197         mac_key = gost34112012_pbkdf2(
198             password=self.password.encode("utf-8"),
199             salt=bytes(mac_data["macSalt"]),
200             iterations=int(mac_data["iterations"]),
201             dklen=96,
202         )[-32:]
203         # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5")
204         self.assertSequenceEqual(
205             hmac_new(
206                 key=mac_key,
207                 msg=SafeContents(outer_safe_contents).encode(),
208                 digestmod=GOST34112012512,
209             ).digest(),
210             bytes(mac_data["mac"]["digest"]),
211         )
212
213
214 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
215 class TestPFX2020(TestCase):
216     """PFX test vectors from newer PKCS#12 update
217     """
218     ca_prv_raw = hexdec("092F8D059E97E22B90B1AE99F0087FC4D26620B91550CBB437C191005A290810")
219     ca_curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"]
220     ca_cert = Certificate().decod(b64decode(b"""
221         MIIB+TCCAaagAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
222         cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
223         MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx
224         5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMHAQEBATALBgkq
225         hQMHAQIBAQEDQwAEQBpKgpyPDnhQAJyLqy8Qs0XQhgxEhby6tSypqYimgbjpcKqtU6
226         4jpDXc3h3BxGxtl2oHJ/4YLZ/ll87dto3ltMqjgZgwgZUwYwYDVR0jBFwwWoAUrGwO
227         TERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHk
228         NBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUrGwO
229         TERmokKW4p8JOyVm88ukUyowDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNBAB
230         Gg3nhgQ5oCKbqlEdVaRxH+1WX4wVkawGXuTYkr1AC2OWw3ZC14Vvg3nazm8UMWUZtk
231         vu1kJcHQ4jFKkjUeg2E=
232     """))
233     ca_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
234         ca_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
235     ))))
236     password = "Пароль для PFX".encode("utf-8")
237     cert_test = Certificate().decod(b64decode(b"""
238         MIICLjCCAdugAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
239         cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
240         MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYDVQQDEy
241         FPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaAwFwYIKoUDBwEBAQIw
242         CwYJKoUDBwECAQIBA4GEAASBgLSLt1q8KQ4YZVxioU+1LV9QhE7MHR9gBEh7S1yVNG
243         lqt7+rNG5VFqmrPM74rbUsOlhV8M+zZKprXdk35Oz8lSW/n2oIUHZxikXIH/SSHj4r
244         v3K/Puvz7hYTQSZl/xPdp78nUmjrEa6d5wfX8biEy2z0dgufFvAkMw1Ua4gdXqDOo4
245         GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0wCwYD
246         VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaX
247         SCBAGMuoEwHQYDVR0OBBYEFH4GVwmYDK1rCKhX7nkAWDrJ16CkMAoGCCqFAwcBAQMC
248         A0EACl6p8dAbpi9Hk+3mgMyI0WIh17IrlrSp/mB0F7ZzMt8XUD1Dwz3JrrnxeXnfMv
249         OA5BdUJ9hCyDgMVAGs/IcEEA==
250     """))
251     prv_test_raw = b64decode("""
252         MIHiAgEBMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQRAEWkl+eblsHWs86SNgRKq
253         SxMOgGhbvR/uZ5/WWfdNG1axvUwVhpcXIxDZUmzQuNzqJBkseI7f5/JjXyTFRF1a
254         +YGBgQG0i7davCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO
255         +K21LDpYVfDPs2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0Em
256         Zf8T3ae/J1Jo6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzg==
257     """)
258
259     def test_cert_and_encrypted_key(self):
260         pfx_raw = b64decode(b"""
261             MIIFKwIBAzCCBMQGCSqGSIb3DQEHAaCCBLUEggSxMIIErTCCAswGCSqGSIb3DQEH
262             AaCCAr0EggK5MIICtTCCArEGCyqGSIb3DQEMCgEDoIICSjCCAkYGCiqGSIb3DQEJ
263             FgGgggI2BIICMjCCAi4wggHboAMCAQICBAGMuoQwCgYIKoUDBwEBAwIwODENMAsG
264             A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt
265             Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME
266             VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiA1MTItYml0
267             MIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQOBhAAEgYC0i7davCkOGGVcYqFP
268             tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts
269             /JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4
270             hMts9HYLnxbwJDMNVGuIHV6gzqOBhzCBhDBjBgNVHSMEXDBagBSsbA5MRGaiQpbi
271             nwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsy
272             NjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBR+BlcJmAyt
273             awioV+55AFg6ydegpDAKBggqhQMHAQEDAgNBAApeqfHQG6YvR5Pt5oDMiNFiIdey
274             K5a0qf5gdBe2czLfF1A9Q8M9ya658Xl53zLzgOQXVCfYQsg4DFQBrPyHBBAxVDAj
275             BgkqhkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkU
276             MSAeHgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTCCAdkGCSqGSIb3DQEH
277             AaCCAcoEggHGMIIBwjCCAb4GCyqGSIb3DQEMCgECoIIBVzCCAVMwWQYJKoZIhvcN
278             AQUNMEwwKQYJKoZIhvcNAQUMMBwECKf4N7NMwugqAgIIADAMBggqhQMHAQEEAgUA
279             MB8GCSqFAwcBAQUCAjASBBAlmt2WDfaPJlsAs0mLKglzBIH1DMvEacbbWRNDVSnX
280             JLWygYrKoipdOjDA/2HEnBZ34uFOLNheUqiKpCPoFpbR2GBiVYVTVK9ibiczgaca
281             EQYzDXtcS0QCZOxpKWfteAlbdJLC/SqPurPYyKi0MVRUPROhbisFASDT38HDH1Dh
282             0dL5f6ga4aPWLrWbbgWERFOoOPyh4DotlPF37AQOwiEjsbyyRHq3HgbWiaxQRuAh
283             eqHOn4QVGY92/HFvJ7u3TcnQdLWhTe/lh1RHLNF3RnXtN9if9zC23laDZOiWZplU
284             yLrUiTCbHrtn1RppPDmLFNMt9dJ7KKgCkOi7Zm5nhqPChbywX13wcfYxVDAjBgkq
285             hkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkUMSAe
286             HgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTBeME4wCgYIKoUDBwEBAgME
287             QAkBKw4ihn7pSIYTEhu0bcvTPZjI3WgVxCkUVlOsc80G69EKFEOTnObGJGSKJ51U
288             KkOsXF0a7+VBZf3BcVVQh9UECIVEtO+VpuskAgIIAA==
289         """)
290         pfx = PFX().decod(pfx_raw)
291         _, outer_safe_contents = pfx["authSafe"]["content"].defined
292
293         safe_contents = OctetStringSafeContents().decod(bytes(
294             outer_safe_contents[0]["bagValue"]
295         ))
296         safe_bag = safe_contents[0]
297         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
298         cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
299         self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
300         _, cert = cert_bag["certValue"].defined
301         self.assertEqual(Certificate(cert), self.cert_test)
302
303         safe_contents = OctetStringSafeContents().decod(bytes(
304             outer_safe_contents[1]["bagValue"]
305         ))
306         safe_bag = safe_contents[0]
307         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag)
308         shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"]))
309         _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
310         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
311         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
312         ukm = bytes(enc_scheme_params["ukm"])
313         key = gost34112012_pbkdf2(
314             password=self.password,
315             salt=bytes(pbkdf2_params["salt"]["specified"]),
316             iterations=int(pbkdf2_params["iterationCount"]),
317             dklen=32,
318         )
319         # key = hexdec("4b7ae649ca31dd5fe3243a91a5188c03f1d7049bec8e0d241c0e1e8c39ea4c1f")
320         key_enc, key_mac = kdf_tree_gostr3411_2012_256(
321             key, b"kdf tree", ukm[GOST3412Kuznechik.blocksize // 2:], 2,
322         )
323         ciphertext = bytes(shrouded_key_bag["encryptedData"])
324         plaintext = ctr_acpkm(
325             GOST3412Kuznechik,
326             GOST3412Kuznechik(key_enc).encrypt,
327             section_size=256 * 1024,
328             bs=GOST3412Kuznechik.blocksize,
329             data=ciphertext,
330             iv=ukm[:GOST3412Kuznechik.blocksize // 2],
331         )
332         mac_expected = plaintext[-GOST3412Kuznechik.blocksize:]
333         plaintext = plaintext[:-GOST3412Kuznechik.blocksize]
334         mac = omac(
335             GOST3412Kuznechik(key_mac).encrypt,
336             GOST3412Kuznechik.blocksize,
337             plaintext,
338         )
339         self.assertSequenceEqual(mac, mac_expected)
340         self.assertSequenceEqual(plaintext, self.prv_test_raw)
341
342         mac_data = pfx["macData"]
343         mac_key = gost34112012_pbkdf2(
344             password=self.password,
345             salt=bytes(mac_data["macSalt"]),
346             iterations=int(mac_data["iterations"]),
347             dklen=96,
348         )[-32:]
349         # mac_key = hexdec("a81d1bc91a4a5cf1fd7320f92dda7e5b285816c3b20826a382d7ed0cbf3a9bf4")
350         self.assertSequenceEqual(
351             hmac_new(
352                 key=mac_key,
353                 msg=SafeContents(outer_safe_contents).encode(),
354                 digestmod=GOST34112012512,
355             ).digest(),
356             bytes(mac_data["mac"]["digest"]),
357         )
358         self.assertTrue(gost3410.verify(
359             self.ca_curve,
360             self.ca_pub,
361             GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1],
362             bytes(cert["signatureValue"]),
363         ))
364
365     def test_encrypted_cert_and_key(self):
366         pfx_raw = b64decode(b"""
367             MIIFjAIBAzCCBSUGCSqGSIb3DQEHAaCCBRYEggUSMIIFDjCCA0EGCSqGSIb3DQEH
368             BqCCAzIwggMuAgEAMIIDJwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBIMCkGCSqG
369             SIb3DQEFDDAcBAgUuSVGsSwGjQICCAAwDAYIKoUDBwEBBAIFADAbBgkqhQMHAQEF
370             AQIwDgQM9Hk3dagtS48+G/x+gIICwWGPqxxN+sTrKbruRf9R5Ya9cf5AtO1frqMn
371             f1eULfmZmTg/BdE51QQ+Vbnh3v1kmspr6h2+e4Wli+ndEeCWG6A6X/G22h/RAHW2
372             YrVmf6cCWxW+YrqzT4h/8RQL/9haunD5LmHPLVsYrEai0OwbgXayDSwARVJQLQYq
373             sLNmZK5ViN+fRiS5wszVJ3AtVq8EuPt41aQEKwPy2gmH4S6WmnQRC6W7aoqmIifF
374             PJENJNn5K2M1J6zNESs6bFtYNKMArNqtvv3rioY6eAaaLy6AV6ljsekmqodHmQjv
375             Y4eEioJs0xhpXhZY69PXT+ZBeHv6MSheBhwXqxAd1DqtPTafMjNK8rqKCap9TtPG
376             vONvo5W9dgwegxRRQzlum8dzV4m1W9Aq4W7t8/UcxDWRz3k6ijFPlGaA9+8ZMTEO
377             RHhBRvM6OY2/VNNxbgxWfGYuPxpSi3YnCZIPmBEe5lU/Xv7KjzFusGM38F8YR61k
378             4/QNpKI1QUv714YKfaUQznshGGzILv1NGID62pl1+JI3vuawi2mDMrmkuM9QFU9v
379             /kRP+c2uBHDuOGEUUSNhF08p7+w3vxplatGWXH9fmIsPBdk2f3wkn+rwoqrEuijM
380             I/bCAylU/M0DMKhAo9j31UYSZdi4fsfRWYDJMq/8FPn96tuo+oCpbqv3NUwpZM/8
381             Li4xqgTHtYw/+fRG0/P6XadNEiII/TYjenLfVHXjAHOVJsVeCu/t3EsMYHQddNCh
382             rFk/Ic2PdIQOyB4/enpW0qrKegSbyZNuF1WI4zl4mI89L8dTQBUkhy45yQXZlDD8
383             k1ErYdtdEsPtz/4zuSpbnmwCEIRoOuSXtGuJP+tbcWEXRKM2UBgi3qBjpn7DU18M
384             tsrRM9pDdadl8mT/Vfh9+B8dZBZVxgQu70lMPEGexbUkYHuFCCnyi9J0V92StbIz
385             Elxla1VebjCCAcUGCSqGSIb3DQEHAaCCAbYEggGyMIIBrjCCAaoGCyqGSIb3DQEM
386             CgECoIIBQzCCAT8wVQYJKoZIhvcNAQUNMEgwKQYJKoZIhvcNAQUMMBwECP0EQk0O
387             1twvAgIIADAMBggqhQMHAQEEAgUAMBsGCSqFAwcBAQUBATAOBAzwxSqgAAAAAAAA
388             AAAEgeUqj9mI3RDfK5hMd0EeYws7foZK/5ANr2wUhP5qnDjAZgn76lExJ+wuvlnS
389             9PChfWVugvdl/9XJgQvvr9Cu4pOh4ICXplchcy0dGk/MzItHRVC5wK2nTxwQ4kKT
390             kG9xhLFzoD16dhtqX0+/dQg9G8pE5EzCBIYRXLm1Arcz9k7KVsTJuNMjFrr7EQuu
391             Tr80ATSQOtsq50zpFyrpznVPGCrOdIjpymZxNdvw48bZxqTtRVDxCYATOGqz0pwH
392             ClWULHD9LIajLMB2GhBKyQw6ujIlltJs0T+WNdX/AT2FLi1LFSS3+Cj9MVQwIwYJ
393             KoZIhvcNAQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMC0GCSqGSIb3DQEJFDEg
394             Hh4AcAAxADIARgByAGkAZQBuAGQAbAB5AE4AYQBtAGUwXjBOMAoGCCqFAwcBAQID
395             BEDp4e22JmXdnvR0xA99yQuzQuJ8pxBeOpsLm2dZQqt3Fje5zqW1uk/7VOcfV5r2
396             bKm8nsLOs2rPT8hBOoeAZvOIBAjGIUHw6IjG2QICCAA=
397         """)
398         pfx = PFX().decod(pfx_raw)
399         _, outer_safe_contents = pfx["authSafe"]["content"].defined
400
401         encrypted_data = EncryptedData().decod(bytes(
402             outer_safe_contents[0]["bagValue"]
403         ))
404         eci = encrypted_data["encryptedContentInfo"]
405         self.assertEqual(eci["contentEncryptionAlgorithm"]["algorithm"], id_pbes2)
406         pbes2_params = PBES2Params().decod(bytes(
407             eci["contentEncryptionAlgorithm"]["parameters"]
408         ))
409         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
410         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
411         ukm = bytes(enc_scheme_params["ukm"])
412         key = gost34112012_pbkdf2(
413             password=self.password,
414             salt=bytes(pbkdf2_params["salt"]["specified"]),
415             iterations=int(pbkdf2_params["iterationCount"]),
416             dklen=32,
417         )
418         # key = hexdec("d066a96fb326ba896a2352d3f40240a4ded6e7e7bd5b4db6b5241d631c8c381c")
419         key_enc, key_mac = kdf_tree_gostr3411_2012_256(
420             key, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2,
421         )
422         ciphertext = bytes(eci["encryptedContent"])
423         plaintext = ctr_acpkm(
424             GOST3412Magma,
425             GOST3412Magma(key_enc).encrypt,
426             section_size=8 * 1024,
427             bs=GOST3412Magma.blocksize,
428             data=ciphertext,
429             iv=ukm[:GOST3412Magma.blocksize // 2],
430         )
431         mac_expected = plaintext[-GOST3412Magma.blocksize:]
432         plaintext = plaintext[:-GOST3412Magma.blocksize]
433         mac = omac(
434             GOST3412Magma(key_mac).encrypt,
435             GOST3412Magma.blocksize,
436             plaintext,
437         )
438         self.assertSequenceEqual(mac, mac_expected)
439
440         safe_contents = SafeContents().decod(plaintext)
441         safe_bag = safe_contents[0]
442         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
443         cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
444         self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
445         _, cert = cert_bag["certValue"].defined
446         self.assertEqual(Certificate(cert), self.cert_test)
447
448         safe_contents = OctetStringSafeContents().decod(bytes(
449             outer_safe_contents[1]["bagValue"]
450         ))
451         safe_bag = safe_contents[0]
452         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag)
453         shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"]))
454         _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
455         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
456         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
457         ukm = bytes(enc_scheme_params["ukm"])
458         key = gost34112012_pbkdf2(
459             password=self.password,
460             salt=bytes(pbkdf2_params["salt"]["specified"]),
461             iterations=int(pbkdf2_params["iterationCount"]),
462             dklen=32,
463         )
464         # key = hexdec("f840d001fd11441e0fb7ccf48f471915e5bf35275309dbe7ade9da4fe460ba7e")
465         ciphertext = bytes(shrouded_key_bag["encryptedData"])
466         plaintext = ctr_acpkm(
467             GOST3412Magma,
468             GOST3412Magma(key).encrypt,
469             section_size=8 * 1024,
470             bs=GOST3412Magma.blocksize,
471             data=ciphertext,
472             iv=ukm[:GOST3412Magma.blocksize // 2],
473         )
474         self.assertSequenceEqual(plaintext, self.prv_test_raw)
475
476         mac_data = pfx["macData"]
477         mac_key = gost34112012_pbkdf2(
478             password=self.password,
479             salt=bytes(mac_data["macSalt"]),
480             iterations=int(mac_data["iterations"]),
481             dklen=96,
482         )[-32:]
483         # mac_key = hexdec("084f81782af1534ffd67e3c579c14cb45d7a6f659f46fdbb51a552e874e66fb2")
484         self.assertSequenceEqual(
485             hmac_new(
486                 key=mac_key,
487                 msg=SafeContents(outer_safe_contents).encode(),
488                 digestmod=GOST34112012512,
489             ).digest(),
490             bytes(mac_data["mac"]["digest"]),
491         )
492
493     def test_dh(self):
494         curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"]
495         # sender_prv_raw = hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8")
496         sender_cert = Certificate().decod(b64decode("""
497             MIIB6zCCAZigAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
498             cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
499             MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYDVQQDEy
500             FPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMHAQEBATAL
501             BgkqhQMHAQIBAQEDQwAEQJYpDRNiWWqDgaZje0EmLLOldQ35o5X1ZuZNSKequYQc/s
502             oI3OgDMWD7ThJJCk01IelCeb6MsBmG4lol+pnpVtOjgYcwgYQwYwYDVR0jBFwwWoAU
503             rGwOTERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBA
504             MTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQU
505             Px5RgcjkifhlJm4/jQdkbm30rVQwCgYIKoUDBwEBAwIDQQA68x7Vk6PvP/8xOGHhf8
506             PuqaXAYskSyJPuBu+3Bo/PEj10devwc1J9uYWIDCGdKKPybSlnQHqUPBBPM30YX1YN
507         """))
508         recipient_prv_raw = hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1]
509         recipient_prv = gost3410.prv_unmarshal(recipient_prv_raw)
510         recipient_cert = Certificate().decod(b64decode("""
511             MIIB6jCCAZegAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
512             cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
513             MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYDVQQDEy
514             BSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBeMBcGCCqFAwcBAQEBMAsG
515             CSqFAwcBAgEBAQNDAARAvyeCGXMsYwpYe5aE0w8w3m4vpKQapGInqpnFlv7h08psFP
516             0s1W80q3BR534F4TmR+o5+iU+AW6ycvWuc73JEQ6OBhzCBhDBjBgNVHSMEXDBagBSs
517             bA5MRGaiQpbinwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAx
518             MeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBQ3
519             5gHPN1bx8l2eEMTbrtIg+5MU0TAKBggqhQMHAQEDAgNBABF2RHDaRqQuBS2yu7yGIG
520             FgA6c/LG4GKjSOwYsRVmXJNNkQ4TB7PB8j3q7gx2koPsVBm90WfMWSL6SNSh3muuM=
521         """))
522         self.assertTrue(gost3410.verify(
523             self.ca_curve,
524             self.ca_pub,
525             GOST34112012256(sender_cert["tbsCertificate"].encode()).digest()[::-1],
526             bytes(sender_cert["signatureValue"]),
527         ))
528         self.assertTrue(gost3410.verify(
529             self.ca_curve,
530             self.ca_pub,
531             GOST34112012256(recipient_cert["tbsCertificate"].encode()).digest()[::-1],
532             bytes(recipient_cert["signatureValue"]),
533         ))
534
535         pfx_raw = b64decode("""
536             MIIKZwIBAzCCCmAGCSqGSIb3DQEHAqCCClEwggpNAgEBMQwwCgYIKoUDBwEBAgIw
537             ggcrBgkqhkiG9w0BBwGgggccBIIHGDCCBxQwggKdBgkqhkiG9w0BBwGgggKOBIIC
538             ijCCAoYwggKCBgsqhkiG9w0BDAoBA6CCAkowggJGBgoqhkiG9w0BCRYBoIICNgSC
539             AjIwggIuMIIB26ADAgECAgQBjLqEMAoGCCqFAwcBAQMCMDgxDTALBgNVBAoTBFRL
540             MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDAeFw0w
541             MTAxMDEwMDAwMDBaFw00OTEyMzEwMDAwMDBaMDsxDTALBgNVBAoTBFRLMjYxKjAo
542             BgNVBAMTIU9SSUdJTkFUT1I6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBoDAXBggq
543             hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAtIu3WrwpDhhlXGKhT7UtX1CETswd
544             H2AESHtLXJU0aWq3v6s0blUWqas8zvittSw6WFXwz7Nkqmtd2Tfk7PyVJb+faghQ
545             dnGKRcgf9JIePiu/cr8+6/PuFhNBJmX/E92nvydSaOsRrp3nB9fxuITLbPR2C58W
546             8CQzDVRriB1eoM6jgYcwgYQwYwYDVR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88uk
547             UyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
548             MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUfgZXCZgMrWsIqFfueQBY
549             OsnXoKQwCgYIKoUDBwEBAwIDQQAKXqnx0BumL0eT7eaAzIjRYiHXsiuWtKn+YHQX
550             tnMy3xdQPUPDPcmuufF5ed8y84DkF1Qn2ELIOAxUAaz8hwQQMSUwIwYJKoZIhvcN
551             AQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMIIEbwYJKoZIhvcNAQcDoIIEYDCC
552             BFwCAQKgggH7oIIB9zCCAfMwggGgoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODEN
553             MAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAy
554             NTYtYml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UE
555             ChMEVEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYt
556             Yml0MGgwIQYIKoUDBwEBAQEwFQYJKoUDBwECAQEBBggqhQMHAQECAgNDAARAlikN
557             E2JZaoOBpmN7QSYss6V1DfmjlfVm5k1Ip6q5hBz+ygjc6AMxYPtOEkkKTTUh6UJ5
558             voywGYbiWiX6melW06OBhTCBgjBhBgNVHQEEWjBYgBSA2Qz3mfhmTZNTiY7AnnEt
559             p6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
560             MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQU0ZwoR0lm0GMJyQutp9s7
561             uTY3dN8wCgYIKoUDBwEBAwIDQQAeNaIo2l0hJ+fJe/Mtq4cWN+f5ShKuF1me9Bbb
562             DZVcVgE8s4DuVpYsJ7dTuBqGbMcfK+k/4u1RuuVDZkJcHTikMYH/oYH8AgEDoEIw
563             QDA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEw
564             LTEyIDI1Ni1iaXQCBAGMuoKhIgQgVyXLHEfPiRZOVrAtgmddYSS+MjuKuWWA2fC7
565             TlhQ7/wwFwYJKoUDBwEBBwIBMAoGCCqFAwcBAQYBMHYwdDBAMDgxDTALBgNVBAoT
566             BFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdAIE
567             AYy6gwQwSqUJbuUEakskO0Ks2l4YIRo3aUnzluRiy17S7jXmupoIYWUMo44iYXs3
568             05wL+iU1MIIBVAYJKoZIhvcNAQcBMB8GCSqFAwcBAQUCATASBBCdur5wO9c8vgAA
569             AAAAAAAAgIIBJLIXYiLziJdz/VDbl+tEd3lItfQWvLy+P1my1ZRZ3FFnQvuDHHo/
570             i9pB2/8xaaWJhhfXMORJR7DdvwAMvzcwwgX5KIbbjrrK1EFADult5D2MMiRNBa9d
571             jM6w+pgKsZhi+id+v+Hx83xGimM5qUf/oe40TolQKM6uMq9XG/efRYKJ67Bht4s6
572             bKKy2+Uswv91m5uNNyrjSsA5UAW5PSMKovjwnVC/wIWs7Zlk8SVzlK4bdUppJF6F
573             Hca1knFlzvyi5mRnoIqcVe11bDM7GROSBtp4Po23+GGSGIBCMgP2I2ePoarQNYG3
574             jY5W6zoxGuH+xA8D+XrCbWJToNHekfYUlXkGSkEnHc5ZywK3tvvIBXu6z0ebvEKP
575             3VVkBvu1rhZY8/DBXaegggH3MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQED
576             AjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEw
577             LTEyIDI1Ni1iaXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0w
578             CwYDVQQKEwRUSzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEy
579             IDI1Ni1iaXQwaDAhBggqhQMHAQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MA
580             BECWKQ0TYllqg4GmY3tBJiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpN
581             NSHpQnm+jLAZhuJaJfqZ6VbTo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJ
582             jsCecS2npzESoTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjog
583             R09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJ
584             C62n2zu5Njd03zAKBggqhQMHAQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4X
585             WZ70FtsNlVxWATyzgO5Wliwnt1O4GoZsxx8r6T/i7VG65UNmQlwdOKQxggEOMIIB
586             CgIBATBAMDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
587             MzQuMTAtMTIgMjU2LWJpdAIEAYy6gjAKBggqhQMHAQECAqBpMBgGCSqGSIb3DQEJ
588             AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIwMTIyODIxNTcxNVowLwYJ
589             KoZIhvcNAQkEMSIEIDpxzO/5T2vf3BSYXhSaNCL9kMTRIVG6UVv0h1sRa+6tMAoG
590             CCqFAwcBAQEBBEA9mo045ap4k03ZdSacyoZlbvSqNZMHsGUciqE7aWGc5h7U23H8
591             e6qgRVHn9b+Mq3sp57LxHk4Sny0zV8TRwCr8
592         """)
593         pfx = PFX().decod(pfx_raw)
594         self.assertEqual(pfx["authSafe"]["contentType"], id_signedData)
595
596         sd = SignedData().decod(bytes(pfx["authSafe"]["content"]))
597         # self.assertEqual(sd["certificates"][0]["certificate"], sender_cert)
598         si = sd["signerInfos"][0]
599         self.assertEqual(
600             si["digestAlgorithm"]["algorithm"],
601             id_tc26_gost3411_2012_256,
602         )
603         digest = [
604             bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"]
605             if attr["attrType"] == id_messageDigest
606         ][0]
607         sender_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
608             sender_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
609         ))))
610         content = bytes(sd["encapContentInfo"]["eContent"])
611         self.assertSequenceEqual(digest, GOST34112012256(content).digest())
612         self.assertTrue(gost3410.verify(
613             curve,
614             sender_pub,
615             GOST34112012256(
616                 SignedAttributes(si["signedAttrs"]).encode()
617             ).digest()[::-1],
618             bytes(si["signature"]),
619         ))
620
621         outer_safe_contents = SafeContents().decod(content)
622
623         safe_bag = outer_safe_contents[0]
624         self.assertEqual(safe_bag["bagId"], id_data)
625         safe_contents = OctetStringSafeContents().decod(bytes(safe_bag["bagValue"]))
626         safe_bag = safe_contents[0]
627         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
628         cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
629         self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
630         _, cert = cert_bag["certValue"].defined
631         self.assertEqual(Certificate(cert), self.cert_test)
632
633         safe_bag = outer_safe_contents[1]
634         self.assertEqual(safe_bag["bagId"], id_envelopedData)
635         ed = EnvelopedData().decod(bytes(safe_bag["bagValue"]))
636         kari = ed["recipientInfos"][0]["kari"]
637         ukm = bytes(kari["ukm"])
638         self.assertEqual(
639             kari["keyEncryptionAlgorithm"]["algorithm"],
640             id_gostr3412_2015_kuznyechik_wrap_kexp15,
641         )
642         self.assertEqual(
643             kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"],
644             id_tc26_agreement_gost3410_2012_256,
645         )
646         kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"])
647         keymat = keg(curve, recipient_prv, sender_pub, ukm)
648         kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:]
649         cek = kimp15(
650             GOST3412Kuznechik(kek).encrypt,
651             GOST3412Kuznechik(kim).encrypt,
652             GOST3412Kuznechik.blocksize,
653             kexp,
654             ukm[24:24 + GOST3412Kuznechik.blocksize // 2],
655         )
656         eci = ed["encryptedContentInfo"]
657         self.assertEqual(
658             eci["contentEncryptionAlgorithm"]["algorithm"],
659             id_gostr3412_2015_kuznyechik_ctracpkm,
660         )
661         eci_ukm = bytes(
662             eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"]
663         )
664         content = ctr_acpkm(
665             GOST3412Kuznechik,
666             GOST3412Kuznechik(cek).encrypt,
667             256 * 1024,
668             GOST3412Kuznechik.blocksize,
669             bytes(eci["encryptedContent"]),
670             eci_ukm[:GOST3412Kuznechik.blocksize // 2],
671         )
672
673         safe_contents = SafeContents().decod(content)
674         safe_bag = safe_contents[0]
675         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_keyBag)
676         KeyBag().decod(bytes(safe_bag["bagValue"]))
677         self.assertSequenceEqual(bytes(safe_bag["bagValue"]), self.prv_test_raw)