681 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			681 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # coding: utf-8
 | |
| # PyGOST -- Pure Python GOST cryptographic functions library
 | |
| # Copyright (C) 2015-2023 Sergey Matveev <stargrave@stargrave.org>
 | |
| #
 | |
| # This program is free software: you can redistribute it and/or modify
 | |
| # it under the terms of the GNU General Public License as published by
 | |
| # the Free Software Foundation, version 3 of the License.
 | |
| #
 | |
| # This program is distributed in the hope that it will be useful,
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| # GNU General Public License for more details.
 | |
| #
 | |
| # You should have received a copy of the GNU General Public License
 | |
| # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| from base64 import b64decode
 | |
| from hmac import new as hmac_new
 | |
| from unittest import skipIf
 | |
| from unittest import TestCase
 | |
| 
 | |
| from pygost import gost3410
 | |
| from pygost.gost28147 import cfb_decrypt
 | |
| from pygost.gost34112012256 import GOST34112012256
 | |
| from pygost.gost34112012512 import GOST34112012512
 | |
| from pygost.gost34112012512 import pbkdf2 as gost34112012_pbkdf2
 | |
| from pygost.gost3412 import GOST3412Kuznechik
 | |
| from pygost.gost3412 import GOST3412Magma
 | |
| from pygost.gost3412 import KEYSIZE
 | |
| from pygost.gost3413 import ctr_acpkm
 | |
| from pygost.gost3413 import mac as omac
 | |
| from pygost.kdf import kdf_tree_gostr3411_2012_256
 | |
| from pygost.kdf import keg
 | |
| from pygost.utils import hexdec
 | |
| from pygost.wrap import kimp15
 | |
| 
 | |
| 
 | |
| try:
 | |
|     from pyderasn import OctetString
 | |
| 
 | |
|     from pygost.asn1schemas.cms import EncryptedData
 | |
|     from pygost.asn1schemas.cms import EnvelopedData
 | |
|     from pygost.asn1schemas.cms import SignedAttributes
 | |
|     from pygost.asn1schemas.cms import SignedData
 | |
|     from pygost.asn1schemas.oids import id_data
 | |
|     from pygost.asn1schemas.oids import id_envelopedData
 | |
|     from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm
 | |
|     from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15
 | |
|     from pygost.asn1schemas.oids import id_messageDigest
 | |
|     from pygost.asn1schemas.oids import id_pbes2
 | |
|     from pygost.asn1schemas.oids import id_pkcs12_bagtypes_certBag
 | |
|     from pygost.asn1schemas.oids import id_pkcs12_bagtypes_keyBag
 | |
|     from pygost.asn1schemas.oids import id_pkcs12_bagtypes_pkcs8ShroudedKeyBag
 | |
|     from pygost.asn1schemas.oids import id_pkcs9_certTypes_x509Certificate
 | |
|     from pygost.asn1schemas.oids import id_signedData
 | |
|     from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_256
 | |
|     from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256
 | |
|     from pygost.asn1schemas.pfx import CertBag
 | |
|     from pygost.asn1schemas.pfx import KeyBag
 | |
|     from pygost.asn1schemas.pfx import OctetStringSafeContents
 | |
|     from pygost.asn1schemas.pfx import PBES2Params
 | |
|     from pygost.asn1schemas.pfx import PFX
 | |
|     from pygost.asn1schemas.pfx import PKCS8ShroudedKeyBag
 | |
|     from pygost.asn1schemas.pfx import SafeContents
 | |
|     from pygost.asn1schemas.x509 import Certificate
 | |
| except ImportError:
 | |
|     pyderasn_exists = False
 | |
| else:
 | |
|     pyderasn_exists = True
 | |
| 
 | |
| 
 | |
| @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
 | |
| class TestPFX(TestCase):
 | |
|     """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf)
 | |
|     """
 | |
|     pfx_raw = b64decode("""
 | |
| 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
 | |
| mwcl1xQV2G5/fgICB9A=
 | |
|     """)
 | |
|     password = u"Пароль для PFX"
 | |
| 
 | |
|     def test_shrouded_key_bag(self):
 | |
|         private_key_info_expected = b64decode(b"""
 | |
| MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG
 | |
| G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY=
 | |
|         """)
 | |
| 
 | |
|         pfx, tail = PFX().decode(self.pfx_raw)
 | |
|         self.assertSequenceEqual(tail, b"")
 | |
|         _, outer_safe_contents = pfx["authSafe"]["content"].defined
 | |
|         safe_contents, tail = OctetStringSafeContents().decode(
 | |
|             bytes(outer_safe_contents[0]["bagValue"]),
 | |
|         )
 | |
|         self.assertSequenceEqual(tail, b"")
 | |
|         safe_bag = safe_contents[0]
 | |
|         shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode(
 | |
|             bytes(safe_bag["bagValue"]),
 | |
|         )
 | |
|         self.assertSequenceEqual(tail, b"")
 | |
|         _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
 | |
|         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
 | |
|         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
 | |
| 
 | |
|         key = gost34112012_pbkdf2(
 | |
|             password=self.password.encode("utf-8"),
 | |
|             salt=bytes(pbkdf2_params["salt"]["specified"]),
 | |
|             iterations=int(pbkdf2_params["iterationCount"]),
 | |
|             dklen=32,
 | |
|         )
 | |
|         # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8")
 | |
|         self.assertSequenceEqual(
 | |
|             cfb_decrypt(
 | |
|                 key,
 | |
|                 bytes(shrouded_key_bag["encryptedData"]),
 | |
|                 iv=bytes(enc_scheme_params["iv"]),
 | |
|                 sbox="id-tc26-gost-28147-param-Z",
 | |
|             ),
 | |
|             private_key_info_expected,
 | |
|         )
 | |
