3.15:
* Licence changed back to GNU GPLv3+.
GNU LGPLv3+ licenced versions are not available anymore
+ * More ASN.1-based test vectors (PyDERASN dependency required)
3.14:
Add missing typing stubs related to previous release.
@itemize
@item Licence changed back to GNU GPLv3+. GNU LGPLv3+ licenced
versions are not available anymore
+ @item More ASN.1-based test vectors
+ (`PyDERASN <http://pyderasn.cypherpunks.ru/>`__ dependency required)
@end itemize
@anchor{Release 3.14}
PYTHONPATH=$(PYTHONPATH):.. $(PYTHON) -m unittest test_wrap
PYTHONPATH=$(PYTHONPATH):.. $(PYTHON) -m unittest test_gost3412
PYTHONPATH=$(PYTHONPATH):.. $(PYTHON) -m unittest test_gost3413
- PYTHONPATH=$(PYTHONPATH):.. $(PYTHON) -m unittest test_cms_enveloped
+ PYTHONPATH=$(PYTHONPATH):.. $(PYTHON) -m unittest test_x509
+ PYTHONPATH=$(PYTHONPATH):.. $(PYTHON) -m unittest test_cms
+ PYTHONPATH=$(PYTHONPATH):.. $(PYTHON) -m unittest test_pfx
--- /dev/null
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2018 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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/>.
+"""CMS related structures (**NOT COMPLETE**)
+"""
+
+from pyderasn import Any
+from pyderasn import BitString
+from pyderasn import Choice
+from pyderasn import Integer
+from pyderasn import ObjectIdentifier
+from pyderasn import OctetString
+from pyderasn import Sequence
+from pyderasn import SequenceOf
+from pyderasn import SetOf
+from pyderasn import tag_ctxc
+from pyderasn import tag_ctxp
+
+from pygost.asn1schemas.x509 import AlgorithmIdentifier
+from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
+
+
+class CMSVersion(Integer):
+ pass
+
+
+class ContentType(ObjectIdentifier):
+ pass
+
+
+class RecipientIdentifier(Choice):
+ schema = (
+ ("issuerAndSerialNumber", Any()),
+ # ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))),
+ )
+
+
+class KeyEncryptionAlgorithmIdentifier(AlgorithmIdentifier):
+ pass
+
+
+class EncryptedKey(OctetString):
+ pass
+
+
+class KeyTransRecipientInfo(Sequence):
+ schema = (
+ ("version", CMSVersion()),
+ ("rid", RecipientIdentifier()),
+ ("keyEncryptionAlgorithm", KeyEncryptionAlgorithmIdentifier()),
+ ("encryptedKey", EncryptedKey()),
+ )
+
+
+class OriginatorPublicKey(Sequence):
+ schema = (
+ ("algorithm", AlgorithmIdentifier()),
+ ("publicKey", BitString()),
+ )
+
+
+class OriginatorIdentifierOrKey(Choice):
+ schema = (
+ # ("issuerAndSerialNumber", IssuerAndSerialNumber()),
+ # ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))),
+ ("originatorKey", OriginatorPublicKey(impl=tag_ctxc(1))),
+ )
+
+
+class UserKeyingMaterial(OctetString):
+ pass
+
+
+class KeyAgreeRecipientIdentifier(Choice):
+ schema = (
+ ("issuerAndSerialNumber", Any()),
+ # ("rKeyId", RecipientKeyIdentifier(impl=tag_ctxc(0))),
+ )
+
+
+class RecipientEncryptedKey(Sequence):
+ schema = (
+ ("rid", KeyAgreeRecipientIdentifier()),
+ ("encryptedKey", EncryptedKey()),
+ )
+
+
+class RecipientEncryptedKeys(SequenceOf):
+ schema = RecipientEncryptedKey()
+
+
+class KeyAgreeRecipientInfo(Sequence):
+ schema = (
+ ("version", CMSVersion(3)),
+ ("originator", OriginatorIdentifierOrKey(expl=tag_ctxc(0))),
+ ("ukm", UserKeyingMaterial(expl=tag_ctxc(1), optional=True)),
+ ("keyEncryptionAlgorithm", KeyEncryptionAlgorithmIdentifier()),
+ ("recipientEncryptedKeys", RecipientEncryptedKeys()),
+ )
+
+
+class RecipientInfo(Choice):
+ schema = (
+ ("ktri", KeyTransRecipientInfo()),
+ ("kari", KeyAgreeRecipientInfo(impl=tag_ctxc(1))),
+ # ("kekri", KEKRecipientInfo(impl=tag_ctxc(2))),
+ # ("pwri", PasswordRecipientInfo(impl=tag_ctxc(3))),
+ # ("ori", OtherRecipientInfo(impl=tag_ctxc(4))),
+ )
+
+
+class RecipientInfos(SetOf):
+ schema = RecipientInfo()
+ bounds = (1, float("+inf"))
+
+
+class ContentEncryptionAlgorithmIdentifier(AlgorithmIdentifier):
+ pass
+
+
+class EncryptedContent(OctetString):
+ pass
+
+
+class EncryptedContentInfo(Sequence):
+ schema = (
+ ("contentType", ContentType()),
+ ("contentEncryptionAlgorithm", ContentEncryptionAlgorithmIdentifier()),
+ ("encryptedContent", EncryptedContent(impl=tag_ctxp(0), optional=True)),
+ )
+
+
+class EnvelopedData(Sequence):
+ schema = (
+ ("version", CMSVersion()),
+ # ("originatorInfo", OriginatorInfo(impl=tag_ctxc(0), optional=True)),
+ ("recipientInfos", RecipientInfos()),
+ ("encryptedContentInfo", EncryptedContentInfo()),
+ # ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)),
+ )
+
+
+class ContentInfo(Sequence):
+ schema = (
+ ("contentType", ContentType()),
+ ("content", Any(expl=tag_ctxc(0))),
+ )
+
+
+class Gost2814789IV(OctetString):
+ bounds = (8, 8)
+
+
+class Gost2814789Parameters(Sequence):
+ schema = (
+ ("iv", Gost2814789IV()),
+ ("encryptionParamSet", ObjectIdentifier()),
+ )
+
+
+class Gost2814789Key(OctetString):
+ bounds = (32, 32)
+
+
+class Gost2814789MAC(OctetString):
+ bounds = (4, 4)
+
+
+class Gost2814789EncryptedKey(Sequence):
+ schema = (
+ ("encryptedKey", Gost2814789Key()),
+ ("maskKey", Gost2814789Key(impl=tag_ctxp(0), optional=True)),
+ ("macKey", Gost2814789MAC()),
+ )
+
+
+class GostR34102001TransportParameters(Sequence):
+ schema = (
+ ("encryptionParamSet", ObjectIdentifier()),
+ ("ephemeralPublicKey", SubjectPublicKeyInfo(
+ impl=tag_ctxc(0),
+ optional=True,
+ )),
+ ("ukm", OctetString()),
+ )
+
+
+class GostR3410KeyTransport(Sequence):
+ schema = (
+ ("sessionEncryptedKey", Gost2814789EncryptedKey()),
+ ("transportParameters", GostR34102001TransportParameters(
+ impl=tag_ctxc(0),
+ optional=True,
+ )),
+ )
+
+
+class EncapsulatedContentInfo(Sequence):
+ schema = (
+ ("eContentType", ContentType()),
+ ("eContent", OctetString(expl=tag_ctxc(0), optional=True)),
+ )
+
+
+class SignerIdentifier(Choice):
+ schema = (
+ ("issuerAndSerialNumber", Any()),
+ # ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))),
+ )
+
+
+class DigestAlgorithmIdentifiers(SetOf):
+ schema = AlgorithmIdentifier()
+
+
+class DigestAlgorithmIdentifier(AlgorithmIdentifier):
+ pass
+
+
+class SignatureAlgorithmIdentifier(AlgorithmIdentifier):
+ pass
+
+
+class SignatureValue(OctetString):
+ pass
+
+
+class SignerInfo(Sequence):
+ schema = (
+ ("version", CMSVersion()),
+ ("sid", SignerIdentifier()),
+ ("digestAlgorithm", DigestAlgorithmIdentifier()),
+ # ("signedAttrs", SignedAttributes(impl=tag_ctxc(0), optional=True)),
+ ("signatureAlgorithm", SignatureAlgorithmIdentifier()),
+ ("signature", SignatureValue()),
+ # ("unsignedAttrs", UnsignedAttributes(impl=tag_ctxc(1), optional=True)),
+ )
+
+
+class SignerInfos(SetOf):
+ schema = SignerInfo()
+
+
+class SignedData(Sequence):
+ schema = (
+ ("version", CMSVersion()),
+ ("digestAlgorithms", DigestAlgorithmIdentifiers()),
+ ("encapContentInfo", EncapsulatedContentInfo()),
+ # ("certificates", CertificateSet(impl=tag_ctxc(0), optional=True)),
+ # ("crls", RevocationInfoChoices(impl=tag_ctxc(1), optional=True)),
+ ("signerInfos", SignerInfos()),
+ )
+
+
+class Digest(OctetString):
+ pass
+
+
+class DigestedData(Sequence):
+ schema = (
+ ("version", CMSVersion()),
+ ("digestAlgorithm", DigestAlgorithmIdentifier()),
+ ("encapContentInfo", EncapsulatedContentInfo()),
+ ("digest", Digest()),
+ )
--- /dev/null
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2018 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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/>.
+"""PKCS #12 related structures (**NOT COMPLETE**)
+"""
+
+from pyderasn import Any
+from pyderasn import Choice
+from pyderasn import Integer
+from pyderasn import ObjectIdentifier
+from pyderasn import OctetString
+from pyderasn import Sequence
+from pyderasn import SequenceOf
+from pyderasn import SetOf
+from pyderasn import tag_ctxc
+from pyderasn import tag_ctxp
+
+from pygost.asn1schemas.cms import CMSVersion
+from pygost.asn1schemas.cms import ContentType
+from pygost.asn1schemas.x509 import AlgorithmIdentifier
+
+
+class EncryptionAlgorithmIdentifier(AlgorithmIdentifier):
+ schema = (
+ ("algorithm", ObjectIdentifier()),
+ ("parameters", Any(optional=True)),
+ )
+
+
+class ContentEncryptionAlgorithmIdentifier(EncryptionAlgorithmIdentifier):
+ pass
+
+
+class PBES2KDFs(AlgorithmIdentifier):
+ schema = (
+ ("algorithm", ObjectIdentifier()),
+ ("parameters", Any(optional=True)),
+ )
+
+
+class PBES2Encs(AlgorithmIdentifier):
+ schema = (
+ ("algorithm", ObjectIdentifier()),
+ ("parameters", Any(optional=True)),
+ )
+
+
+class PBES2Params(Sequence):
+ schema = (
+ ("keyDerivationFunc", PBES2KDFs()),
+ ("encryptionScheme", PBES2Encs()),
+ )
+
+
+class EncryptedContent(OctetString):
+ pass
+
+
+class EncryptedContentInfo(Sequence):
+ schema = (
+ ("contentType", ContentType()),
+ ("contentEncryptionAlgorithm", ContentEncryptionAlgorithmIdentifier()),
+ ("encryptedContent", EncryptedContent(impl=tag_ctxp(0), optional=True)),
+ )
+
+
+class EncryptedData(Sequence):
+ schema = (
+ ("version", CMSVersion()),
+ ("encryptedContentInfo", EncryptedContentInfo()),
+ # ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)),
+ )
+
+
+class PKCS12BagSet(Any):
+ pass
+
+
+class AttrValue(SetOf):
+ schema = Any()
+
+
+class PKCS12Attribute(Sequence):
+ schema = (
+ ("attrId", ObjectIdentifier()),
+ ("attrValue", AttrValue()),
+ )
+
+
+class PKCS12Attributes(SetOf):
+ schema = PKCS12Attribute()
+
+
+class SafeBag(Sequence):
+ schema = (
+ ("bagId", ObjectIdentifier()),
+ ("bagValue", PKCS12BagSet(expl=tag_ctxc(0))),
+ ("bagAttributes", PKCS12Attributes(optional=True)),
+ )
+
+
+class SafeContents(SequenceOf):
+ schema = SafeBag()
+
+
+class OctetStringSafeContents(Sequence):
+ tag_default = OctetString.tag_default
+ schema = (("safeContents", SafeContents()),)
+
+
+class AuthSafe(Sequence):
+ schema = (
+ ("contentType", ContentType()),
+ ("content", Any(expl=tag_ctxc(0))),
+ )
+
+
+class DigestInfo(Sequence):
+ schema = (
+ ("digestAlgorithm", AlgorithmIdentifier()),
+ ("digest", OctetString()),
+ )
+
+
+class MacData(Sequence):
+ schema = (
+ ("mac", DigestInfo()),
+ ("macSalt", OctetString()),
+ ("iterations", Integer(default=1)),
+ )
+
+
+class PFX(Sequence):
+ schema = (
+ ("version", Integer(default=1)),
+ ("authSafe", AuthSafe()),
+ ("macData", MacData(optional=True)),
+ )
+
+
+class EncryptedPrivateKeyInfo(Sequence):
+ schema = (
+ ("encryptionAlgorithm", EncryptionAlgorithmIdentifier()),
+ ("encryptedData", OctetString()),
+ )
+
+
+class PKCS8ShroudedKeyBag(EncryptedPrivateKeyInfo):
+ pass
+
+
+class PBKDF2Salt(Choice):
+ schema = (
+ ("specified", OctetString()),
+ # ("otherSource", PBKDF2SaltSources()),
+ )
+
+
+id_hmacWithSHA1 = ObjectIdentifier("1.2.840.113549.2.7")
+
+
+class PBKDF2PRFs(AlgorithmIdentifier):
+ schema = (
+ ("algorithm", ObjectIdentifier(default=id_hmacWithSHA1)),
+ ("parameters", Any(optional=True)),
+ )
+
+
+class IterationCount(Integer):
+ bounds = (1, float("+inf"))
+
+
+class KeyLength(Integer):
+ bounds = (1, float("+inf"))
+
+
+class PBKDF2Params(Sequence):
+ schema = (
+ ("salt", PBKDF2Salt()),
+ ("iterationCount", IterationCount(optional=True)),
+ ("keyLength", KeyLength(optional=True)),
+ ("prf", PBKDF2PRFs()),
+ )
--- /dev/null
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2018 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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/>.
+""":rfc:`5280` related structures (**NOT COMPLETE**)
+
+They are taken from `PyDERASN <http://pyderasn.cypherpunks.ru/`__ tests.
+"""
+
+from pyderasn import Any
+from pyderasn import BitString
+from pyderasn import Boolean
+from pyderasn import Choice
+from pyderasn import GeneralizedTime
+from pyderasn import Integer
+from pyderasn import ObjectIdentifier
+from pyderasn import OctetString
+from pyderasn import PrintableString
+from pyderasn import Sequence
+from pyderasn import SequenceOf
+from pyderasn import SetOf
+from pyderasn import tag_ctxc
+from pyderasn import tag_ctxp
+from pyderasn import TeletexString
+from pyderasn import UTCTime
+
+
+class Version(Integer):
+ schema = (
+ ("v1", 0),
+ ("v2", 1),
+ ("v3", 2),
+ )
+
+
+class CertificateSerialNumber(Integer):
+ pass
+
+
+class AlgorithmIdentifier(Sequence):
+ schema = (
+ ("algorithm", ObjectIdentifier()),
+ ("parameters", Any(optional=True)),
+ )
+
+
+class AttributeType(ObjectIdentifier):
+ pass
+
+
+class AttributeValue(Any):
+ pass
+
+
+class OrganizationName(Choice):
+ schema = (
+ ("printableString", PrintableString()),
+ ("teletexString", TeletexString()),
+ )
+
+
+class AttributeTypeAndValue(Sequence):
+ schema = (
+ ("type", AttributeType(defines=(((".", "value"), {
+ ObjectIdentifier("2.5.4.6"): PrintableString(),
+ ObjectIdentifier("2.5.4.8"): PrintableString(),
+ ObjectIdentifier("2.5.4.7"): PrintableString(),
+ ObjectIdentifier("2.5.4.10"): OrganizationName(),
+ ObjectIdentifier("2.5.4.3"): PrintableString(),
+ }),))),
+ ("value", AttributeValue()),
+ )
+
+
+class RelativeDistinguishedName(SetOf):
+ schema = AttributeTypeAndValue()
+ bounds = (1, float("+inf"))
+
+
+class RDNSequence(SequenceOf):
+ schema = RelativeDistinguishedName()
+
+
+class Name(Choice):
+ schema = (
+ ("rdnSequence", RDNSequence()),
+ )
+
+
+class Time(Choice):
+ schema = (
+ ("utcTime", UTCTime()),
+ ("generalTime", GeneralizedTime()),
+ )
+
+
+class Validity(Sequence):
+ schema = (
+ ("notBefore", Time()),
+ ("notAfter", Time()),
+ )
+
+
+class SubjectPublicKeyInfo(Sequence):
+ schema = (
+ ("algorithm", AlgorithmIdentifier()),
+ ("subjectPublicKey", BitString()),
+ )
+
+
+class UniqueIdentifier(BitString):
+ pass
+
+
+class Extension(Sequence):
+ schema = (
+ ("extnID", ObjectIdentifier()),
+ ("critical", Boolean(default=False)),
+ ("extnValue", OctetString()),
+ )
+
+
+class Extensions(SequenceOf):
+ schema = Extension()
+ bounds = (1, float("+inf"))
+
+
+class TBSCertificate(Sequence):
+ schema = (
+ ("version", Version(expl=tag_ctxc(0), default="v1")),
+ ("serialNumber", CertificateSerialNumber()),
+ ("signature", AlgorithmIdentifier()),
+ ("issuer", Name()),
+ ("validity", Validity()),
+ ("subject", Name()),
+ ("subjectPublicKeyInfo", SubjectPublicKeyInfo()),
+ ("issuerUniqueID", UniqueIdentifier(impl=tag_ctxp(1), optional=True)),
+ ("subjectUniqueID", UniqueIdentifier(impl=tag_ctxp(2), optional=True)),
+ ("extensions", Extensions(expl=tag_ctxc(3), optional=True)),
+ )
+
+
+class Certificate(Sequence):
+ schema = (
+ ("tbsCertificate", TBSCertificate()),
+ ("signatureAlgorithm", AlgorithmIdentifier()),
+ ("signatureValue", BitString()),
+ )
--- /dev/null
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2018 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 unittest import skipIf
+from unittest import TestCase
+
+from pygost.gost28147 import cfb_decrypt
+from pygost.gost3410 import CURVE_PARAMS
+from pygost.gost3410 import GOST3410Curve
+from pygost.gost3410 import prv_unmarshal
+from pygost.gost3410 import pub_unmarshal
+from pygost.gost3410 import public_key
+from pygost.gost3410 import verify
+from pygost.gost3410_vko import kek_34102012256
+from pygost.gost3410_vko import ukm_unmarshal
+from pygost.gost34112012256 import GOST34112012256
+from pygost.gost34112012512 import GOST34112012512
+from pygost.utils import hexdec
+from pygost.wrap import unwrap_cryptopro
+from pygost.wrap import unwrap_gost
+
+try:
+ from pyderasn import OctetString
+
+ from pygost.asn1schemas.cms import ContentInfo
+ from pygost.asn1schemas.cms import DigestedData
+ from pygost.asn1schemas.cms import EnvelopedData
+ from pygost.asn1schemas.cms import Gost2814789EncryptedKey
+ from pygost.asn1schemas.cms import Gost2814789Parameters
+ from pygost.asn1schemas.cms import GostR3410KeyTransport
+ from pygost.asn1schemas.cms import SignedData
+except ImportError:
+ pyderasn_exists = False
+else:
+ pyderasn_exists = True
+
+
+@skipIf(not pyderasn_exists, "PyDERASN dependency is required")
+class TestSigned(TestCase):
+ """SignedData test vectors from "Использование
+ алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 в
+ криптографических сообщениях формата CMS" (TK26CMS.pdf)
+ """
+
+ def process_cms(
+ self,
+ content_info_raw,
+ prv_key_raw,
+ curve_name,
+ hasher,
+ mode,
+ ):
+ content_info, tail = ContentInfo().decode(content_info_raw)
+ self.assertSequenceEqual(tail, b"")
+ signed_data, tail = SignedData().decode(bytes(content_info["content"]))
+ self.assertSequenceEqual(tail, b"")
+ self.assertEqual(len(signed_data["signerInfos"]), 1)
+ curve = GOST3410Curve(*CURVE_PARAMS[curve_name])
+ self.assertTrue(verify(
+ curve,
+ public_key(curve, prv_unmarshal(prv_key_raw)),
+ hasher(bytes(signed_data["encapContentInfo"]["eContent"])).digest()[::-1],
+ bytes(signed_data["signerInfos"][0]["signature"]),
+ mode=mode,
+ ))
+
+ def test_256(self):
+ content_info_raw = b64decode("""
+MIIBBQYJKoZIhvcNAQcCoIH3MIH0AgEBMQ4wDAYIKoUDBwEBAgIFADAbBgkqhkiG
+9w0BBwGgDgQMVGVzdCBtZXNzYWdlMYHBMIG+AgEBMFswVjEpMCcGCSqGSIb3DQEJ
+ARYaR29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RSMzQx
+MC0yMDEyICgyNTYgYml0KSBleGFtcGxlAgEBMAwGCCqFAwcBAQICBQAwDAYIKoUD
+BwEBAQEFAARAkptb2ekZbC94FaGDQeP70ExvTkXtOY9zgz3cCco/hxPhXUVo3eCx
+VNwDQ8enFItJZ8DEX4blZ8QtziNCMl5HbA==
+ """)
+ prv_key_raw = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1]
+ self.process_cms(
+ content_info_raw,
+ prv_key_raw,
+ "GostR3410_2001_CryptoPro_XchA_ParamSet",
+ GOST34112012256,
+ 2001,
+ )
+
+ def test_512(self):
+ content_info_raw = b64decode("""
+MIIBSQYJKoZIhvcNAQcCoIIBOjCCATYCAQExDjAMBggqhQMHAQECAwUAMBsGCSqG
+SIb3DQEHAaAOBAxUZXN0IG1lc3NhZ2UxggECMIH/AgEBMFswVjEpMCcGCSqGSIb3
+DQEJARYaR29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RS
+MzQxMC0yMDEyICg1MTIgYml0KSBleGFtcGxlAgEBMAwGCCqFAwcBAQIDBQAwDAYI
+KoUDBwEBAQIFAASBgFyVohNhMHUi/+RAF3Gh/cC7why6v+4jPWVlx1TYlXtV8Hje
+hI2Y+rP52/LO6EUHG/XcwCBbUxmRWsbUSRRBAexmaafkSdvv2FFwC8kHOcti+UPX
+PS+KRYxT8vhcsBLWWxDkc1McI7aF09hqtED36mQOfACzeJjEoUjALpmJob1V
+ """)
+ prv_key_raw = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1]
+ self.process_cms(
+ content_info_raw,
+ prv_key_raw,
+ "GostR3410_2012_TC26_ParamSetB",
+ GOST34112012512,
+ 2012,
+ )
+
+
+@skipIf(not pyderasn_exists, "PyDERASN dependency is required")
+class TestDigested(TestCase):
+ """DigestedData test vectors from "Использование
+ алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 в
+ криптографических сообщениях формата CMS" (TK26CMS.pdf)
+ """
+
+ def process_cms(self, content_info_raw, hasher):
+ content_info, tail = ContentInfo().decode(content_info_raw)
+ self.assertSequenceEqual(tail, b"")
+ digested_data, tail = DigestedData().decode(bytes(content_info["content"]))
+ self.assertSequenceEqual(tail, b"")
+ self.assertSequenceEqual(
+ hasher(bytes(digested_data["encapContentInfo"]["eContent"])).digest(),
+ bytes(digested_data["digest"]),
+ )
+
+ def test_256(self):
+ content_info_raw = b64decode("""
+MIGdBgkqhkiG9w0BBwWggY8wgYwCAQAwDAYIKoUDBwEBAgIFADBXBgkqhkiG9w0B
+BwGgSgRI0eUg4uXy8OgsINHy8Ojh7uboIOLt8/boLCDi5f7y+iDxIOzu8P8g8fLw
+5evg7Ogg7eAg9fDg4fD7/yDv6/rq+yDI4+7w5eL7BCCd0v5OkECeXah/U5dtdAWw
+wMrGKPxmmnQdUAY8VX6PUA==
+ """)
+ self.process_cms(content_info_raw, GOST34112012256)
+
+ def test_512(self):
+ content_info_raw = b64decode("""
+MIG0BgkqhkiG9w0BBwWggaYwgaMCAQAwDAYIKoUDBwEBAgMFADBOBgkqhkiG9w0B
+BwGgQQQ/MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAx
+MjM0NTY3ODkwMTIzNDU2Nzg5MDEyBEAbVNAaSvW51cw9htaNKFRisZq8JHUiLzXA
+hRIr5Lof+gCtMPh2ezqCOExldPAkwxHipIEzKwjvf0F5eJHBZG9I
+ """)
+ self.process_cms(content_info_raw, GOST34112012512)
+
+
+@skipIf(not pyderasn_exists, "PyDERASN dependency is required")
+class TestEnvelopedKTRI(TestCase):
+ """EnvelopedData KeyTransRecipientInfo-based test vectors from
+ "Использование алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10
+ в криптографических сообщениях формата CMS" (TK26CMS.pdf)
+ """
+
+ def process_cms(
+ self,
+ content_info_raw,
+ prv_key_our,
+ curve_name,
+ keker,
+ plaintext_expected,
+ ):
+ sbox = "Gost28147_tc26_ParamZ"
+ content_info, tail = ContentInfo().decode(content_info_raw)
+ self.assertSequenceEqual(tail, b"")
+ enveloped_data, tail = EnvelopedData().decode(bytes(content_info["content"]))
+ self.assertSequenceEqual(tail, b"")
+ eci = enveloped_data["encryptedContentInfo"]
+ ri = enveloped_data["recipientInfos"][0]
+ encrypted_key, tail = GostR3410KeyTransport().decode(
+ bytes(ri["ktri"]["encryptedKey"])
+ )
+ self.assertSequenceEqual(tail, b"")
+ ukm = bytes(encrypted_key["transportParameters"]["ukm"])
+ spk = bytes(encrypted_key["transportParameters"]["ephemeralPublicKey"]["subjectPublicKey"])
+ pub_key_their, tail = OctetString().decode(spk)
+ self.assertSequenceEqual(tail, b"")
+ curve = GOST3410Curve(*CURVE_PARAMS[curve_name])
+ kek = keker(curve, prv_key_our, bytes(pub_key_their), ukm)
+ key_wrapped = bytes(encrypted_key["sessionEncryptedKey"]["encryptedKey"])
+ mac = bytes(encrypted_key["sessionEncryptedKey"]["macKey"])
+ cek = unwrap_cryptopro(kek, ukm + key_wrapped + mac, sbox=sbox)
+ ciphertext = bytes(eci["encryptedContent"])
+ encryption_params, tail = Gost2814789Parameters().decode(
+ bytes(eci["contentEncryptionAlgorithm"]["parameters"])
+ )
+ self.assertSequenceEqual(tail, b"")
+ iv = bytes(encryption_params["iv"])
+ self.assertSequenceEqual(
+ cfb_decrypt(cek, ciphertext, iv, sbox=sbox, mesh=True),
+ plaintext_expected,
+ )
+
+ def test_256(self):
+ content_info_raw = b64decode("""
+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
+ """)
+ prv_key_our = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1]
+
+ def keker(curve, prv, pub, ukm):
+ return kek_34102012256(
+ curve,
+ prv_unmarshal(prv),
+ pub_unmarshal(pub),
+ ukm_unmarshal(ukm),
+ mode=2001,
+ )
+
+ self.process_cms(
+ content_info_raw,
+ prv_key_our,
+ "GostR3410_2001_CryptoPro_XchA_ParamSet",
+ keker,
+ b"Test data to encrypt.\n" * 100,
+ )
+
+ def test_512(self):
+ content_info_raw = b64decode("""
+MIIB0gYJKoZIhvcNAQcDoIIBwzCCAb8CAQAxggF8MIIBeAIBADBbMFYxKTAnBgkq
+hkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBH
+b3N0UjM0MTAtMjAxMiA1MTIgYml0cyBleGNoYW5nZQIBATAhBggqhQMHAQEBAjAV
+BgkqhQMHAQIBAgIGCCqFAwcBAQIDBIHyMIHvMCgEIIsYzbVLn33aLinQ7SLNA7y+
+Lrm02khqDCfXrNS9iiMhBATerS8zoIHCBgkqhQMHAQIFAQGggaowIQYIKoUDBwEB
+AQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYAYiTVLKpSGaAvjJEDQ0hdK
+qR/jek5Q9Q2pXC+NkOimQh7dpCi+wcaHlPcBk96hmpnOFvLaiokX8V6jqtBl5gdk
+M40kOXv8kcDdTzEVKA/ZLxA8xanL+gTD6ZjaPsUu06nsA2MoMBWcHLUzueaP3bGT
+/yHTV+Za5xdcQehag/lNBgQIvCw4uUl0XC4wOgYJKoZIhvcNAQcBMB8GBiqFAwIC
+FTAVBAj+1QzaXaN9FwYJKoUDBwECBQEBgAyK54euw0sHhEVEkA0=
+ """)
+ prv_key_our = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1]
+
+ def keker(curve, prv, pub, ukm):
+ return kek_34102012256(
+ curve,
+ prv_unmarshal(prv),
+ pub_unmarshal(pub, mode=2012),
+ ukm_unmarshal(ukm),
+ )
+
+ self.process_cms(
+ content_info_raw,
+ prv_key_our,
+ "GostR3410_2012_TC26_ParamSetB",
+ keker,
+ b"Test message",
+ )
+
+
+@skipIf(not pyderasn_exists, "PyDERASN dependency is required")
+class TestEnvelopedKARI(TestCase):
+ """EnvelopedData KeyAgreeRecipientInfo-based test vectors from
+ "Использование алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10
+ в криптографических сообщениях формата CMS" (TK26CMS.pdf)
+ """
+
+ def process_cms(
+ self,
+ content_info_raw,
+ prv_key_our,
+ curve_name,
+ keker,
+ plaintext_expected,
+ ):
+ sbox = "Gost28147_tc26_ParamZ"
+ content_info, tail = ContentInfo().decode(content_info_raw)
+ self.assertSequenceEqual(tail, b"")
+ enveloped_data, tail = EnvelopedData().decode(bytes(content_info["content"]))
+ self.assertSequenceEqual(tail, b"")
+ eci = enveloped_data["encryptedContentInfo"]
+ kari = enveloped_data["recipientInfos"][0]["kari"]
+ pub_key_their, tail = OctetString().decode(
+ bytes(kari["originator"]["originatorKey"]["publicKey"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ ukm = bytes(kari["ukm"])
+ rek = kari["recipientEncryptedKeys"][0]
+ curve = GOST3410Curve(*CURVE_PARAMS[curve_name])
+ kek = keker(curve, prv_key_our, bytes(pub_key_their), ukm)
+ encrypted_key, tail = Gost2814789EncryptedKey().decode(
+ bytes(rek["encryptedKey"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ key_wrapped = bytes(encrypted_key["encryptedKey"])
+ mac = bytes(encrypted_key["macKey"])
+ cek = unwrap_gost(kek, ukm + key_wrapped + mac, sbox=sbox)
+ ciphertext = bytes(eci["encryptedContent"])
+ encryption_params, tail = Gost2814789Parameters().decode(
+ bytes(eci["contentEncryptionAlgorithm"]["parameters"])
+ )
+ self.assertSequenceEqual(tail, b"")
+ iv = bytes(encryption_params["iv"])
+ self.assertSequenceEqual(
+ cfb_decrypt(cek, ciphertext, iv, sbox=sbox, mesh=True),
+ plaintext_expected,
+ )
+
+ def test_256(self):
+ content_info_raw = b64decode("""
+MIIBhgYJKoZIhvcNAQcDoIIBdzCCAXMCAQIxggEwoYIBLAIBA6BooWYwHwYIKoUD
+BwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEBAgIDQwAEQPAdWM4pO38iZ49UjaXQpq+a
+jhTa4KwY4B9TFMK7AiYmbFKE0eX/wvu69kFMQ2o3OJTnMOlr1WHiPYOmNO6C5hOh
+CgQIX+vNomZakEIwIgYIKoUDBwEBAQEwFgYHKoUDAgINADALBgkqhQMHAQIFAQEw
+gYwwgYkwWzBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxl
+LmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgMjU2IGJpdHMgZXhjaGFuZ2UC
+AQEEKjAoBCCNhrZOr7x2fsjjQAeDMv/tSoNRQSSQzzxgqdnYxJ3fIAQEgYLqVDA6
+BgkqhkiG9w0BBwEwHwYGKoUDAgIVMBUECHVmR/S+hlYiBgkqhQMHAQIFAQGADEI9
+UNjyuY+54uVcHw==
+ """)
+ prv_key_our = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1]
+
+ def keker(curve, prv, pub, ukm):
+ return kek_34102012256(
+ curve,
+ prv_unmarshal(prv),
+ pub_unmarshal(pub),
+ ukm_unmarshal(ukm),
+ mode=2001,
+ )
+
+ self.process_cms(
+ content_info_raw,
+ prv_key_our,
+ "GostR3410_2001_CryptoPro_XchA_ParamSet",
+ keker,
+ b"Test message",
+ )
+
+ def test_512(self):
+ content_info_raw = b64decode("""
+MIIBzAYJKoZIhvcNAQcDoIIBvTCCAbkCAQIxggF2oYIBcgIBA6CBraGBqjAhBggq
+hQMHAQEBAjAVBgkqhQMHAQIBAgIGCCqFAwcBAQIDA4GEAASBgCB0nQy/Ljva/mRj
+w6o+eDKIvnxwYIQB5XCHhZhCpHNZiWcFxFpYXZLWRPKifOxV7NStvqGE1+fkfhBe
+btkQu0tdC1XL3LO2Cp/jX16XhW/IP5rKV84qWr1Owy/6tnSsNRb+ez6IttwVvaVV
+pA6ONFy9p9gawoC8nitvAVJkWW0PoQoECDVfxzxgMTAHMCIGCCqFAwcBAQECMBYG
+ByqFAwICDQAwCwYJKoUDBwECBQEBMIGMMIGJMFswVjEpMCcGCSqGSIb3DQEJARYa
+R29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RSMzQxMC0y
+MDEyIDUxMiBiaXRzIGV4Y2hhbmdlAgEBBCowKAQg8C/OcxRR0Uq8nDjHrQlayFb3
+WFUZEnEuAKcuG6dTOawEBLhi9hIwOgYJKoZIhvcNAQcBMB8GBiqFAwICFTAVBAiD
+1wH+CX6CwgYJKoUDBwECBQEBgAzUvQI4H2zRfgNgdlY=
+ """)
+ prv_key_our = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1]
+
+ def keker(curve, prv, pub, ukm):
+ return kek_34102012256(
+ curve,
+ prv_unmarshal(prv),
+ pub_unmarshal(pub, mode=2012),
+ ukm_unmarshal(ukm),
+ )
+
+ self.process_cms(
+ content_info_raw,
+ prv_key_our,
+ "GostR3410_2012_TC26_ParamSetB",
+ keker,
+ b"Test message",
+ )
+++ /dev/null
-# coding: utf-8
-
-from base64 import b64decode
-from unittest import skipIf
-from unittest import TestCase
-
-from pygost.gost28147 import cfb_decrypt
-from pygost.gost3410 import CURVE_PARAMS
-from pygost.gost3410 import GOST3410Curve
-from pygost.gost3410 import prv_unmarshal
-from pygost.gost3410 import pub_unmarshal
-from pygost.gost3410_vko import kek_34102012256
-from pygost.gost3410_vko import ukm_unmarshal
-from pygost.utils import hexdec
-from pygost.wrap import unwrap_cryptopro
-
-try:
-
- from pyderasn import Any
- from pyderasn import BitString
- from pyderasn import Choice
- from pyderasn import Integer
- from pyderasn import ObjectIdentifier
- from pyderasn import OctetString
- from pyderasn import Sequence
- from pyderasn import SetOf
- from pyderasn import tag_ctxc
- from pyderasn import tag_ctxp
-
- class CMSVersion(Integer):
- pass
-
- class ContentType(ObjectIdentifier):
- pass
-
- class RecipientIdentifier(Choice):
- schema = (
- ("issuerAndSerialNumber", Any()),
- # ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))),
- )
-
- class AlgorithmIdentifier(Sequence):
- schema = (
- ("algorithm", ObjectIdentifier()),
- ("parameters", Any(optional=True)),
- )
-
- class KeyEncryptionAlgorithmIdentifier(AlgorithmIdentifier):
- pass
-
- class EncryptedKey(OctetString):
- pass
-
- class KeyTransRecipientInfo(Sequence):
- schema = (
- ("version", CMSVersion()),
- ("rid", RecipientIdentifier()),
- ("keyEncryptionAlgorithm", KeyEncryptionAlgorithmIdentifier()),
- ("encryptedKey", EncryptedKey()),
- )
-
- class RecipientInfo(Choice):
- schema = (
- ("ktri", KeyTransRecipientInfo()),
- # ("kari", KeyAgreeRecipientInfo(impl=tag_ctxc(1))),
- # ("kekri", KEKRecipientInfo(impl=tag_ctxc(2))),
- # ("pwri", PasswordRecipientInfo(impl=tag_ctxc(3))),
- # ("ori", OtherRecipientInfo(impl=tag_ctxc(4))),
- )
-
- class RecipientInfos(SetOf):
- schema = RecipientInfo()
- bounds = (1, float("+inf"))
-
- class ContentEncryptionAlgorithmIdentifier(AlgorithmIdentifier):
- pass
-
- class EncryptedContent(OctetString):
- pass
-
- class EncryptedContentInfo(Sequence):
- schema = (
- ("contentType", ContentType()),
- ("contentEncryptionAlgorithm", ContentEncryptionAlgorithmIdentifier()),
- ("encryptedContent", EncryptedContent(impl=tag_ctxp(0), optional=True)),
- )
-
- class EnvelopedData(Sequence):
- schema = (
- ("version", CMSVersion()),
- # ("originatorInfo", OriginatorInfo(impl=tag_ctxc(0), optional=True)),
- ("recipientInfos", RecipientInfos()),
- ("encryptedContentInfo", EncryptedContentInfo()),
- # ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)),
- )
-
- class ContentInfo(Sequence):
- schema = (
- ("contentType", ContentType()),
- ("content", Any(expl=tag_ctxc(0))),
- )
-
- class Gost2814789IV(OctetString):
- bounds = (8, 8)
-
- class Gost2814789Parameters(Sequence):
- schema = (
- ("iv", Gost2814789IV()),
- ("encryptionParamSet", ObjectIdentifier()),
- )
-
- class Gost2814789Key(OctetString):
- bounds = (32, 32)
-
- class Gost2814789MAC(OctetString):
- bounds = (4, 4)
-
- class Gost2814789EncryptedKey(Sequence):
- schema = (
- ("encryptedKey", Gost2814789Key()),
- ("maskKey", Gost2814789Key(impl=tag_ctxp(0), optional=True)),
- ("macKey", Gost2814789MAC()),
- )
-
- class SubjectPublicKeyInfo(Sequence):
- schema = (
- ("algorithm", AlgorithmIdentifier()),
- ("subjectPublicKey", BitString()),
- )
-
- class GostR34102001TransportParameters(Sequence):
- schema = (
- ("encryptionParamSet", ObjectIdentifier()),
- ("ephemeralPublicKey", SubjectPublicKeyInfo(
- impl=tag_ctxc(0),
- optional=True,
- )),
- ("ukm", OctetString()),
- )
-
- class GostR3410KeyTransport(Sequence):
- schema = (
- ("sessionEncryptedKey", Gost2814789EncryptedKey()),
- ("transportParameters", GostR34102001TransportParameters(
- impl=tag_ctxc(0),
- optional=True,
- )),
- )
-
-except ImportError:
- pyderasn_exists = False
-else:
- pyderasn_exists = True
-
-
-@skipIf(not pyderasn_exists, "PyDERASN dependency is required")
-class TestCMSEnveloped(TestCase):
- """KeyTransRecipientInfo-based test vectors from "Использование
- алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 в
- криптографических сообщениях формата CMS" (TK26CMS.pdf)
-
- `PyDERASN <http://pyderasn.cypherpunks.ru/>`__ library is required
- for CMS parsing.
- """
-
- def process_cms(
- self,
- content_info_raw,
- prv_key_our,
- curve_name,
- keker,
- plaintext_expected,
- ):
- sbox = "Gost28147_tc26_ParamZ"
- content_info, _ = ContentInfo().decode(content_info_raw)
- enveloped_data, _ = EnvelopedData().decode(bytes(content_info["content"]))
- eci = enveloped_data["encryptedContentInfo"]
- ri = enveloped_data["recipientInfos"][0]
- encrypted_key, _ = GostR3410KeyTransport().decode(
- bytes(ri["ktri"]["encryptedKey"])
- )
- ukm = bytes(encrypted_key["transportParameters"]["ukm"])
- spk = bytes(encrypted_key["transportParameters"]["ephemeralPublicKey"]["subjectPublicKey"])
- pub_key_their, _ = OctetString().decode(spk)
- curve = GOST3410Curve(*CURVE_PARAMS[curve_name])
- kek = keker(curve, prv_key_our, bytes(pub_key_their), ukm)
- key_wrapped = bytes(encrypted_key["sessionEncryptedKey"]["encryptedKey"])
- mac = bytes(encrypted_key["sessionEncryptedKey"]["macKey"])
- cek = unwrap_cryptopro(kek, ukm + key_wrapped + mac, sbox=sbox)
- ciphertext = bytes(eci["encryptedContent"])
- encryption_params, _ = Gost2814789Parameters().decode(
- bytes(eci["contentEncryptionAlgorithm"]["parameters"])
- )
- iv = bytes(encryption_params["iv"])
- self.assertSequenceEqual(
- cfb_decrypt(cek, ciphertext, iv, sbox=sbox, mesh=True),
- plaintext_expected,
- )
-
- def test_256(self):
- content_info_raw = b64decode("""
-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
- """)
- prv_key_our = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1]
-
- def keker(curve, prv, pub, ukm):
- return kek_34102012256(
- curve,
- prv_unmarshal(prv),
- pub_unmarshal(pub),
- ukm_unmarshal(ukm),
- mode=2001,
- )
-
- self.process_cms(
- content_info_raw,
- prv_key_our,
- "GostR3410_2001_CryptoPro_XchA_ParamSet",
- keker,
- b"Test data to encrypt.\n" * 100,
- )
-
- def test_512(self):
- content_info_raw = b64decode("""
-MIIB0gYJKoZIhvcNAQcDoIIBwzCCAb8CAQAxggF8MIIBeAIBADBbMFYxKTAnBgkq
-hkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBH
-b3N0UjM0MTAtMjAxMiA1MTIgYml0cyBleGNoYW5nZQIBATAhBggqhQMHAQEBAjAV
-BgkqhQMHAQIBAgIGCCqFAwcBAQIDBIHyMIHvMCgEIIsYzbVLn33aLinQ7SLNA7y+
-Lrm02khqDCfXrNS9iiMhBATerS8zoIHCBgkqhQMHAQIFAQGggaowIQYIKoUDBwEB
-AQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYAYiTVLKpSGaAvjJEDQ0hdK
-qR/jek5Q9Q2pXC+NkOimQh7dpCi+wcaHlPcBk96hmpnOFvLaiokX8V6jqtBl5gdk
-M40kOXv8kcDdTzEVKA/ZLxA8xanL+gTD6ZjaPsUu06nsA2MoMBWcHLUzueaP3bGT
-/yHTV+Za5xdcQehag/lNBgQIvCw4uUl0XC4wOgYJKoZIhvcNAQcBMB8GBiqFAwIC
-FTAVBAj+1QzaXaN9FwYJKoUDBwECBQEBgAyK54euw0sHhEVEkA0=
- """)
- prv_key_our = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1]
-
- def keker(curve, prv, pub, ukm):
- return kek_34102012256(
- curve,
- prv_unmarshal(prv),
- pub_unmarshal(pub, mode=2012),
- ukm_unmarshal(ukm),
- )
-
- self.process_cms(
- content_info_raw,
- prv_key_our,
- "GostR3410_2012_TC26_ParamSetB",
- keker,
- b"Test message",
- )
--- /dev/null
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2018 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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.gost28147 import cfb_decrypt
+from pygost.gost34112012512 import GOST34112012512
+from pygost.gost34112012512 import pbkdf2 as gost34112012_pbkdf2
+
+
+try:
+ from pygost.asn1schemas.cms import Gost2814789Parameters
+ from pygost.asn1schemas.pfx import EncryptedData
+ from pygost.asn1schemas.pfx import OctetStringSafeContents
+ from pygost.asn1schemas.pfx import PBES2Params
+ from pygost.asn1schemas.pfx import PBKDF2Params
+ from pygost.asn1schemas.pfx import PFX
+ from pygost.asn1schemas.pfx import PKCS8ShroudedKeyBag
+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"")
+ octet_string_safe_contents, tail = OctetStringSafeContents().decode(
+ bytes(pfx["authSafe"]["content"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ outer_safe_contents = octet_string_safe_contents["safeContents"]
+
+ octet_string_safe_contents, tail = OctetStringSafeContents().decode(
+ bytes(outer_safe_contents[0]["bagValue"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ safe_bag = octet_string_safe_contents["safeContents"][0]
+ shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode(
+ bytes(safe_bag["bagValue"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ pbes2_params, tail = PBES2Params().decode(
+ bytes(shrouded_key_bag["encryptionAlgorithm"]["parameters"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ pbkdf2_params, tail = PBKDF2Params().decode(
+ bytes(pbes2_params["keyDerivationFunc"]["parameters"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ enc_scheme_params, tail = Gost2814789Parameters().decode(
+ bytes(pbes2_params["encryptionScheme"]["parameters"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+
+ 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="Gost28147_tc26_ParamZ",
+ ),
+ 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"")
+ octet_string_safe_contents, tail = OctetStringSafeContents().decode(
+ bytes(pfx["authSafe"]["content"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ outer_safe_contents = octet_string_safe_contents["safeContents"]
+
+ encrypted_data, tail = EncryptedData().decode(
+ bytes(outer_safe_contents[1]["bagValue"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ pbes2_params, _ = PBES2Params().decode(
+ bytes(encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ pbkdf2_params, tail = PBKDF2Params().decode(
+ bytes(pbes2_params["keyDerivationFunc"]["parameters"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ enc_scheme_params, tail = Gost2814789Parameters().decode(
+ bytes(pbes2_params["encryptionScheme"]["parameters"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ 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="Gost28147_tc26_ParamZ",
+ ),
+ cert_bag_expected,
+ )
+
+ def test_mac(self):
+ pfx, tail = PFX().decode(self.pfx_raw)
+ self.assertSequenceEqual(tail, b"")
+ octet_string_safe_contents, tail = OctetStringSafeContents().decode(
+ bytes(pfx["authSafe"]["content"]),
+ )
+ self.assertSequenceEqual(tail, b"")
+ outer_safe_contents = octet_string_safe_contents["safeContents"]
+
+ 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=outer_safe_contents.encode(),
+ digestmod=GOST34112012512,
+ ).digest(),
+ bytes(mac_data["mac"]["digest"]),
+ )
--- /dev/null
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2018 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 unittest import skipIf
+from unittest import TestCase
+
+from pygost.gost3410 import CURVE_PARAMS
+from pygost.gost3410 import GOST3410Curve
+from pygost.gost3410 import prv_unmarshal
+from pygost.gost3410 import pub_unmarshal
+from pygost.gost3410 import public_key
+from pygost.gost3410 import verify
+from pygost.gost34112012256 import GOST34112012256
+from pygost.gost34112012512 import GOST34112012512
+from pygost.utils import hexdec
+
+try:
+ from pyderasn import OctetString
+
+ from pygost.asn1schemas.x509 import Certificate
+except ImportError:
+ pyderasn_exists = False
+else:
+ pyderasn_exists = True
+
+
+@skipIf(not pyderasn_exists, "PyDERASN dependency is required")
+class TestCertificate(TestCase):
+ """Certificate test vectors from "Использования алгоритмов ГОСТ Р
+ 34.10, ГОСТ Р 34.11 в профиле сертификата и списке отзыва
+ сертификатов (CRL) инфраструктуры открытых ключей X.509"
+ (TK26IOK.pdf)
+ """
+
+ def process_cert(self, curve_name, mode, hasher, prv_key_raw, cert_raw):
+ cert, tail = Certificate().decode(cert_raw)
+ self.assertSequenceEqual(tail, b"")
+ curve = GOST3410Curve(*CURVE_PARAMS[curve_name])
+ prv_key = prv_unmarshal(prv_key_raw)
+ pub_key_raw, tail = OctetString().decode(
+ bytes(cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"])
+ )
+ pub_key = pub_unmarshal(bytes(pub_key_raw), mode=mode)
+ self.assertSequenceEqual(tail, b"")
+ self.assertSequenceEqual(pub_key, public_key(curve, prv_key))
+ self.assertTrue(verify(
+ curve,
+ pub_key,
+ hasher(cert["tbsCertificate"].encode()).digest()[::-1],
+ bytes(cert["signatureValue"]),
+ mode=mode,
+ ))
+
+ def test_256(self):
+ cert_raw = b64decode("""
+MIICYjCCAg+gAwIBAgIBATAKBggqhQMHAQEDAjBWMSkwJwYJKoZIhvcNAQkBFhpH
+b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw
+MTIgKDI1NiBiaXQpIGV4YW1wbGUwHhcNMTMxMTA1MTQwMjM3WhcNMzAxMTAxMTQw
+MjM3WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv
+bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDI1NiBiaXQpIGV4YW1wbGUwZjAf
+BggqhQMHAQEBATATBgcqhQMCAiQABggqhQMHAQECAgNDAARAut/Qw1MUq9KPqkdH
+C2xAF3K7TugHfo9n525D2s5mFZdD5pwf90/i4vF0mFmr9nfRwMYP4o0Pg1mOn5Rl
+aXNYraOBwDCBvTAdBgNVHQ4EFgQU1fIeN1HaPbw+XWUzbkJ+kHJUT0AwCwYDVR0P
+BAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYDVR0BBHcwdYAU1fIeN1HaPbw+XWUz
+bkJ+kHJUT0ChWqRYMFYxKTAnBgkqhkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4
+YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0MTAtMjAxMiAoMjU2IGJpdCkgZXhh
+bXBsZYIBATAKBggqhQMHAQEDAgNBAF5bm4BbARR6hJLEoWJkOsYV3Hd7kXQQjz3C
+dqQfmHrz6TI6Xojdh/t8ckODv/587NS5/6KsM77vc6Wh90NAT2s=
+ """)
+ prv_key_raw = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1]
+ self.process_cert(
+ "GostR3410_2001_CryptoPro_XchA_ParamSet",
+ 2001,
+ GOST34112012256,
+ prv_key_raw,
+ cert_raw,
+ )
+
+ def test_512(self):
+ cert_raw = b64decode("""
+MIIC6DCCAlSgAwIBAgIBATAKBggqhQMHAQEDAzBWMSkwJwYJKoZIhvcNAQkBFhpH
+b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw
+MTIgKDUxMiBiaXQpIGV4YW1wbGUwHhcNMTMxMDA0MDczNjA0WhcNMzAxMDAxMDcz
+NjA0WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv
+bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDUxMiBiaXQpIGV4YW1wbGUwgaow
+IQYIKoUDBwEBAQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYATGQ9VCiM5
+FRGCQ8MEz2F1dANqhaEuywa8CbxOnTvaGJpFQVXQwkwvLFAKh7hk542vOEtxpKtT
+CXfGf84nRhMH/Q9bZeAc2eO/yhxrsQhTBufa1Fuou2oe/jUOaG6RAtUUvRzhNTpp
+RGGl1+EIY2vzzUua9j9Ol/gAoy/LNKQIfqOBwDCBvTAdBgNVHQ4EFgQUPcbTRXJZ
+nHtjj+eBP7b5lcTMekIwCwYDVR0PBAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYD
+VR0BBHcwdYAUPcbTRXJZnHtjj+eBP7b5lcTMekKhWqRYMFYxKTAnBgkqhkiG9w0B
+CQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0
+MTAtMjAxMiAoNTEyIGJpdCkgZXhhbXBsZYIBATAKBggqhQMHAQEDAwOBgQBObS7o
+ppPTXzHyVR1DtPa8b57nudJzI4czhsfeX5HDntOq45t9B/qSs8dC6eGxbhHZ9zCO
+SFtxWYdmg0au8XI9Xb8vTC1qdwWID7FFjMWDNQZb6lYh/J+8F2xKylvB5nIlRZqO
+o3eUNFkNyHJwQCk2WoOlO16zwGk2tdKH4KmD5w==
+ """)
+ prv_key_raw = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1]
+ self.process_cert(
+ "GostR3410_2012_TC26_ParamSetB",
+ 2012,
+ GOST34112012512,
+ prv_key_raw,
+ cert_raw,
+ )
"Topic :: Security :: Cryptography",
"Topic :: Software Development :: Libraries :: Python Modules",
],
- packages=["pygost"],
+ packages=["pygost", "pygost.asn1schemas"],
package_data={
"pygost": ["stubs/**/*.pyi"],
},