]> Cypherpunks.ru repositories - pygost.git/blob - pygost/test_pfx.py
Fix small lint errors
[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
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
66 except ImportError:
67     pyderasn_exists = False
68 else:
69     pyderasn_exists = True
70
71
72 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
73 class TestPFX(TestCase):
74     """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf)
75     """
76     pfx_raw = b64decode("""
77 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
107 mwcl1xQV2G5/fgICB9A=
108     """)
109     password = u"Пароль для PFX"
110
111     def test_shrouded_key_bag(self):
112         private_key_info_expected = b64decode(b"""
113 MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG
114 G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY=
115         """)
116
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"]),
122         )
123         self.assertSequenceEqual(tail, b"")
124         safe_bag = safe_contents[0]
125         shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode(
126             bytes(safe_bag["bagValue"]),
127         )
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
132
133         key = gost34112012_pbkdf2(
134             password=self.password.encode("utf-8"),
135             salt=bytes(pbkdf2_params["salt"]["specified"]),
136             iterations=int(pbkdf2_params["iterationCount"]),
137             dklen=32,
138         )
139         # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8")
140         self.assertSequenceEqual(
141             cfb_decrypt(
142                 key,
143                 bytes(shrouded_key_bag["encryptedData"]),
144                 iv=bytes(enc_scheme_params["iv"]),
145                 sbox="id-tc26-gost-28147-param-Z",
146             ),
147             private_key_info_expected,
148         )
149
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
167         """)
168
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"]),
180             dklen=32,
181         )
182         # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc")
183         self.assertSequenceEqual(
184             cfb_decrypt(
185                 key,
186                 bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]),
187                 iv=bytes(enc_scheme_params["iv"]),
188                 sbox="id-tc26-gost-28147-param-Z",
189             ),
190             cert_bag_expected,
191         )
192
193     def test_mac(self):
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"]),
202             dklen=96,
203         )[-32:]
204         # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5")
205         self.assertSequenceEqual(
206             hmac_new(
207                 key=mac_key,
208                 msg=SafeContents(outer_safe_contents).encode(),
209                 digestmod=GOST34112012512,
210             ).digest(),
211             bytes(mac_data["mac"]["digest"]),
212         )
213
214
215 @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
216 class TestPFX2020(TestCase):
217     """PFX test vectors from newer PKCS#12 update
218     """
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
232         vu1kJcHQ4jFKkjUeg2E=
233     """))
234     ca_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
235         ca_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
236     ))))
237     password = "Пароль для 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==
251     """))
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==
258     """)
259
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==
290         """)
291         pfx = PFX().decod(pfx_raw)
292         _, outer_safe_contents = pfx["authSafe"]["content"].defined
293
294         safe_contents = OctetStringSafeContents().decod(bytes(
295             outer_safe_contents[0]["bagValue"]
296         ))
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)
303
304         safe_contents = OctetStringSafeContents().decod(bytes(
305             outer_safe_contents[1]["bagValue"]
306         ))
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"]),
318             dklen=32,
319         )
320         # key = hexdec("4b7ae649ca31dd5fe3243a91a5188c03f1d7049bec8e0d241c0e1e8c39ea4c1f")
321         key_enc, key_mac = kdf_tree_gostr3411_2012_256(
322             key, b"kdf tree", ukm[GOST3412Kuznechik.blocksize // 2:], 2,
323         )
324         ciphertext = bytes(shrouded_key_bag["encryptedData"])
325         plaintext = ctr_acpkm(
326             GOST3412Kuznechik,
327             GOST3412Kuznechik(key_enc).encrypt,
328             section_size=256 * 1024,
329             bs=GOST3412Kuznechik.blocksize,
330             data=ciphertext,
331             iv=ukm[:GOST3412Kuznechik.blocksize // 2],
332         )
333         mac_expected = plaintext[-GOST3412Kuznechik.blocksize:]
334         plaintext = plaintext[:-GOST3412Kuznechik.blocksize]
335         mac = omac(
336             GOST3412Kuznechik(key_mac).encrypt,
337             GOST3412Kuznechik.blocksize,
338             plaintext,
339         )
340         self.assertSequenceEqual(mac, mac_expected)
341         self.assertSequenceEqual(plaintext, self.prv_test_raw)
342
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"]),
348             dklen=96,
349         )[-32:]
350         # mac_key = hexdec("a81d1bc91a4a5cf1fd7320f92dda7e5b285816c3b20826a382d7ed0cbf3a9bf4")
351         self.assertSequenceEqual(
352             hmac_new(
353                 key=mac_key,
354                 msg=SafeContents(outer_safe_contents).encode(),
355                 digestmod=GOST34112012512,
356             ).digest(),
357             bytes(mac_data["mac"]["digest"]),
358         )
359         self.assertTrue(gost3410.verify(
360             self.ca_curve,
361             self.ca_pub,
362             GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1],
363             bytes(cert["signatureValue"]),
364         ))
365
366     def test_encrypted_cert_and_key(self):
367         pfx_raw = b64decode(b"""
368             MIIFjAIBAzCCBSUGCSqGSIb3DQEHAaCCBRYEggUSMIIFDjCCA0EGCSqGSIb3DQEH
369             BqCCAzIwggMuAgEAMIIDJwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBIMCkGCSqG
370             SIb3DQEFDDAcBAgUuSVGsSwGjQICCAAwDAYIKoUDBwEBBAIFADAbBgkqhQMHAQEF
371             AQIwDgQM9Hk3dagtS48+G/x+gIICwWGPqxxN+sTrKbruRf9R5Ya9cf5AtO1frqMn
372             f1eULfmZmTg/BdE51QQ+Vbnh3v1kmspr6h2+e4Wli+ndEeCWG6A6X/G22h/RAHW2
373             YrVmf6cCWxW+YrqzT4h/8RQL/9haunD5LmHPLVsYrEai0OwbgXayDSwARVJQLQYq
374             sLNmZK5ViN+fRiS5wszVJ3AtVq8EuPt41aQEKwPy2gmH4S6WmnQRC6W7aoqmIifF
375             PJENJNn5K2M1J6zNESs6bFtYNKMArNqtvv3rioY6eAaaLy6AV6ljsekmqodHmQjv
376             Y4eEioJs0xhpXhZY69PXT+ZBeHv6MSheBhwXqxAd1DqtPTafMjNK8rqKCap9TtPG
377             vONvo5W9dgwegxRRQzlum8dzV4m1W9Aq4W7t8/UcxDWRz3k6ijFPlGaA9+8ZMTEO
378             RHhBRvM6OY2/VNNxbgxWfGYuPxpSi3YnCZIPmBEe5lU/Xv7KjzFusGM38F8YR61k
379             4/QNpKI1QUv714YKfaUQznshGGzILv1NGID62pl1+JI3vuawi2mDMrmkuM9QFU9v
380             /kRP+c2uBHDuOGEUUSNhF08p7+w3vxplatGWXH9fmIsPBdk2f3wkn+rwoqrEuijM
381             I/bCAylU/M0DMKhAo9j31UYSZdi4fsfRWYDJMq/8FPn96tuo+oCpbqv3NUwpZM/8
382             Li4xqgTHtYw/+fRG0/P6XadNEiII/TYjenLfVHXjAHOVJsVeCu/t3EsMYHQddNCh
383             rFk/Ic2PdIQOyB4/enpW0qrKegSbyZNuF1WI4zl4mI89L8dTQBUkhy45yQXZlDD8
384             k1ErYdtdEsPtz/4zuSpbnmwCEIRoOuSXtGuJP+tbcWEXRKM2UBgi3qBjpn7DU18M
385             tsrRM9pDdadl8mT/Vfh9+B8dZBZVxgQu70lMPEGexbUkYHuFCCnyi9J0V92StbIz
386             Elxla1VebjCCAcUGCSqGSIb3DQEHAaCCAbYEggGyMIIBrjCCAaoGCyqGSIb3DQEM
387             CgECoIIBQzCCAT8wVQYJKoZIhvcNAQUNMEgwKQYJKoZIhvcNAQUMMBwECP0EQk0O
388             1twvAgIIADAMBggqhQMHAQEEAgUAMBsGCSqFAwcBAQUBATAOBAzwxSqgAAAAAAAA
389             AAAEgeUqj9mI3RDfK5hMd0EeYws7foZK/5ANr2wUhP5qnDjAZgn76lExJ+wuvlnS
390             9PChfWVugvdl/9XJgQvvr9Cu4pOh4ICXplchcy0dGk/MzItHRVC5wK2nTxwQ4kKT
391             kG9xhLFzoD16dhtqX0+/dQg9G8pE5EzCBIYRXLm1Arcz9k7KVsTJuNMjFrr7EQuu
392             Tr80ATSQOtsq50zpFyrpznVPGCrOdIjpymZxNdvw48bZxqTtRVDxCYATOGqz0pwH
393             ClWULHD9LIajLMB2GhBKyQw6ujIlltJs0T+WNdX/AT2FLi1LFSS3+Cj9MVQwIwYJ
394             KoZIhvcNAQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMC0GCSqGSIb3DQEJFDEg
395             Hh4AcAAxADIARgByAGkAZQBuAGQAbAB5AE4AYQBtAGUwXjBOMAoGCCqFAwcBAQID
396             BEDp4e22JmXdnvR0xA99yQuzQuJ8pxBeOpsLm2dZQqt3Fje5zqW1uk/7VOcfV5r2
397             bKm8nsLOs2rPT8hBOoeAZvOIBAjGIUHw6IjG2QICCAA=
398         """)
399         pfx = PFX().decod(pfx_raw)
400         _, outer_safe_contents = pfx["authSafe"]["content"].defined
401
402         encrypted_data = EncryptedData().decod(bytes(
403             outer_safe_contents[0]["bagValue"]
404         ))
405         eci = encrypted_data["encryptedContentInfo"]
406         self.assertEqual(eci["contentEncryptionAlgorithm"]["algorithm"], id_pbes2)
407         pbes2_params = PBES2Params().decod(bytes(
408             eci["contentEncryptionAlgorithm"]["parameters"]
409         ))
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"]),
417             dklen=32,
418         )
419         # key = hexdec("d066a96fb326ba896a2352d3f40240a4ded6e7e7bd5b4db6b5241d631c8c381c")
420         key_enc, key_mac = kdf_tree_gostr3411_2012_256(
421             key, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2,
422         )
423         ciphertext = bytes(eci["encryptedContent"])
424         plaintext = ctr_acpkm(
425             GOST3412Magma,
426             GOST3412Magma(key_enc).encrypt,
427             section_size=8 * 1024,
428             bs=GOST3412Magma.blocksize,
429             data=ciphertext,
430             iv=ukm[:GOST3412Magma.blocksize // 2],
431         )
432         mac_expected = plaintext[-GOST3412Magma.blocksize:]
433         plaintext = plaintext[:-GOST3412Magma.blocksize]
434         mac = omac(
435             GOST3412Magma(key_mac).encrypt,
436             GOST3412Magma.blocksize,
437             plaintext,
438         )
439         self.assertSequenceEqual(mac, mac_expected)
440
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)
448
449         safe_contents = OctetStringSafeContents().decod(bytes(
450             outer_safe_contents[1]["bagValue"]
451         ))
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"]),
463             dklen=32,
464         )
465         # key = hexdec("f840d001fd11441e0fb7ccf48f471915e5bf35275309dbe7ade9da4fe460ba7e")
466         ciphertext = bytes(shrouded_key_bag["encryptedData"])
467         plaintext = ctr_acpkm(
468             GOST3412Magma,
469             GOST3412Magma(key).encrypt,
470             section_size=8 * 1024,
471             bs=GOST3412Magma.blocksize,
472             data=ciphertext,
473             iv=ukm[:GOST3412Magma.blocksize // 2],
474         )
475         self.assertSequenceEqual(plaintext, self.prv_test_raw)
476
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"]),
482             dklen=96,
483         )[-32:]
484         # mac_key = hexdec("084f81782af1534ffd67e3c579c14cb45d7a6f659f46fdbb51a552e874e66fb2")
485         self.assertSequenceEqual(
486             hmac_new(
487                 key=mac_key,
488                 msg=SafeContents(outer_safe_contents).encode(),
489                 digestmod=GOST34112012512,
490             ).digest(),
491             bytes(mac_data["mac"]["digest"]),
492         )
493
494     def test_dh(self):
495         curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"]
496         # sender_prv_raw = hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8")
497         sender_cert = Certificate().decod(b64decode("""
498             MIIB6zCCAZigAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
499             cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
500             MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYDVQQDEy
501             FPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMHAQEBATAL
502             BgkqhQMHAQIBAQEDQwAEQJYpDRNiWWqDgaZje0EmLLOldQ35o5X1ZuZNSKequYQc/s
503             oI3OgDMWD7ThJJCk01IelCeb6MsBmG4lol+pnpVtOjgYcwgYQwYwYDVR0jBFwwWoAU
504             rGwOTERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBA
505             MTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQU
506             Px5RgcjkifhlJm4/jQdkbm30rVQwCgYIKoUDBwEBAwIDQQA68x7Vk6PvP/8xOGHhf8
507             PuqaXAYskSyJPuBu+3Bo/PEj10devwc1J9uYWIDCGdKKPybSlnQHqUPBBPM30YX1YN
508         """))
509         recipient_prv_raw = hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1]
510         recipient_prv = gost3410.prv_unmarshal(recipient_prv_raw)
511         recipient_cert = Certificate().decod(b64decode("""
512             MIIB6jCCAZegAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
513             cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
514             MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYDVQQDEy
515             BSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBeMBcGCCqFAwcBAQEBMAsG
516             CSqFAwcBAgEBAQNDAARAvyeCGXMsYwpYe5aE0w8w3m4vpKQapGInqpnFlv7h08psFP
517             0s1W80q3BR534F4TmR+o5+iU+AW6ycvWuc73JEQ6OBhzCBhDBjBgNVHSMEXDBagBSs
518             bA5MRGaiQpbinwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAx
519             MeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBQ3
520             5gHPN1bx8l2eEMTbrtIg+5MU0TAKBggqhQMHAQEDAgNBABF2RHDaRqQuBS2yu7yGIG
521             FgA6c/LG4GKjSOwYsRVmXJNNkQ4TB7PB8j3q7gx2koPsVBm90WfMWSL6SNSh3muuM=
522         """))
523         self.assertTrue(gost3410.verify(
524             self.ca_curve,
525             self.ca_pub,
526             GOST34112012256(sender_cert["tbsCertificate"].encode()).digest()[::-1],
527             bytes(sender_cert["signatureValue"]),
528         ))
529         self.assertTrue(gost3410.verify(
530             self.ca_curve,
531             self.ca_pub,
532             GOST34112012256(recipient_cert["tbsCertificate"].encode()).digest()[::-1],
533             bytes(recipient_cert["signatureValue"]),
534         ))
535
536         pfx_raw = b64decode("""
537             MIIKZwIBAzCCCmAGCSqGSIb3DQEHAqCCClEwggpNAgEBMQwwCgYIKoUDBwEBAgIw
538             ggcrBgkqhkiG9w0BBwGgggccBIIHGDCCBxQwggKdBgkqhkiG9w0BBwGgggKOBIIC
539             ijCCAoYwggKCBgsqhkiG9w0BDAoBA6CCAkowggJGBgoqhkiG9w0BCRYBoIICNgSC
540             AjIwggIuMIIB26ADAgECAgQBjLqEMAoGCCqFAwcBAQMCMDgxDTALBgNVBAoTBFRL
541             MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDAeFw0w
542             MTAxMDEwMDAwMDBaFw00OTEyMzEwMDAwMDBaMDsxDTALBgNVBAoTBFRLMjYxKjAo
543             BgNVBAMTIU9SSUdJTkFUT1I6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBoDAXBggq
544             hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAtIu3WrwpDhhlXGKhT7UtX1CETswd
545             H2AESHtLXJU0aWq3v6s0blUWqas8zvittSw6WFXwz7Nkqmtd2Tfk7PyVJb+faghQ
546             dnGKRcgf9JIePiu/cr8+6/PuFhNBJmX/E92nvydSaOsRrp3nB9fxuITLbPR2C58W
547             8CQzDVRriB1eoM6jgYcwgYQwYwYDVR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88uk
548             UyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
549             MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUfgZXCZgMrWsIqFfueQBY
550             OsnXoKQwCgYIKoUDBwEBAwIDQQAKXqnx0BumL0eT7eaAzIjRYiHXsiuWtKn+YHQX
551             tnMy3xdQPUPDPcmuufF5ed8y84DkF1Qn2ELIOAxUAaz8hwQQMSUwIwYJKoZIhvcN
552             AQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMIIEbwYJKoZIhvcNAQcDoIIEYDCC
553             BFwCAQKgggH7oIIB9zCCAfMwggGgoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODEN
554             MAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAy
555             NTYtYml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UE
556             ChMEVEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYt
557             Yml0MGgwIQYIKoUDBwEBAQEwFQYJKoUDBwECAQEBBggqhQMHAQECAgNDAARAlikN
558             E2JZaoOBpmN7QSYss6V1DfmjlfVm5k1Ip6q5hBz+ygjc6AMxYPtOEkkKTTUh6UJ5
559             voywGYbiWiX6melW06OBhTCBgjBhBgNVHQEEWjBYgBSA2Qz3mfhmTZNTiY7AnnEt
560             p6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
561             MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQU0ZwoR0lm0GMJyQutp9s7
562             uTY3dN8wCgYIKoUDBwEBAwIDQQAeNaIo2l0hJ+fJe/Mtq4cWN+f5ShKuF1me9Bbb
563             DZVcVgE8s4DuVpYsJ7dTuBqGbMcfK+k/4u1RuuVDZkJcHTikMYH/oYH8AgEDoEIw
564             QDA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEw
565             LTEyIDI1Ni1iaXQCBAGMuoKhIgQgVyXLHEfPiRZOVrAtgmddYSS+MjuKuWWA2fC7
566             TlhQ7/wwFwYJKoUDBwEBBwIBMAoGCCqFAwcBAQYBMHYwdDBAMDgxDTALBgNVBAoT
567             BFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdAIE
568             AYy6gwQwSqUJbuUEakskO0Ks2l4YIRo3aUnzluRiy17S7jXmupoIYWUMo44iYXs3
569             05wL+iU1MIIBVAYJKoZIhvcNAQcBMB8GCSqFAwcBAQUCATASBBCdur5wO9c8vgAA
570             AAAAAAAAgIIBJLIXYiLziJdz/VDbl+tEd3lItfQWvLy+P1my1ZRZ3FFnQvuDHHo/
571             i9pB2/8xaaWJhhfXMORJR7DdvwAMvzcwwgX5KIbbjrrK1EFADult5D2MMiRNBa9d
572             jM6w+pgKsZhi+id+v+Hx83xGimM5qUf/oe40TolQKM6uMq9XG/efRYKJ67Bht4s6
573             bKKy2+Uswv91m5uNNyrjSsA5UAW5PSMKovjwnVC/wIWs7Zlk8SVzlK4bdUppJF6F
574             Hca1knFlzvyi5mRnoIqcVe11bDM7GROSBtp4Po23+GGSGIBCMgP2I2ePoarQNYG3
575             jY5W6zoxGuH+xA8D+XrCbWJToNHekfYUlXkGSkEnHc5ZywK3tvvIBXu6z0ebvEKP
576             3VVkBvu1rhZY8/DBXaegggH3MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQED
577             AjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEw
578             LTEyIDI1Ni1iaXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0w
579             CwYDVQQKEwRUSzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEy
580             IDI1Ni1iaXQwaDAhBggqhQMHAQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MA
581             BECWKQ0TYllqg4GmY3tBJiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpN
582             NSHpQnm+jLAZhuJaJfqZ6VbTo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJ
583             jsCecS2npzESoTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjog
584             R09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJ
585             C62n2zu5Njd03zAKBggqhQMHAQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4X
586             WZ70FtsNlVxWATyzgO5Wliwnt1O4GoZsxx8r6T/i7VG65UNmQlwdOKQxggEOMIIB
587             CgIBATBAMDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
588             MzQuMTAtMTIgMjU2LWJpdAIEAYy6gjAKBggqhQMHAQECAqBpMBgGCSqGSIb3DQEJ
589             AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIwMTIyODIxNTcxNVowLwYJ
590             KoZIhvcNAQkEMSIEIDpxzO/5T2vf3BSYXhSaNCL9kMTRIVG6UVv0h1sRa+6tMAoG
591             CCqFAwcBAQEBBEA9mo045ap4k03ZdSacyoZlbvSqNZMHsGUciqE7aWGc5h7U23H8
592             e6qgRVHn9b+Mq3sp57LxHk4Sny0zV8TRwCr8
593         """)
594         pfx = PFX().decod(pfx_raw)
595         self.assertEqual(pfx["authSafe"]["contentType"], id_signedData)
596
597         sd = SignedData().decod(bytes(pfx["authSafe"]["content"]))
598         # self.assertEqual(sd["certificates"][0]["certificate"], sender_cert)
599         si = sd["signerInfos"][0]
600         self.assertEqual(
601             si["digestAlgorithm"]["algorithm"],
602             id_tc26_gost3411_2012_256,
603         )
604         digest = [
605             bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"]
606             if attr["attrType"] == id_messageDigest
607         ][0]
608         sender_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
609             sender_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
610         ))))
611         content = bytes(sd["encapContentInfo"]["eContent"])
612         self.assertSequenceEqual(digest, GOST34112012256(content).digest())
613         self.assertTrue(gost3410.verify(
614             curve,
615             sender_pub,
616             GOST34112012256(
617                 SignedAttributes(si["signedAttrs"]).encode()
618             ).digest()[::-1],
619             bytes(si["signature"]),
620         ))
621
622         outer_safe_contents = SafeContents().decod(content)
623
624         safe_bag = outer_safe_contents[0]
625         self.assertEqual(safe_bag["bagId"], id_data)
626         safe_contents = OctetStringSafeContents().decod(bytes(safe_bag["bagValue"]))
627         safe_bag = safe_contents[0]
628         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
629         cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
630         self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
631         _, cert = cert_bag["certValue"].defined
632         self.assertEqual(Certificate(cert), self.cert_test)
633
634         safe_bag = outer_safe_contents[1]
635         self.assertEqual(safe_bag["bagId"], id_envelopedData)
636         ed = EnvelopedData().decod(bytes(safe_bag["bagValue"]))
637         kari = ed["recipientInfos"][0]["kari"]
638         ukm = bytes(kari["ukm"])
639         self.assertEqual(
640             kari["keyEncryptionAlgorithm"]["algorithm"],
641             id_gostr3412_2015_kuznyechik_wrap_kexp15,
642         )
643         self.assertEqual(
644             kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"],
645             id_tc26_agreement_gost3410_2012_256,
646         )
647         kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"])
648         keymat = keg(curve, recipient_prv, sender_pub, ukm)
649         kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:]
650         cek = kimp15(
651             GOST3412Kuznechik(kek).encrypt,
652             GOST3412Kuznechik(kim).encrypt,
653             GOST3412Kuznechik.blocksize,
654             kexp,
655             ukm[24:24 + GOST3412Kuznechik.blocksize // 2],
656         )
657         eci = ed["encryptedContentInfo"]
658         self.assertEqual(
659             eci["contentEncryptionAlgorithm"]["algorithm"],
660             id_gostr3412_2015_kuznyechik_ctracpkm,
661         )
662         eci_ukm = bytes(
663             eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"]
664         )
665         content = ctr_acpkm(
666             GOST3412Kuznechik,
667             GOST3412Kuznechik(cek).encrypt,
668             256 * 1024,
669             GOST3412Kuznechik.blocksize,
670             bytes(eci["encryptedContent"]),
671             eci_ukm[:GOST3412Kuznechik.blocksize // 2],
672         )
673
674         safe_contents = SafeContents().decod(content)
675         safe_bag = safe_contents[0]
676         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_keyBag)
677         KeyBag().decod(bytes(safe_bag["bagValue"]))
678         self.assertSequenceEqual(bytes(safe_bag["bagValue"]), self.prv_test_raw)