| 
 | |
|     def test_encrypted_data(self):
 | |
|         cert_bag_expected = b64decode(b"""
 | |
| MIIDSjCCA0YGCyqGSIb3DQEMCgEDoIIDHjCCAxoGCiqGSIb3DQEJFgGgggMKBIIDBjCCAwIwggKt
 | |
| oAMCAQICEAHQaF8xH5bAAAAACycJAAEwDAYIKoUDBwEBAwIFADBgMQswCQYDVQQGEwJSVTEVMBMG
 | |
| A1UEBwwM0JzQvtGB0LrQstCwMQ8wDQYDVQQKDAbQotCaMjYxKTAnBgNVBAMMIENBIGNlcnRpZmlj
 | |
| YXRlIChQS0NTIzEyIGV4YW1wbGUpMB4XDTE1MDMyNzA3MjUwMFoXDTIwMDMyNzA3MjMwMFowZDEL
 | |
| MAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MS0wKwYD
 | |
| VQQDDCRUZXN0IGNlcnRpZmljYXRlIDEgKFBLQ1MjMTIgZXhhbXBsZSkwZjAfBggqhQMHAQEBATAT
 | |
| BgcqhQMCAiMBBggqhQMHAQECAgNDAARA1xzymkpvr2dYJT8WTOX3Dt96/+hGsXNytUQpkWB5ImJM
 | |
| 4tg9AsC4RIUwV5H41MhG0uBRFweTzN6AsAdBvhTClYEJADI3MDkwMDAxo4IBKTCCASUwKwYDVR0Q
 | |
| BCQwIoAPMjAxNTAzMjcwNzI1MDBagQ8yMDE2MDMyNzA3MjUwMFowDgYDVR0PAQH/BAQDAgTwMB0G
 | |
| A1UdDgQWBBQhWOsRQ68yYN2Utg/owHoWcqsVbTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
 | |
| AwQwDAYDVR0TAQH/BAIwADCBmQYDVR0jBIGRMIGOgBQmnc7Xh5ykb5t/BMwOkxA4drfEmqFkpGIw
 | |
| YDELMAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MSkw
 | |
| JwYDVQQDDCBDQSBjZXJ0aWZpY2F0ZSAoUEtDUyMxMiBleGFtcGxlKYIQAdBoXvL8TSAAAAALJwkA
 | |
| ATAMBggqhQMHAQEDAgUAA0EA9oq0Vvk8kkgIwkp0x0J5eKtia4MNTiwKAm7jgnCZIx3O98BThaTX
 | |
| 3ZQhEo2RL9pTCPr6wFMheeJ+YdGMReXvsjEVMBMGCSqGSIb3DQEJFTEGBAQBAAAA
 | |
|         """)
 | |
| 
 | |
|         pfx, tail = PFX().decode(self.pfx_raw)
 | |
|         self.assertSequenceEqual(tail, b"")
 | |
|         _, outer_safe_contents = pfx["authSafe"]["content"].defined
 | |
|         _, encrypted_data = outer_safe_contents[1]["bagValue"].defined
 | |
|         _, pbes2_params = encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"].defined
 | |
|         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
 | |
|         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
 | |
|         key = gost34112012_pbkdf2(
 | |
|             password=self.password.encode("utf-8"),
 | |
|             salt=bytes(pbkdf2_params["salt"]["specified"]),
 | |
|             iterations=int(pbkdf2_params["iterationCount"]),
 | |
|             dklen=32,
 | |
|         )
 | |
|         # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc")
 | |
|         self.assertSequenceEqual(
 | |
|             cfb_decrypt(
 | |
|                 key,
 | |
|                 bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]),
 | |
|                 iv=bytes(enc_scheme_params["iv"]),
 | |
|                 sbox="id-tc26-gost-28147-param-Z",
 | |
|             ),
 | |
|             cert_bag_expected,
 | |
|         )
 | |
| 
 | |
|     def test_mac(self):
 | |
|         pfx, tail = PFX().decode(self.pfx_raw)
 | |
|         self.assertSequenceEqual(tail, b"")
 | |
|         _, outer_safe_contents = pfx["authSafe"]["content"].defined
 | |
|         mac_data = pfx["macData"]
 | |
|         mac_key = gost34112012_pbkdf2(
 | |
|             password=self.password.encode("utf-8"),
 | |
|             salt=bytes(mac_data["macSalt"]),
 | |
|             iterations=int(mac_data["iterations"]),
 | |
|             dklen=96,
 | |
|         )[-32:]
 | |
|         # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5")
 | |
|         self.assertSequenceEqual(
 | |
|             hmac_new(
 | |
|                 key=mac_key,
 | |
|                 msg=SafeContents(outer_safe_contents).encode(),
 | |
|                 digestmod=GOST34112012512,
 | |
|             ).digest(),
 | |
|             bytes(mac_data["mac"]["digest"]),
 | |
|         )
 | |
| 
 | |
| 
 | |
| @skipIf(not pyderasn_exists, "PyDERASN dependency is required")
 | |
| class TestPFX2020(TestCase):
 | |
|     """PFX test vectors from newer PKCS#12 update
 | |
|     """
 | |
|     ca_prv_raw = hexdec("092F8D059E97E22B90B1AE99F0087FC4D26620B91550CBB437C191005A290810")
 | |
|     ca_curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"]
 | |
