# 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("""
MIIFqgIBAzCCBSsGCSqGSIb3DQEHAaCCBRwEggUYMIIFFDCCASIGCSqGSIb3DQEH
AaCCARMEggEPMIIBCzCCAQcGCyqGSIb3DQEMCgECoIHgMIHdMHEGCSqGSIb3DQEF
DTBkMEEGCSqGSIb3DQEFDDA0BCD5qZr0TTIsBvdgUoq/zFwOzdyJohj6/4Wiyccg
j9AK/QICB9AwDAYIKoUDBwEBBAIFADAfBgYqhQMCAhUwFQQI3Ip/Vp0IsyIGCSqF
AwcBAgUBAQRoSfLhgx9s/zn+BjnhT0ror07vS55Ys5hgvVpWDx4mXGWWyez/2sMc
aFgSr4H4UTGGwoMynGLpF1IOVo+bGJ0ePqHB+gS5OL9oV+PUmZ/ELrRENKlCDqfY
WvpSystX29CvCFrnTnDsbBYxFTATBgkqhkiG9w0BCRUxBgQEAQAAADCCA+oGCSqG
SIb3DQEHBqCCA9swggPXAgEAMIID0AYJKoZIhvcNAQcBMHEGCSqGSIb3DQEFDTBk
MEEGCSqGSIb3DQEFDDA0BCCJTJLZQRi1WIpQHzyjXbq7+Vw2+1280C45x8ff6kMS
VAICB9AwDAYIKoUDBwEBBAIFADAfBgYqhQMCAhUwFQQIxepowwvS11MGCSqFAwcB
AgUBAYCCA06n09P/o+eDEKoSWpvlpOLKs7dKmVquKzJ81nCngvLQ5fEWL1WkxwiI
rEhm53JKLD0wy4hekalEk011Bvc51XP9gkDkmaoBpnV/TyKIY35wl6ATfeGXno1M
KoA+Ktdhv4gLnz0k2SXdkUj11JwYskXue+REA0p4m2ZsoaTmvoODamh9JeY/5Qjy
Xe58CGnyXFzX3eU86qs4WfdWdS3NzYYOk9zzVl46le9u79O/LnW2j4n2of/Jpk/L
YjrRmz5oYeQOqKOKhEyhpO6e+ejr6laduEv7TwJQKRNiygogbVvkNn3VjHTSOUG4
W+3NRPhjb0jD9obdyx6MWa6O3B9bUzFMNav8/gYn0vTDxqXMLy/92oTngNrVx6Gc
cNl128ISrDS6+RxtAMiEBRK6xNkemqX5yNXG5GrLQQFGP6mbs2nNpjKlgj3pljmX
Eky2/G78XiJrv02OgGs6CKnI9nMpa6N7PBHV34MJ6EZzWOWDRQ420xk63mnicrs0
WDVJ0xjdu4FW3iEk02EaiRTvGBpa6GL7LBp6QlaXSSwONx725cyRsL9cTlukqXER
WHDlMpjYLbkGZRrCc1myWgEfsputfSIPNF/oLv9kJNWacP3uuDOfecg3us7eg2OA
xo5zrYfn39GcBMF1WHAYRO/+PnJb9jrDuLAE8+ONNqjNulWNK9CStEhb6Te+yE6q
oeP6hJjFLi+nFLE9ymIo0A7gLQD5vzFvl+7v1ZNVnQkwRUsWoRiEVVGnv3Z1iZU6
xStxgoHMl62V/P5cz4dr9vJM2adEWNZcVXl6mk1H8DRc1sRGnvs2l237oKWRVntJ
hoWnZ8qtD+3ZUqsX79QhVzUQBzKuBt6jwNhaHLGl5B+Or/zA9FezsOh6+Uc+fZaV
W7fFfeUyWwGy90XD3ybTrjzep9f3nt55Z2c+fu2iEwhoyImWLuC3+CVhf9Af59j9
8/BophMJuATDJEtgi8rt4vLnfxKu250Mv2ZpbfF69EGTgFYbwc55zRfaUG9zlyCu
1YwMJ6HC9FUVtJp9gObSrirbzTH7mVaMjQkBLotazWbegzI+be8V3yT06C+ehD+2
GdLWAVs9hp8gPHEUShb/XrgPpDSJmFlOiyeOFBO/j4edDACKqVcwdjBOMAoGCCqF
AwcBAQIDBEAIFX0fyZe20QKKhWm6WYX+S92Gt6zaXroXOvAmayzLfZ5Sd9C2t9zZ
JSg6M8RBUYpw/8ym5ou1o2nDa09M5zF3BCCpzyCQBI+rzfISeKvPV1ROfcXiYU93
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"""
            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
        """)
        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"""
            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
        """)
        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("""
            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
            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)
