]> Cypherpunks.ru repositories - pygost.git/commitdiff
More test vectors and ASN.1 related structures 4.4
authorSergey Matveev <stargrave@stargrave.org>
Tue, 4 Feb 2020 13:44:56 +0000 (16:44 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 4 Feb 2020 13:58:12 +0000 (16:58 +0300)
VERSION
install.texi
news.texi
pygost/__init__.py
pygost/asn1schemas/cert-selfsigned-example.py
pygost/asn1schemas/oids.py
pygost/asn1schemas/pkcs10.py [new file with mode: 0644]
pygost/asn1schemas/x509.py
pygost/gost3410.py
pygost/test_x509.py

diff --git a/VERSION b/VERSION
index 69df05f33b7e980f3528fbee240360b759b79dfa..515be8f918de9d7addeeccda132a1db7b29afc14 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.3
+4.4
index c022ba13ea23c794bcfe428e49fcecd0ccf87a5a..01a6873a1413addcb2d8f2c83e1322086498cc0a 100644 (file)
@@ -1,7 +1,7 @@
 @node Download
 @unnumbered Download
 
 @node Download
 @unnumbered Download
 
-@set VERSION 4.3
+@set VERSION 4.4
 
 No additional dependencies except Python 2.7/3.x interpreter are required.
 
 
 No additional dependencies except Python 2.7/3.x interpreter are required.
 
index 384258a3513f3db3effd33fe53d57efd056ef624..9d173f994b1bf657bfea9931f222d3cf383bd003 100644 (file)
--- a/news.texi
+++ b/news.texi
@@ -3,6 +3,15 @@
 
 @table @strong
 
 
 @table @strong
 
+@anchor{Release 4.4}
+@item 4.4
+    @itemize
+    @item @code{id-tc26-gost-3410-2012-512-paramSetTest} curve
+    @item Simple FAQ
+    @item More test vectors for 34.10-2012
+    @item More X.509, PKCS #10 and corresponding ASN.1 helper structures
+    @end itemize
+
 @anchor{Release 4.3}
 @item 4.3
 Dummy release with fixed @code{pygost.__version__}.
 @anchor{Release 4.3}
 @item 4.3
 Dummy release with fixed @code{pygost.__version__}.
index 306746ec75fff13d9e141eb61f06f8769c6b95d2..17c35b67ddacb107b62450bd6e3b031f28c7f496 100644 (file)
@@ -3,4 +3,4 @@
 PyGOST is free software: see the file COPYING for copying conditions.
 """
 
 PyGOST is free software: see the file COPYING for copying conditions.
 """
 
-__version__ = "4.3"
+__version__ = "4.4"
index 5cdca8f5cfa4c961e67af56a623259d81cf4a522..10ec5d29de3eeb8c620d39786336dd22b9875e2c 100644 (file)
@@ -7,17 +7,16 @@ from datetime import timedelta
 from os import urandom
 from sys import argv
 from sys import exit as sys_exit
 from os import urandom
 from sys import argv
 from sys import exit as sys_exit
-from sys import stderr
 from textwrap import fill
 
 from pyderasn import Any
 from pyderasn import BitString
 from pyderasn import Integer
 from textwrap import fill
 
 from pyderasn import Any
 from pyderasn import BitString
 from pyderasn import Integer
-from pyderasn import ObjectIdentifier
 from pyderasn import OctetString
 from pyderasn import PrintableString
 from pyderasn import UTCTime
 
 from pyderasn import OctetString
 from pyderasn import PrintableString
 from pyderasn import UTCTime
 
+from pygost.asn1schemas.oids import id_at_commonName
 from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA
 from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA
@@ -52,12 +51,13 @@ from pygost.gost3410 import sign
 from pygost.gost34112012512 import GOST34112012512
 
 if len(argv) != 2:
 from pygost.gost34112012512 import GOST34112012512
 
 if len(argv) != 2:
-    print("Usage: cert-selfsigned-example.py COMMON-NAME", file=stderr)
-    sys_exit(1)
+    sys_exit("Usage: cert-selfsigned-example.py COMMON-NAME")
+
 
 def pem(obj):
     return fill(standard_b64encode(obj.encode()).decode('ascii'), 64)
 
 
 def pem(obj):
     return fill(standard_b64encode(obj.encode()).decode('ascii'), 64)
 
+
 key_params = GostR34102012PublicKeyParameters((
     ("publicKeyParamSet", id_tc26_gost3410_2012_512_paramSetA),
     ("digestParamSet", id_tc26_gost3411_2012_512),
 key_params = GostR34102012PublicKeyParameters((
     ("publicKeyParamSet", id_tc26_gost3410_2012_512_paramSetA),
     ("digestParamSet", id_tc26_gost3411_2012_512),
@@ -78,7 +78,6 @@ print("-----END PRIVATE KEY-----")
 prv = prv_unmarshal(prv_raw)
 curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"]
 pub_raw = pub_marshal(public_key(curve, prv), mode=2012)
 prv = prv_unmarshal(prv_raw)
 curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"]
 pub_raw = pub_marshal(public_key(curve, prv), mode=2012)
-id_at_commonName = ObjectIdentifier("2.5.4.3")
 subj = Name(("rdnSequence", RDNSequence([
     RelativeDistinguishedName((
         AttributeTypeAndValue((
 subj = Name(("rdnSequence", RDNSequence([
     RelativeDistinguishedName((
         AttributeTypeAndValue((
index eff060d891eba5ad07b45e48d096bd6eb009c255..0cb3b8dca41e6da7e74457d1e353df29261005db 100644 (file)
@@ -2,12 +2,12 @@ from pyderasn import ObjectIdentifier
 
 
 id_pkcs7 = ObjectIdentifier("1.2.840.113549.1.7")
 
 
 id_pkcs7 = ObjectIdentifier("1.2.840.113549.1.7")
+id_data = id_pkcs7 + (1,)
 id_signedData = id_pkcs7 + (2,)
 id_envelopedData = id_pkcs7 + (3,)
 id_digestedData = id_pkcs7 + (5,)
 id_encryptedData = id_pkcs7 + (6,)
 
 id_signedData = id_pkcs7 + (2,)
 id_envelopedData = id_pkcs7 + (3,)
 id_digestedData = id_pkcs7 + (5,)
 id_encryptedData = id_pkcs7 + (6,)
 
-id_data = ObjectIdentifier("1.2.840.113549.1.7.1")
 id_tc26_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.1.1")
 id_tc26_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.1.2")
 id_tc26_gost3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.2.2")
 id_tc26_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.1.1")
 id_tc26_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.1.2")
 id_tc26_gost3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.2.2")
@@ -16,14 +16,19 @@ id_tc26_gost3410_2012_256_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.1.1")
 id_tc26_gost3410_2012_256_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.1.2")
 id_tc26_gost3410_2012_256_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.1.3")
 id_tc26_gost3410_2012_256_paramSetD = ObjectIdentifier("1.2.643.7.1.2.1.1.4")
 id_tc26_gost3410_2012_256_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.1.2")
 id_tc26_gost3410_2012_256_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.1.3")
 id_tc26_gost3410_2012_256_paramSetD = ObjectIdentifier("1.2.643.7.1.2.1.1.4")
+id_tc26_gost3410_2012_512_paramSetTest = ObjectIdentifier("1.2.643.7.1.2.1.2.0")
 id_tc26_gost3410_2012_512_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.2.1")
 id_tc26_gost3410_2012_512_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.2.2")
 id_tc26_gost3410_2012_512_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.2.3")
 id_tc26_signwithdigest_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2")
 id_tc26_signwithdigest_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3")
 id_tc26_gost3410_2012_512_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.2.1")
 id_tc26_gost3410_2012_512_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.2.2")
 id_tc26_gost3410_2012_512_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.2.3")
 id_tc26_signwithdigest_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2")
 id_tc26_signwithdigest_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3")
+id_tc26_gost_28147_param_Z = ObjectIdentifier("1.2.643.7.1.2.5.1.1")
 id_Gost28147_89 = ObjectIdentifier("1.2.643.2.2.21")
 id_Gost28147_89 = ObjectIdentifier("1.2.643.2.2.21")
+id_GostR3410_2001_TestParamSet = ObjectIdentifier("1.2.643.2.2.35.0")
 
 id_pbes2 = ObjectIdentifier("1.2.840.113549.1.5.13")
 id_pbkdf2 = ObjectIdentifier("1.2.840.113549.1.5.12")
 
 
 id_pbes2 = ObjectIdentifier("1.2.840.113549.1.5.13")
 id_pbkdf2 = ObjectIdentifier("1.2.840.113549.1.5.12")
 
+id_at_commonName = ObjectIdentifier("2.5.4.3")
+id_ce_basicConstraints = ObjectIdentifier("2.5.29.19")
 id_ce_subjectKeyIdentifier = ObjectIdentifier("2.5.29.14")
 id_ce_subjectKeyIdentifier = ObjectIdentifier("2.5.29.14")
diff --git a/pygost/asn1schemas/pkcs10.py b/pygost/asn1schemas/pkcs10.py
new file mode 100644 (file)
index 0000000..f1f684a
--- /dev/null
@@ -0,0 +1,65 @@
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2020 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/>.
+"""PKCS #10 related structures (**NOT COMPLETE**)
+"""
+
+from pyderasn import Any
+from pyderasn import BitString
+from pyderasn import Integer
+from pyderasn import ObjectIdentifier
+from pyderasn import Sequence
+from pyderasn import SetOf
+from pyderasn import tag_ctxc
+
+from pygost.asn1schemas.x509 import AlgorithmIdentifier
+from pygost.asn1schemas.x509 import Name
+from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
+
+
+class AttributeValue(Any):
+    pass
+
+
+class AttributeValues(SetOf):
+    schema = AttributeValue()
+
+
+class Attribute(Sequence):
+    schema = (
+        ("type", ObjectIdentifier()),
+        ("values", AttributeValues()),
+    )
+
+
+class Attributes(SetOf):
+    schema = Attribute()
+
+
+class CertificationRequestInfo(Sequence):
+    schema = (
+        ("version", Integer(0)),
+        ("subject", Name()),
+        ("subjectPKInfo", SubjectPublicKeyInfo()),
+        ("attributes", Attributes(impl=tag_ctxc(0))),
+    )
+
+
+class CertificationRequest(Sequence):
+    schema = (
+        ("certificationRequestInfo", CertificationRequestInfo()),
+        ("signatureAlgorithm", AlgorithmIdentifier()),
+        ("signature", BitString()),
+    )
index bbeca669c5f5f3412a1ddfa0135c6fb62a1a912a..831f9abc2f248275c22b5e96578886994098fb19 100644 (file)
@@ -112,16 +112,10 @@ class Validity(Sequence):
     )
 
 
     )
 
 
-id_tc26_gost_28147_param_Z = ObjectIdentifier("1.2.643.7.1.2.5.1.1")
-
-
 class GostR34102012PublicKeyParameters(Sequence):
     schema = (
         ("publicKeyParamSet", ObjectIdentifier()),
 class GostR34102012PublicKeyParameters(Sequence):
     schema = (
         ("publicKeyParamSet", ObjectIdentifier()),
-        ("digestParamSet", ObjectIdentifier()),
-        ("encryptionParamSet", ObjectIdentifier(
-            default=id_tc26_gost_28147_param_Z,
-        )),
+        ("digestParamSet", ObjectIdentifier(optional=True)),
     )
 
 
     )
 
 
@@ -144,6 +138,13 @@ class SubjectKeyIdentifier(KeyIdentifier):
     pass
 
 
     pass
 
 
+class BasicConstraints(Sequence):
+    schema = (
+        ('cA', Boolean(default=False)),
+        # ('pathLenConstraint', PathLenConstraint(optional=True)),
+    )
+
+
 class Extension(Sequence):
     schema = (
         ("extnID", ObjectIdentifier()),
 class Extension(Sequence):
     schema = (
         ("extnID", ObjectIdentifier()),
@@ -178,3 +179,28 @@ class Certificate(Sequence):
         ("signatureAlgorithm", AlgorithmIdentifier()),
         ("signatureValue", BitString()),
     )
         ("signatureAlgorithm", AlgorithmIdentifier()),
         ("signatureValue", BitString()),
     )
+
+
+class RevokedCertificates(SequenceOf):
+    # schema = RevokedCertificate()
+    schema = OctetString()  # dummy
+
+
+class TBSCertList(Sequence):
+    schema = (
+        ("version", Version(optional=True)),
+        ("signature", AlgorithmIdentifier()),
+        ("issuer", Name()),
+        ("thisUpdate", Time()),
+        ("nextUpdate", Time(optional=True)),
+        ("revokedCertificates", RevokedCertificates(optional=True)),
+        ("crlExtensions", Extensions(expl=tag_ctxc(0), optional=True)),
+    )
+
+
+class CertificateList(Sequence):
+    schema = (
+        ("tbsCertList", TBSCertList()),
+        ("signatureAlgorithm", AlgorithmIdentifier()),
+        ("signatureValue", BitString()),
+    )
index 1848ef56653d21460f1ccf247d6c22ea8c770e9d..7e92e3422c29b0b5ae569257143196a945836a04 100644 (file)
@@ -168,6 +168,14 @@ CURVES = {
         e=0x01,
         d=bytes2long(hexdec("0605F6B7C183FA81578BC39CFAD518132B9DF62897009AF7E522C32D6DC7BFFB")),
     ),
         e=0x01,
         d=bytes2long(hexdec("0605F6B7C183FA81578BC39CFAD518132B9DF62897009AF7E522C32D6DC7BFFB")),
     ),
+    "id-tc26-gost-3410-2012-512-paramSetTest": GOST3410Curve(
+        p=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DF1D852741AF4704A0458047E80E4546D35B8336FAC224DD81664BBF528BE6373")),
+        q=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DA82F2D7ECB1DBAC719905C5EECC423F1D86E25EDBE23C595D644AAF187E6E6DF")),
+        a=7,
+        b=bytes2long(hexdec("1CFF0806A31116DA29D8CFA54E57EB748BC5F377E49400FDD788B649ECA1AC4361834013B2AD7322480A89CA58E0CF74BC9E540C2ADD6897FAD0A3084F302ADC")),
+        x=bytes2long(hexdec("24D19CC64572EE30F396BF6EBBFD7A6C5213B3B3D7057CC825F91093A68CD762FD60611262CD838DC6B60AA7EEE804E28BC849977FAC33B4B530F1B120248A9A")),
+        y=bytes2long(hexdec("2BB312A43BD2CE6E0D020613C857ACDDCFBF061E91E5F2C3F32447C259F39B2C83AB156D77F1496BF7EB3351E1EE4E43DC1A18B91B24640B6DBB92CB1ADD371E")),
+    ),
     "id-tc26-gost-3410-12-512-paramSetA": GOST3410Curve(
         p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")),
         q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275")),
     "id-tc26-gost-3410-12-512-paramSetA": GOST3410Curve(
         p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")),
         q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275")),
index a892ad4d67dc8e904acd894b12c8f22fad6e7acc..5e6c74d87fd45af602fa30abb6f63f36bc42f6cf 100644 (file)
@@ -20,6 +20,7 @@ from unittest import TestCase
 
 from pygost.gost3410 import CURVES
 from pygost.gost3410 import prv_unmarshal
 
 from pygost.gost3410 import CURVES
 from pygost.gost3410 import prv_unmarshal
+from pygost.gost3410 import pub_marshal
 from pygost.gost3410 import pub_unmarshal
 from pygost.gost3410 import public_key
 from pygost.gost3410 import verify
 from pygost.gost3410 import pub_unmarshal
 from pygost.gost3410 import public_key
 from pygost.gost3410 import verify
@@ -28,11 +29,50 @@ from pygost.gost34112012512 import GOST34112012512
 from pygost.utils import hexdec
 
 try:
 from pygost.utils import hexdec
 
 try:
+
+    from pyderasn import Any
+    from pyderasn import BitString
+    from pyderasn import Boolean
+    from pyderasn import GeneralizedTime
+    from pyderasn import Integer
     from pyderasn import OctetString
     from pyderasn import OctetString
+    from pyderasn import PrintableString
+    from pyderasn import UTCTime
 
 
+    from pygost.asn1schemas.oids import id_at_commonName
+    from pygost.asn1schemas.oids import id_ce_basicConstraints
+    from pygost.asn1schemas.oids import id_GostR3410_2001_TestParamSet
     from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
     from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
+    from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA
     from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
     from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
+    from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetTest
+    from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256
+    from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256
+    from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512
+    from pygost.asn1schemas.pkcs10 import Attributes
+    from pygost.asn1schemas.pkcs10 import CertificationRequest
+    from pygost.asn1schemas.pkcs10 import CertificationRequestInfo
+    from pygost.asn1schemas.x509 import AlgorithmIdentifier
+    from pygost.asn1schemas.x509 import AttributeType
+    from pygost.asn1schemas.x509 import AttributeTypeAndValue
+    from pygost.asn1schemas.x509 import AttributeValue
+    from pygost.asn1schemas.x509 import BasicConstraints
     from pygost.asn1schemas.x509 import Certificate
     from pygost.asn1schemas.x509 import Certificate
+    from pygost.asn1schemas.x509 import CertificateList
+    from pygost.asn1schemas.x509 import CertificateSerialNumber
+    from pygost.asn1schemas.x509 import Extension
+    from pygost.asn1schemas.x509 import Extensions
+    from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters
+    from pygost.asn1schemas.x509 import Name
+    from pygost.asn1schemas.x509 import RDNSequence
+    from pygost.asn1schemas.x509 import RelativeDistinguishedName
+    from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
+    from pygost.asn1schemas.x509 import TBSCertificate
+    from pygost.asn1schemas.x509 import TBSCertList
+    from pygost.asn1schemas.x509 import Time
+    from pygost.asn1schemas.x509 import Validity
+    from pygost.asn1schemas.x509 import Version
+
 except ImportError:
     pyderasn_exists = False
 else:
 except ImportError:
     pyderasn_exists = False
 else:
@@ -137,3 +177,268 @@ o3eUNFkNyHJwQCk2WoOlO16zwGk2tdKH4KmD5w==
             prv_key_raw,
             cert_raw,
         )
             prv_key_raw,
             cert_raw,
         )
+
+
+@skipIf(not pyderasn_exists, "PyDERASN dependency is required")
+class TestRFC4491bis(TestCase):
+    """Test vectors from https://tools.ietf.org/html/draft-deremin-rfc4491-bis-02
+    """
+
+    def _test_vector(
+            self,
+            curve_name,
+            mode,
+            hsh,
+            ai_spki,
+            ai_sign,
+            cert_serial,
+            prv_hex,
+            cr_sign_hex,
+            cr_b64,
+            c_sign_hex,
+            c_b64,
+            crl_sign_hex,
+            crl_b64,
+    ):
+        prv_raw = hexdec(prv_hex)[::-1]
+        prv = prv_unmarshal(prv_raw)
+        curve = CURVES[curve_name]
+        pub = public_key(curve, prv)
+        pub_raw = pub_marshal(pub, mode=mode)
+        subj = Name(("rdnSequence", RDNSequence([
+            RelativeDistinguishedName((
+                AttributeTypeAndValue((
+                    ("type", AttributeType(id_at_commonName)),
+                    ("value", AttributeValue(PrintableString("Example"))),
+                )),
+            ))
+        ])))
+        spki = SubjectPublicKeyInfo((
+            ("algorithm", ai_spki),
+            ("subjectPublicKey", BitString(OctetString(pub_raw).encode())),
+        ))
+
+        # Certification request
+        cri = CertificationRequestInfo((
+            ("version", Integer(0)),
+            ("subject", subj),
+            ("subjectPKInfo", spki),
+            ("attributes", Attributes()),
+        ))
+        sign = hexdec(cr_sign_hex)
+        self.assertTrue(verify(
+            curve,
+            pub,
+            hsh(cri.encode()).digest()[::-1],
+            sign,
+            mode=mode,
+        ))
+        cr = CertificationRequest((
+            ("certificationRequestInfo", cri),
+            ("signatureAlgorithm", ai_sign),
+            ("signature", BitString(sign)),
+        ))
+        self.assertSequenceEqual(cr.encode(), b64decode(cr_b64))
+
+        # Certificate
+        tbs = TBSCertificate((
+            ("version", Version("v3")),
+            ("serialNumber", CertificateSerialNumber(cert_serial)),
+            ("signature", ai_sign),
+            ("issuer", subj),
+            ("validity", Validity((
+                ("notBefore", Time(("utcTime", UTCTime(b"010101000000Z")))),
+                ("notAfter", Time(("generalTime", GeneralizedTime(b"20501231000000Z")))),
+            ))),
+            ("subject", subj),
+            ("subjectPublicKeyInfo", spki),
+            ("extensions", Extensions((
+                Extension((
+                    ("extnID", id_ce_basicConstraints),
+                    ("critical", Boolean(True)),
+                    ("extnValue", OctetString(
+                        BasicConstraints((("cA", Boolean(True)),)).encode()
+                    )),
+                )),
+            ))),
+        ))
+        sign = hexdec(c_sign_hex)
+        self.assertTrue(verify(
+            curve,
+            pub,
+            hsh(tbs.encode()).digest()[::-1],
+            sign,
+            mode=mode,
+        ))
+        cert = Certificate((
+            ("tbsCertificate", tbs),
+            ("signatureAlgorithm", ai_sign),
+            ("signatureValue", BitString(sign)),
+        ))
+        open("/tmp/1", "wb").write(cert.encode())
+        self.assertSequenceEqual(cert.encode(), b64decode(c_b64))
+
+        # CRL
+        tbs = TBSCertList((
+            ("version", Version("v2")),
+            ("signature", ai_sign),
+            ("issuer", subj),
+            ("thisUpdate", Time(("utcTime", UTCTime(b"140101000000Z")))),
+            ("nextUpdate", Time(("utcTime", UTCTime(b"140102000000Z")))),
+        ))
+        sign = hexdec(crl_sign_hex)
+        self.assertTrue(verify(
+            curve,
+            pub,
+            hsh(tbs.encode()).digest()[::-1],
+            sign,
+            mode=mode,
+        ))
+        crl = CertificateList((
+            ("tbsCertList", tbs),
+            ("signatureAlgorithm", ai_sign),
+            ("signatureValue", BitString(sign)),
+        ))
+        self.assertSequenceEqual(crl.encode(), b64decode(crl_b64))
+
+    def test_256_test_paramset(self):
+        self._test_vector(
+            "id-GostR3410-2001-TestParamSet",
+            2001,
+            GOST34112012256,
+            AlgorithmIdentifier((
+                ("algorithm", id_tc26_gost3410_2012_256),
+                ("parameters", Any(
+                    GostR34102012PublicKeyParameters((
+                        ("publicKeyParamSet", id_GostR3410_2001_TestParamSet),
+                        ("digestParamSet", id_tc26_gost3411_2012_256),
+                    ))
+                )),
+            )),
+            AlgorithmIdentifier((
+                ("algorithm", id_tc26_signwithdigest_gost3410_2012_256),
+            )),
+            10,
+            "7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28",
+            "6AAAB38E35D4AAA517940301799122D855484F579F4CBB96D63CDFDF3ACC432A41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493",
+            """
+MIHTMIGBAgEAMBIxEDAOBgNVBAMTB0V4YW1wbGUwZjAfBggqhQMHAQEBATATBgcq
+hQMCAiMABggqhQMHAQECAgNDAARAC9hv5djbiWaPeJtOHbqFhcVQi0XsW1nYkG3b
+cOJJK3/ad/+HGhD73ydm0pPF0WSvuzx7lzpByIXRHXDWibTxJqAAMAoGCCqFAwcB
+AQMCA0EAaqqzjjXUqqUXlAMBeZEi2FVIT1efTLuW1jzf3zrMQypBqijS8asUgoDN
+ntVv7aQZdAU1VKQnZ7g60EP9OdwEkw==
+            """,
+            "4D53F012FE081776507D4D9BB81F00EFDB4EEFD4AB83BAC4BACF735173CFA81C41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493",
+            """
+MIIBLTCB26ADAgECAgEKMAoGCCqFAwcBAQMCMBIxEDAOBgNVBAMTB0V4YW1wbGUw
+IBcNMDEwMTAxMDAwMDAwWhgPMjA1MDEyMzEwMDAwMDBaMBIxEDAOBgNVBAMTB0V4
+YW1wbGUwZjAfBggqhQMHAQEBATATBgcqhQMCAiMABggqhQMHAQECAgNDAARAC9hv
+5djbiWaPeJtOHbqFhcVQi0XsW1nYkG3bcOJJK3/ad/+HGhD73ydm0pPF0WSvuzx7
+lzpByIXRHXDWibTxJqMTMBEwDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNB
+AE1T8BL+CBd2UH1Nm7gfAO/bTu/Uq4O6xLrPc1Fzz6gcQaoo0vGrFIKAzZ7Vb+2k
+GXQFNVSkJ2e4OtBD/TncBJM=
+            """,
+            "42BF392A14D3EBE957AF3E46CB50BF5F4221A003AD3D172753C94A9C37A31D2041AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493",
+            """
+MIGSMEECAQEwCgYIKoUDBwEBAwIwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx
+MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAgNBAEK/OSoU0+vpV68+
+RstQv19CIaADrT0XJ1PJSpw3ox0gQaoo0vGrFIKAzZ7Vb+2kGXQFNVSkJ2e4OtBD
+/TncBJM=
+            """,
+        )
+
+    def test_256a_paramset(self):
+        self._test_vector(
+            "id-tc26-gost-3410-2012-256-paramSetA",
+            2001,
+            GOST34112012256,
+            AlgorithmIdentifier((
+                ("algorithm", id_tc26_gost3410_2012_256),
+                ("parameters", Any(
+                    GostR34102012PublicKeyParameters((
+                        ("publicKeyParamSet", id_tc26_gost3410_2012_256_paramSetA),
+                    ))
+                )),
+            )),
+            AlgorithmIdentifier((
+                ("algorithm", id_tc26_signwithdigest_gost3410_2012_256),
+            )),
+            10,
+            "7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28",
+            "1BDC2A1317679B66232F63EA16FF7C64CCAAB9AD855FC6E18091661DB79D48121D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769",
+            """
+MIHKMHkCAQAwEjEQMA4GA1UEAxMHRXhhbXBsZTBeMBcGCCqFAwcBAQEBMAsGCSqF
+AwcBAgEBAQNDAARAdCeV1L7ohN3yhQ/sA+o/rxhE4B2dpgtkUJOlXibfw5l49ZbP
+TU0MbPHRiUPZRJPRa57AoW1RLS4SfMRpGmMY4qAAMAoGCCqFAwcBAQMCA0EAG9wq
+Exdnm2YjL2PqFv98ZMyqua2FX8bhgJFmHbedSBIdDh2lvjR8bxtSVseurCAK1krH
+em9bOg4Jcxjnrm7naQ==
+            """,
+            "140B4DA9124B09CB0D5CE928EE874273A310129492EC0E29369E3B791248578C1D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769",
+            """
+MIIBJTCB06ADAgECAgEKMAoGCCqFAwcBAQMCMBIxEDAOBgNVBAMTB0V4YW1wbGUw
+IBcNMDEwMTAxMDAwMDAwWhgPMjA1MDEyMzEwMDAwMDBaMBIxEDAOBgNVBAMTB0V4
+YW1wbGUwXjAXBggqhQMHAQEBATALBgkqhQMHAQIBAQEDQwAEQHQnldS+6ITd8oUP
+7APqP68YROAdnaYLZFCTpV4m38OZePWWz01NDGzx0YlD2UST0WuewKFtUS0uEnzE
+aRpjGOKjEzARMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoUDBwEBAwIDQQAUC02pEksJ
+yw1c6Sjuh0JzoxASlJLsDik2njt5EkhXjB0OHaW+NHxvG1JWx66sIArWSsd6b1s6
+DglzGOeubudp
+            """,
+            "14BD68087C3B903C7AA28B07FEB2E7BD6FE0963F563267359F5CD8EAB45059AD1D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769",
+            """
+MIGSMEECAQEwCgYIKoUDBwEBAwIwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx
+MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAgNBABS9aAh8O5A8eqKL
+B/6y571v4JY/VjJnNZ9c2Oq0UFmtHQ4dpb40fG8bUlbHrqwgCtZKx3pvWzoOCXMY
+565u52k=
+            """,
+        )
+
+    def test_512_test_paramset(self):
+        self._test_vector(
+            "id-tc26-gost-3410-2012-512-paramSetTest",
+            2012,
+            GOST34112012512,
+            AlgorithmIdentifier((
+                ("algorithm", id_tc26_gost3410_2012_512),
+                ("parameters", Any(
+                    GostR34102012PublicKeyParameters((
+                        ("publicKeyParamSet", id_tc26_gost3410_2012_512_paramSetTest),
+                    ))
+                )),
+            )),
+            AlgorithmIdentifier((
+                ("algorithm", id_tc26_signwithdigest_gost3410_2012_512),
+            )),
+            11,
+            "0BA6048AADAE241BA40936D47756D7C93091A0E8514669700EE7508E508B102072E8123B2200A0563322DAD2827E2714A2636B7BFD18AADFC62967821FA18DD4",
+            "433B1D6CE40A51F1E5737EB16AA2C683829A405B9D9127E21260FC9D6AC05D87BF24E26C45278A5C2192A75BA94993ABD6074E7FF1BF03FD2F5397AFA1D945582F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36",
+            """
+MIIBTzCBvAIBADASMRAwDgYDVQQDEwdFeGFtcGxlMIGgMBcGCCqFAwcBAQECMAsG
+CSqFAwcBAgECAAOBhAAEgYDh7zDVLGEz3dmdHVxBRVz3302LTJJbvGmvFDPRVlhR
+Wt0hRoUMMlxbgcEzvmVaqMTUQOe5io1ZSHsMdpa8xV0R7L53NqnsNX/y/TmTH04R
+TLjNo1knCsfw5/9D2UGUGeph/Sq3f12fY1I9O1CgT2PioM9Rt8E63CFWDwvUDMnH
+N6AAMAoGCCqFAwcBAQMDA4GBAEM7HWzkClHx5XN+sWqixoOCmkBbnZEn4hJg/J1q
+wF2HvyTibEUnilwhkqdbqUmTq9YHTn/xvwP9L1OXr6HZRVgvhvpgoIEJGiPdeV4e
+PGie5RKjyC7g3MJkPHjuqPys01SSVYSGsg8cnsGXyQaZhQJgyTvLzZxcMxfhk0Th
+c642
+            """,
+            "415703D892F1A5F3F68C4353189A7EE207B80B5631EF9D49529A4D6B542C2CFA15AA2EACF11F470FDE7D954856903C35FD8F955EF300D95C77534A724A0EEE702F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36",
+            """
+MIIBqjCCARagAwIBAgIBCzAKBggqhQMHAQEDAzASMRAwDgYDVQQDEwdFeGFtcGxl
+MCAXDTAxMDEwMTAwMDAwMFoYDzIwNTAxMjMxMDAwMDAwWjASMRAwDgYDVQQDEwdF
+eGFtcGxlMIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAAOBhAAEgYDh7zDVLGEz
+3dmdHVxBRVz3302LTJJbvGmvFDPRVlhRWt0hRoUMMlxbgcEzvmVaqMTUQOe5io1Z
+SHsMdpa8xV0R7L53NqnsNX/y/TmTH04RTLjNo1knCsfw5/9D2UGUGeph/Sq3f12f
+Y1I9O1CgT2PioM9Rt8E63CFWDwvUDMnHN6MTMBEwDwYDVR0TAQH/BAUwAwEB/zAK
+BggqhQMHAQEDAwOBgQBBVwPYkvGl8/aMQ1MYmn7iB7gLVjHvnUlSmk1rVCws+hWq
+LqzxH0cP3n2VSFaQPDX9j5Ve8wDZXHdTSnJKDu5wL4b6YKCBCRoj3XleHjxonuUS
+o8gu4NzCZDx47qj8rNNUklWEhrIPHJ7Bl8kGmYUCYMk7y82cXDMX4ZNE4XOuNg==
+            """,
+            "3A13FB7AECDB5560EEF6137CFC5DD64691732EBFB3690A1FC0C7E8A4EEEA08307D648D4DC0986C46A87B3FBE4C7AF42EA34359C795954CA39FF3ABBED9051F4D2F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36",
+            """
+MIHTMEECAQEwCgYIKoUDBwEBAwMwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx
+MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAwOBgQA6E/t67NtVYO72
+E3z8XdZGkXMuv7NpCh/Ax+ik7uoIMH1kjU3AmGxGqHs/vkx69C6jQ1nHlZVMo5/z
+q77ZBR9NL4b6YKCBCRoj3XleHjxonuUSo8gu4NzCZDx47qj8rNNUklWEhrIPHJ7B
+l8kGmYUCYMk7y82cXDMX4ZNE4XOuNg==
+            """,
+        )