|     ca_cert = Certificate().decod(b64decode(b"""
 | |
|         MIIB+TCCAaagAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
 | |
|         cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
 | |
|         MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx
 | |
|         5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMHAQEBATALBgkq
 | |
|         hQMHAQIBAQEDQwAEQBpKgpyPDnhQAJyLqy8Qs0XQhgxEhby6tSypqYimgbjpcKqtU6
 | |
|         4jpDXc3h3BxGxtl2oHJ/4YLZ/ll87dto3ltMqjgZgwgZUwYwYDVR0jBFwwWoAUrGwO
 | |
|         TERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHk
 | |
|         NBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUrGwO
 | |
|         TERmokKW4p8JOyVm88ukUyowDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNBAB
 | |
|         Gg3nhgQ5oCKbqlEdVaRxH+1WX4wVkawGXuTYkr1AC2OWw3ZC14Vvg3nazm8UMWUZtk
 | |
|         vu1kJcHQ4jFKkjUeg2E=
 | |
|     """))
 | |
|     ca_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
 | |
|         ca_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
 | |
|     ))))
 | |
|     password = u"Пароль для PFX".encode("utf-8")
 | |
|     cert_test = Certificate().decod(b64decode(b"""
 | |
|         MIICLjCCAdugAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS
 | |
|         cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx
 | |
|         MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYDVQQDEy
 | |
|         FPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaAwFwYIKoUDBwEBAQIw
 | |
|         CwYJKoUDBwECAQIBA4GEAASBgLSLt1q8KQ4YZVxioU+1LV9QhE7MHR9gBEh7S1yVNG
 | |
|         lqt7+rNG5VFqmrPM74rbUsOlhV8M+zZKprXdk35Oz8lSW/n2oIUHZxikXIH/SSHj4r
 | |
|         v3K/Puvz7hYTQSZl/xPdp78nUmjrEa6d5wfX8biEy2z0dgufFvAkMw1Ua4gdXqDOo4
 | |
|         GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0wCwYD
 | |
|         VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaX
 | |
|         SCBAGMuoEwHQYDVR0OBBYEFH4GVwmYDK1rCKhX7nkAWDrJ16CkMAoGCCqFAwcBAQMC
 | |
|         A0EACl6p8dAbpi9Hk+3mgMyI0WIh17IrlrSp/mB0F7ZzMt8XUD1Dwz3JrrnxeXnfMv
 | |
|         OA5BdUJ9hCyDgMVAGs/IcEEA==
 | |
|     """))
 | |
|     prv_test_raw = b64decode("""
 | |
|         MIHiAgEBMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQRAEWkl+eblsHWs86SNgRKq
 | |
|         SxMOgGhbvR/uZ5/WWfdNG1axvUwVhpcXIxDZUmzQuNzqJBkseI7f5/JjXyTFRF1a
 | |
|         +YGBgQG0i7davCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO
 | |
|         +K21LDpYVfDPs2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0Em
 | |
|         Zf8T3ae/J1Jo6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzg==
 | |
|     """)
 | |
| 
 | |
|     def test_cert_and_encrypted_key(self):
 | |
|         pfx_raw = b64decode(b"""
 | |
|             MIIFKwIBAzCCBMQGCSqGSIb3DQEHAaCCBLUEggSxMIIErTCCAswGCSqGSIb3DQEH
 | |
|             AaCCAr0EggK5MIICtTCCArEGCyqGSIb3DQEMCgEDoIICSjCCAkYGCiqGSIb3DQEJ
 | |
|             FgGgggI2BIICMjCCAi4wggHboAMCAQICBAGMuoQwCgYIKoUDBwEBAwIwODENMAsG
 | |
|             A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt
 | |
|             Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME
 | |
|             VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiA1MTItYml0
 | |
|             MIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQOBhAAEgYC0i7davCkOGGVcYqFP
 | |
|             tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts
 | |
|             /JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4
 | |
|             hMts9HYLnxbwJDMNVGuIHV6gzqOBhzCBhDBjBgNVHSMEXDBagBSsbA5MRGaiQpbi
 | |
|             nwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsy
 | |
|             NjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBR+BlcJmAyt
 | |
|             awioV+55AFg6ydegpDAKBggqhQMHAQEDAgNBAApeqfHQG6YvR5Pt5oDMiNFiIdey
 | |
|             K5a0qf5gdBe2czLfF1A9Q8M9ya658Xl53zLzgOQXVCfYQsg4DFQBrPyHBBAxVDAj
 | |
|             BgkqhkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkU
 | |
|             MSAeHgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTCCAdkGCSqGSIb3DQEH
 | |
|             AaCCAcoEggHGMIIBwjCCAb4GCyqGSIb3DQEMCgECoIIBVzCCAVMwWQYJKoZIhvcN
 | |
|             AQUNMEwwKQYJKoZIhvcNAQUMMBwECKf4N7NMwugqAgIIADAMBggqhQMHAQEEAgUA
 | |
|             MB8GCSqFAwcBAQUCAjASBBAlmt2WDfaPJlsAs0mLKglzBIH1DMvEacbbWRNDVSnX
 | |
|             JLWygYrKoipdOjDA/2HEnBZ34uFOLNheUqiKpCPoFpbR2GBiVYVTVK9ibiczgaca
 | |
|             EQYzDXtcS0QCZOxpKWfteAlbdJLC/SqPurPYyKi0MVRUPROhbisFASDT38HDH1Dh
 | |
|             0dL5f6ga4aPWLrWbbgWERFOoOPyh4DotlPF37AQOwiEjsbyyRHq3HgbWiaxQRuAh
 | |
|             eqHOn4QVGY92/HFvJ7u3TcnQdLWhTe/lh1RHLNF3RnXtN9if9zC23laDZOiWZplU
 | |
|             yLrUiTCbHrtn1RppPDmLFNMt9dJ7KKgCkOi7Zm5nhqPChbywX13wcfYxVDAjBgkq
 | |
|             hkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkUMSAe
 | |
|             HgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTBeME4wCgYIKoUDBwEBAgME
 | |
|             QAkBKw4ihn7pSIYTEhu0bcvTPZjI3WgVxCkUVlOsc80G69EKFEOTnObGJGSKJ51U
 | |
|             KkOsXF0a7+VBZf3BcVVQh9UECIVEtO+VpuskAgIIAA==
 | |
|         """)
 | |
