]> Cypherpunks.ru repositories - pygost.git/blob - pygost/test_pfx.py
Fixed typo in parameter
[pygost.git] / pygost / test_pfx.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2022 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 = 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==
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             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
508             bSlnQHqUPBBPM30YX1YN
509         """))
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
523             m90WfMWSL6SNSh3muuM=
524         """))
525         self.assertTrue(gost3410.verify(
526             self.ca_curve,
527             self.ca_pub,
528             GOST34112012256(sender_cert["tbsCertificate"].encode()).digest()[::-1],
529             bytes(sender_cert["signatureValue"]),
530         ))
531         self.assertTrue(gost3410.verify(
532             self.ca_curve,
533             self.ca_pub,
534             GOST34112012256(recipient_cert["tbsCertificate"].encode()).digest()[::-1],
535             bytes(recipient_cert["signatureValue"]),
536         ))
537
538         pfx_raw = b64decode("""
539             MIIKVwIBAzCCClAGCSqGSIb3DQEHAqCCCkEwggo9AgEBMQwwCgYIKoUDBwEBAgIw
540             ggcjBgkqhkiG9w0BBwGgggcUBIIHEDCCBwwwggKdBgkqhkiG9w0BBwGgggKOBIIC
541             ijCCAoYwggKCBgsqhkiG9w0BDAoBA6CCAkowggJGBgoqhkiG9w0BCRYBoIICNgSC
542             AjIwggIuMIIB26ADAgECAgQBjLqEMAoGCCqFAwcBAQMCMDgxDTALBgNVBAoTBFRL
543             MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDAeFw0w
544             MTAxMDEwMDAwMDBaFw00OTEyMzEwMDAwMDBaMDsxDTALBgNVBAoTBFRLMjYxKjAo
545             BgNVBAMTIU9SSUdJTkFUT1I6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBoDAXBggq
546             hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAtIu3WrwpDhhlXGKhT7UtX1CETswd
547             H2AESHtLXJU0aWq3v6s0blUWqas8zvittSw6WFXwz7Nkqmtd2Tfk7PyVJb+faghQ
548             dnGKRcgf9JIePiu/cr8+6/PuFhNBJmX/E92nvydSaOsRrp3nB9fxuITLbPR2C58W
549             8CQzDVRriB1eoM6jgYcwgYQwYwYDVR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88uk
550             UyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
551             MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUfgZXCZgMrWsIqFfueQBY
552             OsnXoKQwCgYIKoUDBwEBAwIDQQAKXqnx0BumL0eT7eaAzIjRYiHXsiuWtKn+YHQX
553             tnMy3xdQPUPDPcmuufF5ed8y84DkF1Qn2ELIOAxUAaz8hwQQMSUwIwYJKoZIhvcN
554             AQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMIIEZwYJKoZIhvcNAQcDoIIEWDCC
555             BFQCAQKgggHzoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODEN
556             MAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAy
557             NTYtYml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UE
558             ChMEVEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYt
559             Yml0MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tB
560             JiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ
561             6VbTo4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4
562             MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEy
563             IDI1Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoG
564             CCqFAwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr
565             8HNSfbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGB/6GB/AIBA6BCMEAwODENMAsG
566             A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt
567             Yml0AgQBjLqCoSIEIBt4fjey+k8C1D3OaMca8wl6h3j3C6OAbrx8rmxXktsQMBcG
568             CSqFAwcBAQcCATAKBggqhQMHAQEGATB2MHQwQDA4MQ0wCwYDVQQKEwRUSzI2MScw
569             JQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQCBAGMuoMEMJkp
570             Wae6IVfaY3mP0izRY7ifc41fATXdJ2tmTl+1vitkSE2vLCKXDLl90KfHA6gNmDCC
571             AVQGCSqGSIb3DQEHATAfBgkqhQMHAQEFAgEwEgQQFhEshEBO2LkAAAAAAAAAAICC
572             ASQYvLpT/8azEXJfekyGuyvE9UkVX+Ao8sfu9My/c4WAVRNMhZkCqD+BbPwBsIzN
573             sXZIi9rXGAfsPz7xaO9EUFZPjNOWtF/E01oJgG+gYLFn7qAiEFcmRLptSHuanNHn
574             7Yol6IHushX4UaW9hEa/L6eFQx/hoDhrNZnWTXNZtNuHuMGC9dzhHhTxfkdjZYXD
575             v+M7psVj58JutE3U2d4pgxKcBPdMO4vl4+27cIKxQZFZU2zuCVJLYLqmPT5pCBkM
576             mJqy7bZwHOJ9kBq/TGUf8iJGYSCNre3RTNLbcTTk7rZrbiMkFsG3borzenpouS5E
577             BcCkBt8Mj0nvsMCu9ipHTuWww7LltlkXCjlNXFUi6ZI3VyHW5CDpghujQWiZxiAc
578             JuGl6GwZoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODENMAsG
579             A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt
580             Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME
581             VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYtYml0
582             MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tBJiyz
583             pXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ6VbT
584             o4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0w
585             CwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1
586             Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoGCCqF
587             AwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr8HNS
588             fbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGCAQ4wggEKAgEBMEAwODENMAsGA1UE
589             ChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0
590             AgQBjLqCMAoGCCqFAwcBAQICoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
591             BgkqhkiG9w0BCQUxDxcNMjEwNDE0MTkyMTEyWjAvBgkqhkiG9w0BCQQxIgQg1XOA
592             zNa710QuXsn5+yIf3cNTiFOQMgTiBRJBz8Tr4I0wCgYIKoUDBwEBAQEEQALINal9
593             7wHXYiG+w0yzSkKOs0jRZew0S73r/cfk/sUoM3HKKIEbKruvlAdiOqX/HLFSEx/s
594             kxFG6QUFH8uuoX8=
595         """)
596         pfx = PFX().decod(pfx_raw)
597         self.assertEqual(pfx["authSafe"]["contentType"], id_signedData)
598
599         sd = SignedData().decod(bytes(pfx["authSafe"]["content"]))
600         self.assertEqual(sd["certificates"][0]["certificate"], sender_cert)
601         si = sd["signerInfos"][0]
602         self.assertEqual(
603             si["digestAlgorithm"]["algorithm"],
604             id_tc26_gost3411_2012_256,
605         )
606         digest = [
607             bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"]
608             if attr["attrType"] == id_messageDigest
609         ][0]
610         sender_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
611             sender_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
612         ))))
613         content = bytes(sd["encapContentInfo"]["eContent"])
614         self.assertSequenceEqual(digest, GOST34112012256(content).digest())
615         self.assertTrue(gost3410.verify(
616             curve,
617             sender_pub,
618             GOST34112012256(
619                 SignedAttributes(si["signedAttrs"]).encode()
620             ).digest()[::-1],
621             bytes(si["signature"]),
622         ))
623
624         outer_safe_contents = SafeContents().decod(content)
625
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)
635
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"])
641         self.assertEqual(
642             kari["keyEncryptionAlgorithm"]["algorithm"],
643             id_gostr3412_2015_kuznyechik_wrap_kexp15,
644         )
645         self.assertEqual(
646             kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"],
647             id_tc26_agreement_gost3410_2012_256,
648         )
649         kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"])
650         keymat = keg(curve, recipient_prv, sender_pub, ukm)
651         kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:]
652         cek = kimp15(
653             GOST3412Kuznechik(kek).encrypt,
654             GOST3412Kuznechik(kim).encrypt,
655             GOST3412Kuznechik.blocksize,
656             kexp,
657             ukm[24:24 + GOST3412Kuznechik.blocksize // 2],
658         )
659         eci = ed["encryptedContentInfo"]
660         self.assertEqual(
661             eci["contentEncryptionAlgorithm"]["algorithm"],
662             id_gostr3412_2015_kuznyechik_ctracpkm,
663         )
664         eci_ukm = bytes(
665             eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"]
666         )
667         content = ctr_acpkm(
668             GOST3412Kuznechik,
669             GOST3412Kuznechik(cek).encrypt,
670             256 * 1024,
671             GOST3412Kuznechik.blocksize,
672             bytes(eci["encryptedContent"]),
673             eci_ukm[:GOST3412Kuznechik.blocksize // 2],
674         )
675
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)