|         pfx = PFX().decod(pfx_raw)
 | |
|         _, outer_safe_contents = pfx["authSafe"]["content"].defined
 | |
| 
 | |
|         safe_contents = OctetStringSafeContents().decod(bytes(
 | |
|             outer_safe_contents[0]["bagValue"]
 | |
|         ))
 | |
|         safe_bag = safe_contents[0]
 | |
|         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
 | |
|         cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
 | |
|         self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
 | |
|         _, cert = cert_bag["certValue"].defined
 | |
|         self.assertEqual(Certificate(cert), self.cert_test)
 | |
| 
 | |
|         safe_contents = OctetStringSafeContents().decod(bytes(
 | |
|             outer_safe_contents[1]["bagValue"]
 | |
|         ))
 | |
|         safe_bag = safe_contents[0]
 | |
|         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag)
 | |
|         shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"]))
 | |
|         _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
 | |
|         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
 | |
|         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
 | |
|         ukm = bytes(enc_scheme_params["ukm"])
 | |
|         key = gost34112012_pbkdf2(
 | |
|             password=self.password,
 | |
|             salt=bytes(pbkdf2_params["salt"]["specified"]),
 | |
|             iterations=int(pbkdf2_params["iterationCount"]),
 | |
|             dklen=32,
 | |
|         )
 | |
|         # key = hexdec("4b7ae649ca31dd5fe3243a91a5188c03f1d7049bec8e0d241c0e1e8c39ea4c1f")
 | |
|         key_enc, key_mac = kdf_tree_gostr3411_2012_256(
 | |
|             key, b"kdf tree", ukm[GOST3412Kuznechik.blocksize // 2:], 2,
 | |
|         )
 | |
|         ciphertext = bytes(shrouded_key_bag["encryptedData"])
 | |
|         plaintext = ctr_acpkm(
 | |
|             GOST3412Kuznechik,
 | |
|             GOST3412Kuznechik(key_enc).encrypt,
 | |
|             section_size=256 * 1024,
 | |
|             bs=GOST3412Kuznechik.blocksize,
 | |
|             data=ciphertext,
 | |
|             iv=ukm[:GOST3412Kuznechik.blocksize // 2],
 | |
|         )
 | |
|         mac_expected = plaintext[-GOST3412Kuznechik.blocksize:]
 | |
|         plaintext = plaintext[:-GOST3412Kuznechik.blocksize]
 | |
|         mac = omac(
 | |
|             GOST3412Kuznechik(key_mac).encrypt,
 | |
|             GOST3412Kuznechik.blocksize,
 | |
|             plaintext,
 | |
|         )
 | |
|         self.assertSequenceEqual(mac, mac_expected)
 | |
|         self.assertSequenceEqual(plaintext, self.prv_test_raw)
 | |
| 
 | |
|         mac_data = pfx["macData"]
 | |
|         mac_key = gost34112012_pbkdf2(
 | |
|             password=self.password,
 | |
|             salt=bytes(mac_data["macSalt"]),
 | |
|             iterations=int(mac_data["iterations"]),
 | |
|             dklen=96,
 | |
|         )[-32:]
 | |
|         # mac_key = hexdec("a81d1bc91a4a5cf1fd7320f92dda7e5b285816c3b20826a382d7ed0cbf3a9bf4")
 | |
|         self.assertSequenceEqual(
 | |
|             hmac_new(
 | |
|                 key=mac_key,
 | |
|                 msg=SafeContents(outer_safe_contents).encode(),
 | |
|                 digestmod=GOST34112012512,
 | |
|             ).digest(),
 | |
|             bytes(mac_data["mac"]["digest"]),
 | |
|         )
 | |
|         self.assertTrue(gost3410.verify(
 | |
|             self.ca_curve,
 | |
|             self.ca_pub,
 | |
|             GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1],
 | |
|             bytes(cert["signatureValue"]),
 | |
|         ))
 | |
| 
 | |
|     def test_encrypted_cert_and_key(self):
 | |
|         pfx_raw = b64decode(b"""
 | |
|             MIIFjAIBAzCCBSUGCSqGSIb3DQEHAaCCBRYEggUSMIIFDjCCA0EGCSqGSIb3DQEH
 | |
|             BqCCAzIwggMuAgEAMIIDJwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBIMCkGCSqG
 | |
|             SIb3DQEFDDAcBAgUuSVGsSwGjQICCAAwDAYIKoUDBwEBBAIFADAbBgkqhQMHAQEF
 | |
|             AQIwDgQM9Hk3dagtS48+G/x+gIICwWGPqxxN+sTrKbruRf9R5Ya9cf5AtO1frqMn
 | |
|             f1eULfmZmTg/BdE51QQ+Vbnh3v1kmspr6h2+e4Wli+ndEeCWG6A6X/G22h/RAHW2
 | |
|             YrVmf6cCWxW+YrqzT4h/8RQL/9haunD5LmHPLVsYrEai0OwbgXayDSwARVJQLQYq
 | |
|             sLNmZK5ViN+fRiS5wszVJ3AtVq8EuPt41aQEKwPy2gmH4S6WmnQRC6W7aoqmIifF
 | |
|             PJENJNn5K2M1J6zNESs6bFtYNKMArNqtvv3rioY6eAaaLy6AV6ljsekmqodHmQjv
 | |
|             Y4eEioJs0xhpXhZY69PXT+ZBeHv6MSheBhwXqxAd1DqtPTafMjNK8rqKCap9TtPG
 | |
|             vONvo5W9dgwegxRRQzlum8dzV4m1W9Aq4W7t8/UcxDWRz3k6ijFPlGaA9+8ZMTEO
 | |
|             RHhBRvM6OY2/VNNxbgxWfGYuPxpSi3YnCZIPmBEe5lU/Xv7KjzFusGM38F8YR61k
 | |
|             4/QNpKI1QUv714YKfaUQznshGGzILv1NGID62pl1+JI3vuawi2mDMrmkuM9QFU9v
 | |
|             /kRP+c2uBHDuOGEUUSNhF08p7+w3vxplatGWXH9fmIsPBdk2f3wkn+rwoqrEuijM
 | |
|             I/bCAylU/M0DMKhAo9j31UYSZdi4fsfRWYDJMq/8FPn96tuo+oCpbqv3NUwpZM/8
 | |
|             Li4xqgTHtYw/+fRG0/P6XadNEiII/TYjenLfVHXjAHOVJsVeCu/t3EsMYHQddNCh
 | |
|             rFk/Ic2PdIQOyB4/enpW0qrKegSbyZNuF1WI4zl4mI89L8dTQBUkhy45yQXZlDD8
 | |
|             k1ErYdtdEsPtz/4zuSpbnmwCEIRoOuSXtGuJP+tbcWEXRKM2UBgi3qBjpn7DU18M
 | |
|             tsrRM9pDdadl8mT/Vfh9+B8dZBZVxgQu70lMPEGexbUkYHuFCCnyi9J0V92StbIz
 | |
|             Elxla1VebjCCAcUGCSqGSIb3DQEHAaCCAbYEggGyMIIBrjCCAaoGCyqGSIb3DQEM
 | |
|             CgECoIIBQzCCAT8wVQYJKoZIhvcNAQUNMEgwKQYJKoZIhvcNAQUMMBwECP0EQk0O
 | |
|             1twvAgIIADAMBggqhQMHAQEEAgUAMBsGCSqFAwcBAQUBATAOBAzwxSqgAAAAAAAA
 | |
|             AAAEgeUqj9mI3RDfK5hMd0EeYws7foZK/5ANr2wUhP5qnDjAZgn76lExJ+wuvlnS
 | |
|             9PChfWVugvdl/9XJgQvvr9Cu4pOh4ICXplchcy0dGk/MzItHRVC5wK2nTxwQ4kKT
 | |
|             kG9xhLFzoD16dhtqX0+/dQg9G8pE5EzCBIYRXLm1Arcz9k7KVsTJuNMjFrr7EQuu
 | |
|             Tr80ATSQOtsq50zpFyrpznVPGCrOdIjpymZxNdvw48bZxqTtRVDxCYATOGqz0pwH
 | |
|             ClWULHD9LIajLMB2GhBKyQw6ujIlltJs0T+WNdX/AT2FLi1LFSS3+Cj9MVQwIwYJ
 | |
|             KoZIhvcNAQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMC0GCSqGSIb3DQEJFDEg
 | |
|             Hh4AcAAxADIARgByAGkAZQBuAGQAbAB5AE4AYQBtAGUwXjBOMAoGCCqFAwcBAQID
 | |
|             BEDp4e22JmXdnvR0xA99yQuzQuJ8pxBeOpsLm2dZQqt3Fje5zqW1uk/7VOcfV5r2
 | |
|             bKm8nsLOs2rPT8hBOoeAZvOIBAjGIUHw6IjG2QICCAA=
 | |
|         """)
 | |
|         pfx = PFX().decod(pfx_raw)
 | |
|         _, outer_safe_contents = pfx["authSafe"]["content"].defined
 | |
| 
 | |
|         encrypted_data = EncryptedData().decod(bytes(
 | |
|             outer_safe_contents[0]["bagValue"]
 | |
|         ))
 | |
|         eci = encrypted_data["encryptedContentInfo"]
 | |
|         self.assertEqual(eci["contentEncryptionAlgorithm"]["algorithm"], id_pbes2)
 | |
|         pbes2_params = PBES2Params().decod(bytes(
 | |
|             eci["contentEncryptionAlgorithm"]["parameters"]
 | |
|         ))
 | |
|         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
 | |
|         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
 | |
|         ukm = bytes(enc_scheme_params["ukm"])
 | |
|         key = gost34112012_pbkdf2(
 | |
|             password=self.password,
 | |
|             salt=bytes(pbkdf2_params["salt"]["specified"]),
 | |
|             iterations=int(pbkdf2_params["iterationCount"]),
 | |
|             dklen=32,
 | |
|         )
 | |
|         # key = hexdec("d066a96fb326ba896a2352d3f40240a4ded6e7e7bd5b4db6b5241d631c8c381c")
 | |
|         key_enc, key_mac = kdf_tree_gostr3411_2012_256(
 | |
|             key, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2,
 | |
|         )
 | |
|         ciphertext = bytes(eci["encryptedContent"])
 | |
|         plaintext = ctr_acpkm(
 | |
|             GOST3412Magma,
 | |
|             GOST3412Magma(key_enc).encrypt,
 | |
|             section_size=8 * 1024,
 | |
|             bs=GOST3412Magma.blocksize,
 | |
|             data=ciphertext,
 | |
|             iv=ukm[:GOST3412Magma.blocksize // 2],
 | |
|         )
 | |
|         mac_expected = plaintext[-GOST3412Magma.blocksize:]
 | |
|         plaintext = plaintext[:-GOST3412Magma.blocksize]
 | |
|         mac = omac(
 | |
|             GOST3412Magma(key_mac).encrypt,
 | |
|             GOST3412Magma.blocksize,
 | |
|             plaintext,
 | |
|         )
 | |
|         self.assertSequenceEqual(mac, mac_expected)
 | |
| 
 | |
|         safe_contents = SafeContents().decod(plaintext)
 | |
|         safe_bag = safe_contents[0]
 | |
|         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
 | |
|         cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
 | |
|         self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
 | |
|         _, cert = cert_bag["certValue"].defined
 | |
|         self.assertEqual(Certificate(cert), self.cert_test)
 | |
| 
 | |
|         safe_contents = OctetStringSafeContents().decod(bytes(
 | |
|             outer_safe_contents[1]["bagValue"]
 | |
|         ))
 | |
|         safe_bag = safe_contents[0]
 | |
|         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag)
 | |
|         shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"]))
 | |
|         _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined
 | |
|         _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined
 | |
|         _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined
 | |
|         ukm = bytes(enc_scheme_params["ukm"])
 | |
|         key = gost34112012_pbkdf2(
 | |
|             password=self.password,
 | |
|             salt=bytes(pbkdf2_params["salt"]["specified"]),
 | |
|             iterations=int(pbkdf2_params["iterationCount"]),
 | |
|             dklen=32,
 | |
|         )
 | |
|         # key = hexdec("f840d001fd11441e0fb7ccf48f471915e5bf35275309dbe7ade9da4fe460ba7e")
 | |
|         ciphertext = bytes(shrouded_key_bag["encryptedData"])
 | |
|         plaintext = ctr_acpkm(
 | |
|             GOST3412Magma,
 | |
|             GOST3412Magma(key).encrypt,
 | |
|             section_size=8 * 1024,
 | |
|             bs=GOST3412Magma.blocksize,
 | |
|             data=ciphertext,
 | |
|             iv=ukm[:GOST3412Magma.blocksize // 2],
 | |
|         )
 | |
|         self.assertSequenceEqual(plaintext, self.prv_test_raw)
 | |
| 
 | |
|         mac_data = pfx["macData"]
 | |
|         mac_key = gost34112012_pbkdf2(
 | |
|             password=self.password,
 | |
|             salt=bytes(mac_data["macSalt"]),
 | |
|             iterations=int(mac_data["iterations"]),
 | |
|             dklen=96,
 | |
|         )[-32:]
 | |
|         # mac_key = hexdec("084f81782af1534ffd67e3c579c14cb45d7a6f659f46fdbb51a552e874e66fb2")
 | |
|         self.assertSequenceEqual(
 | |
|             hmac_new(
 | |
|                 key=mac_key,
 | |
|                 msg=SafeContents(outer_safe_contents).encode(),
 | |
|                 digestmod=GOST34112012512,
 | |
|             ).digest(),
 | |
|             bytes(mac_data["mac"]["digest"]),
 | |
|         )
 | |
| 
 | |
|     def test_dh(self):
 | |
|         curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"]
 | |
|         # sender_prv_raw = hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8")
 | |
|         sender_cert = Certificate().decod(b64decode("""
 | |
|             MIIB6zCCAZigAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2
 | |
|             MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw
 | |
|             MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD
 | |
|             VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMH
 | |
|             AQEBATALBgkqhQMHAQIBAQEDQwAEQJYpDRNiWWqDgaZje0EmLLOldQ35o5X1ZuZN
 | |
|             SKequYQc/soI3OgDMWD7ThJJCk01IelCeb6MsBmG4lol+pnpVtOjgYcwgYQwYwYD
 | |
|             VR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRL
 | |
|             MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6
 | |
|             gTAdBgNVHQ4EFgQUPx5RgcjkifhlJm4/jQdkbm30rVQwCgYIKoUDBwEBAwIDQQA6
 | |
|             8x7Vk6PvP/8xOGHhf8PuqaXAYskSyJPuBu+3Bo/PEj10devwc1J9uYWIDCGdKKPy
 | |
|             bSlnQHqUPBBPM30YX1YN
 | |
|         """))
 | |
|         recipient_prv_raw = hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1]
 | |
|         recipient_prv = gost3410.prv_unmarshal(recipient_prv_raw)
 | |
|         recipient_cert = Certificate().decod(b64decode("""
 | |
|             MIIB6jCCAZegAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2
 | |
|             MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw
 | |
|             MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD
 | |
|             VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBeMBcGCCqFAwcB
 | |
|             AQEBMAsGCSqFAwcBAgEBAQNDAARAvyeCGXMsYwpYe5aE0w8w3m4vpKQapGInqpnF
 | |
|             lv7h08psFP0s1W80q3BR534F4TmR+o5+iU+AW6ycvWuc73JEQ6OBhzCBhDBjBgNV
 | |
|             HSMEXDBagBSsbA5MRGaiQpbinwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsy
 | |
|             NjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqB
 | |
|             MB0GA1UdDgQWBBQ35gHPN1bx8l2eEMTbrtIg+5MU0TAKBggqhQMHAQEDAgNBABF2
 | |
|             RHDaRqQuBS2yu7yGIGFgA6c/LG4GKjSOwYsRVmXJNNkQ4TB7PB8j3q7gx2koPsVB
 | |
|             m90WfMWSL6SNSh3muuM=
 | |
|         """))
 | |
|         self.assertTrue(gost3410.verify(
 | |
|             self.ca_curve,
 | |
|             self.ca_pub,
 | |
|             GOST34112012256(sender_cert["tbsCertificate"].encode()).digest()[::-1],
 | |
|             bytes(sender_cert["signatureValue"]),
 | |
|         ))
 | |
|         self.assertTrue(gost3410.verify(
 | |
|             self.ca_curve,
 | |
|             self.ca_pub,
 | |
|             GOST34112012256(recipient_cert["tbsCertificate"].encode()).digest()[::-1],
 | |
|             bytes(recipient_cert["signatureValue"]),
 | |
|         ))
 | |
| 
 | |
|         pfx_raw = b64decode("""
 | |
|             MIIKVwIBAzCCClAGCSqGSIb3DQEHAqCCCkEwggo9AgEBMQwwCgYIKoUDBwEBAgIw
 | |
|             ggcjBgkqhkiG9w0BBwGgggcUBIIHEDCCBwwwggKdBgkqhkiG9w0BBwGgggKOBIIC
 | |
|             ijCCAoYwggKCBgsqhkiG9w0BDAoBA6CCAkowggJGBgoqhkiG9w0BCRYBoIICNgSC
 | |
|             AjIwggIuMIIB26ADAgECAgQBjLqEMAoGCCqFAwcBAQMCMDgxDTALBgNVBAoTBFRL
 | |
|             MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDAeFw0w
 | |
|             MTAxMDEwMDAwMDBaFw00OTEyMzEwMDAwMDBaMDsxDTALBgNVBAoTBFRLMjYxKjAo
 | |
|             BgNVBAMTIU9SSUdJTkFUT1I6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBoDAXBggq
 | |
|             hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAtIu3WrwpDhhlXGKhT7UtX1CETswd
 | |
|             H2AESHtLXJU0aWq3v6s0blUWqas8zvittSw6WFXwz7Nkqmtd2Tfk7PyVJb+faghQ
 | |
|             dnGKRcgf9JIePiu/cr8+6/PuFhNBJmX/E92nvydSaOsRrp3nB9fxuITLbPR2C58W
 | |
|             8CQzDVRriB1eoM6jgYcwgYQwYwYDVR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88uk
 | |
|             UyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg
 | |
|             MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUfgZXCZgMrWsIqFfueQBY
 | |
|             OsnXoKQwCgYIKoUDBwEBAwIDQQAKXqnx0BumL0eT7eaAzIjRYiHXsiuWtKn+YHQX
 | |
|             tnMy3xdQPUPDPcmuufF5ed8y84DkF1Qn2ELIOAxUAaz8hwQQMSUwIwYJKoZIhvcN
 | |
|             AQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMIIEZwYJKoZIhvcNAQcDoIIEWDCC
 | |
|             BFQCAQKgggHzoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODEN
 | |
|             MAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAy
 | |
|             NTYtYml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UE
 | |
|             ChMEVEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYt
 | |
|             Yml0MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tB
 | |
|             JiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ
 | |
|             6VbTo4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4
 | |
|             MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEy
 | |
|             IDI1Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoG
 | |
|             CCqFAwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr
 | |
|             8HNSfbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGB/6GB/AIBA6BCMEAwODENMAsG
 | |
|             A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt
 | |
|             Yml0AgQBjLqCoSIEIBt4fjey+k8C1D3OaMca8wl6h3j3C6OAbrx8rmxXktsQMBcG
 | |
|             CSqFAwcBAQcCATAKBggqhQMHAQEGATB2MHQwQDA4MQ0wCwYDVQQKEwRUSzI2MScw
 | |
|             JQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQCBAGMuoMEMJkp
 | |
|             Wae6IVfaY3mP0izRY7ifc41fATXdJ2tmTl+1vitkSE2vLCKXDLl90KfHA6gNmDCC
 | |
|             AVQGCSqGSIb3DQEHATAfBgkqhQMHAQEFAgEwEgQQFhEshEBO2LkAAAAAAAAAAICC
 | |
|             ASQYvLpT/8azEXJfekyGuyvE9UkVX+Ao8sfu9My/c4WAVRNMhZkCqD+BbPwBsIzN
 | |
|             sXZIi9rXGAfsPz7xaO9EUFZPjNOWtF/E01oJgG+gYLFn7qAiEFcmRLptSHuanNHn
 | |
|             7Yol6IHushX4UaW9hEa/L6eFQx/hoDhrNZnWTXNZtNuHuMGC9dzhHhTxfkdjZYXD
 | |
|             v+M7psVj58JutE3U2d4pgxKcBPdMO4vl4+27cIKxQZFZU2zuCVJLYLqmPT5pCBkM
 | |
|             mJqy7bZwHOJ9kBq/TGUf8iJGYSCNre3RTNLbcTTk7rZrbiMkFsG3borzenpouS5E
 | |
|             BcCkBt8Mj0nvsMCu9ipHTuWww7LltlkXCjlNXFUi6ZI3VyHW5CDpghujQWiZxiAc
 | |
|             JuGl6GwZoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODENMAsG
 | |
|             A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt
 | |
|             Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME
 | |
|             VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYtYml0
 | |
|             MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tBJiyz
 | |
|             pXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ6VbT
 | |
|             o4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0w
 | |
|             CwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1
 | |
|             Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoGCCqF
 | |
|             AwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr8HNS
 | |
|             fbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGCAQ4wggEKAgEBMEAwODENMAsGA1UE
 | |
|             ChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0
 | |
|             AgQBjLqCMAoGCCqFAwcBAQICoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
 | |
|             BgkqhkiG9w0BCQUxDxcNMjEwNDE0MTkyMTEyWjAvBgkqhkiG9w0BCQQxIgQg1XOA
 | |
|             zNa710QuXsn5+yIf3cNTiFOQMgTiBRJBz8Tr4I0wCgYIKoUDBwEBAQEEQALINal9
 | |
|             7wHXYiG+w0yzSkKOs0jRZew0S73r/cfk/sUoM3HKKIEbKruvlAdiOqX/HLFSEx/s
 | |
|             kxFG6QUFH8uuoX8=
 | |
|         """)
 | |
|         pfx = PFX().decod(pfx_raw)
 | |
|         self.assertEqual(pfx["authSafe"]["contentType"], id_signedData)
 | |
| 
 | |
|         sd = SignedData().decod(bytes(pfx["authSafe"]["content"]))
 | |
|         self.assertEqual(sd["certificates"][0]["certificate"], sender_cert)
 | |
|         si = sd["signerInfos"][0]
 | |
|         self.assertEqual(
 | |
|             si["digestAlgorithm"]["algorithm"],
 | |
|             id_tc26_gost3411_2012_256,
 | |
|         )
 | |
|         digest = [
 | |
|             bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"]
 | |
|             if attr["attrType"] == id_messageDigest
 | |
|         ][0]
 | |
|         sender_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes(
 | |
|             sender_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"]
 | |
|         ))))
 | |
|         content = bytes(sd["encapContentInfo"]["eContent"])
 | |
|         self.assertSequenceEqual(digest, GOST34112012256(content).digest())
 | |
|         self.assertTrue(gost3410.verify(
 | |
|             curve,
 | |
|             sender_pub,
 | |
|             GOST34112012256(
 | |
|                 SignedAttributes(si["signedAttrs"]).encode()
 | |
|             ).digest()[::-1],
 | |
|             bytes(si["signature"]),
 | |
|         ))
 | |
| 
 | |
|         outer_safe_contents = SafeContents().decod(content)
 | |
| 
 | |
|         safe_bag = outer_safe_contents[0]
 | |
|         self.assertEqual(safe_bag["bagId"], id_data)
 | |
|         safe_contents = OctetStringSafeContents().decod(bytes(safe_bag["bagValue"]))
 | |
|         safe_bag = safe_contents[0]
 | |
|         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag)
 | |
|         cert_bag = CertBag().decod(bytes(safe_bag["bagValue"]))
 | |
|         self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate)
 | |
|         _, cert = cert_bag["certValue"].defined
 | |
|         self.assertEqual(Certificate(cert), self.cert_test)
 | |
| 
 | |
|         safe_bag = outer_safe_contents[1]
 | |
|         self.assertEqual(safe_bag["bagId"], id_envelopedData)
 | |
|         ed = EnvelopedData().decod(bytes(safe_bag["bagValue"]))
 | |
|         kari = ed["recipientInfos"][0]["kari"]
 | |
|         ukm = bytes(kari["ukm"])
 | |
|         self.assertEqual(
 | |
|             kari["keyEncryptionAlgorithm"]["algorithm"],
 | |
|             id_gostr3412_2015_kuznyechik_wrap_kexp15,
 | |
|         )
 | |
|         self.assertEqual(
 | |
|             kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"],
 | |
|             id_tc26_agreement_gost3410_2012_256,
 | |
|         )
 | |
|         kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"])
 | |
|         keymat = keg(curve, recipient_prv, sender_pub, ukm)
 | |
|         kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:]
 | |
|         cek = kimp15(
 | |
|             GOST3412Kuznechik(kek).encrypt,
 | |
|             GOST3412Kuznechik(kim).encrypt,
 | |
|             GOST3412Kuznechik.blocksize,
 | |
|             kexp,
 | |
|             ukm[24:24 + GOST3412Kuznechik.blocksize // 2],
 | |
|         )
 | |
|         eci = ed["encryptedContentInfo"]
 | |
|         self.assertEqual(
 | |
|             eci["contentEncryptionAlgorithm"]["algorithm"],
 | |
|             id_gostr3412_2015_kuznyechik_ctracpkm,
 | |
|         )
 | |
|         eci_ukm = bytes(
 | |
|             eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"]
 | |
|         )
 | |
|         content = ctr_acpkm(
 | |
|             GOST3412Kuznechik,
 | |
|             GOST3412Kuznechik(cek).encrypt,
 | |
|             256 * 1024,
 | |
|             GOST3412Kuznechik.blocksize,
 | |
|             bytes(eci["encryptedContent"]),
 | |
|             eci_ukm[:GOST3412Kuznechik.blocksize // 2],
 | |
|         )
 | |
| 
 | |
|         safe_contents = SafeContents().decod(content)
 | |
|         safe_bag = safe_contents[0]
 | |
|         self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_keyBag)
 | |
|         KeyBag().decod(bytes(safe_bag["bagValue"]))
 | |
|         self.assertSequenceEqual(bytes(safe_bag["bagValue"]), self.prv_test_raw)
 |