aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--docs/development/custom-vectors/secp256k1.rst32
-rw-r--r--docs/development/custom-vectors/secp256k1/generate_secp256k1.py89
-rw-r--r--docs/development/custom-vectors/secp256k1/verify_secp256k1.py59
-rw-r--r--docs/development/test-vectors.rst27
-rw-r--r--docs/spelling_wordlist.txt1
-rw-r--r--docs/x509.rst100
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py106
-rw-r--r--src/cryptography/hazmat/bindings/openssl/ssl.py6
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509.py5
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509v3.py17
-rw-r--r--src/cryptography/hazmat/primitives/kdf/hkdf.py4
-rw-r--r--src/cryptography/x509.py96
-rw-r--r--tests/test_x509_ext.py502
-rw-r--r--tests/utils.py1
-rw-r--r--vectors/cryptography_vectors/x509/custom/aia_ca_issuers.pem19
-rw-r--r--vectors/cryptography_vectors/x509/custom/aia_ocsp.pem19
-rw-r--r--vectors/cryptography_vectors/x509/custom/aia_ocsp_ca_issuers.pem21
-rw-r--r--vectors/cryptography_vectors/x509/custom/authority_key_identifier.pem19
-rw-r--r--vectors/cryptography_vectors/x509/custom/authority_key_identifier_no_keyid.pem19
-rw-r--r--vectors/cryptography_vectors/x509/custom/extended_key_usage.pem20
-rw-r--r--vectors/cryptography_vectors/x509/custom/inhibit_any_policy_5.pem18
-rw-r--r--vectors/cryptography_vectors/x509/custom/inhibit_any_policy_negative.pem18
23 files changed, 1166 insertions, 34 deletions
diff --git a/.travis.yml b/.travis.yml
index 71efd8ff..c7413ea9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -120,3 +120,5 @@ notifications:
- "irc.freenode.org#cryptography-dev"
use_notice: true
skip_join: true
+ webhooks:
+ - https://buildtimetrend.herokuapp.com/travis
diff --git a/docs/development/custom-vectors/secp256k1.rst b/docs/development/custom-vectors/secp256k1.rst
new file mode 100644
index 00000000..b19bf4e4
--- /dev/null
+++ b/docs/development/custom-vectors/secp256k1.rst
@@ -0,0 +1,32 @@
+SECP256K1 vector creation
+=========================
+
+This page documents the code that was used to generate the SECP256K1 elliptic
+curve test vectors as well as code used to verify them against another
+implementation.
+
+
+Creation
+--------
+
+The vectors are generated using a `pure Python ecdsa`_ implementation. The test
+messages and combinations of algorithms are derived from the NIST vector data.
+
+.. literalinclude:: /development/custom-vectors/secp256k1/generate_secp256k1.py
+
+Download link: :download:`generate_secp256k1.py
+</development/custom-vectors/secp256k1/generate_secp256k1.py>`
+
+
+Verification
+------------
+
+``cryptography`` was modified to support the SECP256K1 curve. Then
+the following python script was run to generate the vector files.
+
+.. literalinclude:: /development/custom-vectors/secp256k1/verify_secp256k1.py
+
+Download link: :download:`verify_secp256k1.py
+</development/custom-vectors/secp256k1/verify_secp256k1.py>`
+
+.. _`pure Python ecdsa`: https://pypi.python.org/pypi/ecdsa
diff --git a/docs/development/custom-vectors/secp256k1/generate_secp256k1.py b/docs/development/custom-vectors/secp256k1/generate_secp256k1.py
new file mode 100644
index 00000000..502a3ff6
--- /dev/null
+++ b/docs/development/custom-vectors/secp256k1/generate_secp256k1.py
@@ -0,0 +1,89 @@
+from __future__ import absolute_import, print_function
+
+import hashlib
+import os
+from binascii import hexlify
+from collections import defaultdict
+
+from ecdsa import SECP256k1, SigningKey
+from ecdsa.util import sigdecode_der, sigencode_der
+
+from cryptography_vectors import open_vector_file
+
+from tests.utils import (
+ load_fips_ecdsa_signing_vectors, load_vectors_from_file
+)
+
+HASHLIB_HASH_TYPES = {
+ "SHA-1": hashlib.sha1,
+ "SHA-224": hashlib.sha224,
+ "SHA-256": hashlib.sha256,
+ "SHA-384": hashlib.sha384,
+ "SHA-512": hashlib.sha512,
+}
+
+
+class TruncatedHash(object):
+ def __init__(self, hasher):
+ self.hasher = hasher
+
+ def __call__(self, data):
+ self.hasher.update(data)
+ return self
+
+ def digest(self):
+ return self.hasher.digest()[:256 // 8]
+
+
+def build_vectors(fips_vectors):
+ vectors = defaultdict(list)
+ for vector in fips_vectors:
+ vectors[vector['digest_algorithm']].append(vector['message'])
+
+ for digest_algorithm, messages in vectors.items():
+ if digest_algorithm not in HASHLIB_HASH_TYPES:
+ continue
+
+ yield ""
+ yield "[K-256,{0}]".format(digest_algorithm)
+ yield ""
+
+ for message in messages:
+ # Make a hash context
+ hash_func = TruncatedHash(HASHLIB_HASH_TYPES[digest_algorithm]())
+
+ # Sign the message using warner/ecdsa
+ secret_key = SigningKey.generate(curve=SECP256k1)
+ public_key = secret_key.get_verifying_key()
+ signature = secret_key.sign(message, hashfunc=hash_func,
+ sigencode=sigencode_der)
+
+ r, s = sigdecode_der(signature, None)
+
+ yield "Msg = {0}".format(hexlify(message))
+ yield "d = {0:x}".format(secret_key.privkey.secret_multiplier)
+ yield "Qx = {0:x}".format(public_key.pubkey.point.x())
+ yield "Qy = {0:x}".format(public_key.pubkey.point.y())
+ yield "R = {0:x}".format(r)
+ yield "S = {0:x}".format(s)
+ yield ""
+
+
+def write_file(lines, dest):
+ for line in lines:
+ print(line)
+ print(line, file=dest)
+
+source_path = os.path.join("asymmetric", "ECDSA", "FIPS_186-3", "SigGen.txt")
+dest_path = os.path.join("asymmetric", "ECDSA", "SECP256K1", "SigGen.txt")
+
+fips_vectors = load_vectors_from_file(
+ source_path,
+ load_fips_ecdsa_signing_vectors
+)
+
+with open_vector_file(dest_path, "w") as dest_file:
+ write_file(
+ build_vectors(fips_vectors),
+ dest_file
+ )
diff --git a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py
new file mode 100644
index 00000000..3d2c25b9
--- /dev/null
+++ b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py
@@ -0,0 +1,59 @@
+from __future__ import absolute_import, print_function
+
+import os
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives.asymmetric.utils import (
+ encode_rfc6979_signature
+)
+
+from tests.utils import (
+ load_fips_ecdsa_signing_vectors, load_vectors_from_file
+)
+
+CRYPTOGRAPHY_HASH_TYPES = {
+ "SHA-1": hashes.SHA1,
+ "SHA-224": hashes.SHA224,
+ "SHA-256": hashes.SHA256,
+ "SHA-384": hashes.SHA384,
+ "SHA-512": hashes.SHA512,
+}
+
+
+def verify_one_vector(vector):
+ digest_algorithm = vector['digest_algorithm']
+ message = vector['message']
+ x = vector['x']
+ y = vector['y']
+ signature = encode_rfc6979_signature(vector['r'], vector['s'])
+
+ numbers = ec.EllipticCurvePublicNumbers(
+ x, y,
+ ec.SECP256K1()
+ )
+
+ key = numbers.public_key(default_backend())
+
+ verifier = key.verifier(
+ signature,
+ ec.ECDSA(CRYPTOGRAPHY_HASH_TYPES[digest_algorithm]())
+ )
+ verifier.update(message)
+ return verifier.verify()
+
+
+def verify_vectors(vectors):
+ for vector in vectors:
+ assert verify_one_vector(vector)
+
+
+vector_path = os.path.join("asymmetric", "ECDSA", "SECP256K1", "SigGen.txt")
+
+secp256k1_vectors = load_vectors_from_file(
+ vector_path,
+ load_fips_ecdsa_signing_vectors
+)
+
+verify_vectors(secp256k1_vectors)
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index 622a9d70..41531f7b 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -37,9 +37,14 @@ Asymmetric ciphers
Ruby test suite.
-Custom Asymmetric Vectors
+Custom asymmetric vectors
~~~~~~~~~~~~~~~~~~~~~~~~~
+.. toctree::
+ :maxdepth: 1
+
+ custom-vectors/secp256k1
+
* ``asymmetric/PEM_Serialization/ec_private_key.pem`` and
``asymmetric/DER_Serialization/ec_private_key.der`` - Contains an Elliptic
Curve key generated by OpenSSL from the curve ``secp256r1``.
@@ -78,6 +83,7 @@ Custom Asymmetric Vectors
``asymmetric/public/PKCS1/rsa.pub.der`` are PKCS1 conversions of the public
key from ``asymmetric/PKCS8/unenc-rsa-pkcs8.pem`` using PEM and DER encoding.
+
Key exchange
~~~~~~~~~~~~
@@ -140,6 +146,8 @@ Custom X.509 Vectors
subject alternative name extension with the ``registeredID`` general name.
* ``all_key_usages.pem`` - An RSA 2048 bit self-signed certificate containing
a key usage extension with all nine purposes set to true.
+* ``extended_key_usage.pem`` - An RSA 2048 bit self-signed certificate
+ containing an extended key usage extension with eight usages.
* ``san_idna_names.pem`` - An RSA 2048 bit self-signed certificate containing
a subject alternative name extension with ``rfc822Name``, ``dNSName``, and
``uniformResourceIdentifier`` general names with IDNA (:rfc:`5895`) encoding.
@@ -154,6 +162,23 @@ Custom X.509 Vectors
subject alternative name extension with an ``iPAddress`` value.
* ``san_dirname.pem`` - An RSA 2048 bit self-signed certificate containing a
subject alternative name extension with a ``directoryName`` value.
+* ``inhibit_any_policy_5.pem`` - An RSA 2048 bit self-signed certificate
+ containing an inhibit any policy extension with the value 5.
+* ``inhibit_any_policy_negative.pem`` - An RSA 2048 bit self-signed certificate
+ containing an inhibit any policy extension with the value -1.
+* ``authority_key_identifier.pem`` - An RSA 2048 bit self-signed certificate
+ containing an authority key identifier extension with key identifier,
+ authority certificate issuer, and authority certificate serial number fields.
+* ``authority_key_identifier_no_keyid.pem`` - An RSA 2048 bit self-signed
+ certificate containing an authority key identifier extension with authority
+ certificate issuer and authority certificate serial number fields.
+* ``aia_ocsp_ca_issuers.pem`` - An RSA 2048 bit self-signed certificate
+ containing an authority information access extension with two OCSP and one
+ CA issuers entry.
+* ``aia_ocsp.pem`` - An RSA 2048 bit self-signed certificate
+ containing an authority information access extension with an OCSP entry.
+* ``aia_ca_issuers.pem`` - An RSA 2048 bit self-signed certificate
+ containing an authority information access extension with a CA issuers entry.
Custom X.509 Request Vectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index b7c4c6c2..badb500c 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -40,6 +40,7 @@ multi
naïve
namespace
namespaces
+online
paddings
pickleable
plaintext
diff --git a/docs/x509.rst b/docs/x509.rst
index 5f36a921..f4ea2a52 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -50,6 +50,42 @@ X.509
-----END CERTIFICATE-----
""".strip()
+ cryptography_cert_pem = b"""
+ -----BEGIN CERTIFICATE-----
+ MIIFvTCCBKWgAwIBAgICPyAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCVVMx
+ FjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlkU1NMIFNIQTI1
+ NiBDQSAtIEczMB4XDTE0MTAxNTEyMDkzMloXDTE4MTExNjAxMTUwM1owgZcxEzAR
+ BgNVBAsTCkdUNDg3NDI5NjUxMTAvBgNVBAsTKFNlZSB3d3cucmFwaWRzc2wuY29t
+ L3Jlc291cmNlcy9jcHMgKGMpMTQxLzAtBgNVBAsTJkRvbWFpbiBDb250cm9sIFZh
+ bGlkYXRlZCAtIFJhcGlkU1NMKFIpMRwwGgYDVQQDExN3d3cuY3J5cHRvZ3JhcGh5
+ LmlvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAom/FebKJIot7Sp3s
+ itG1sicpe3thCssjI+g1JDAS7I3GLVNmbms1DOdIIqwf01gZkzzXBN2+9sOnyRaR
+ PPfCe1jTr3dk2y6rPE559vPa1nZQkhlzlhMhlPyjaT+S7g4Tio4qV2sCBZU01DZJ
+ CaksfohN+5BNVWoJzTbOcrHOEJ+M8B484KlBCiSxqf9cyNQKru4W3bHaCVNVJ8eu
+ 6i6KyhzLa0L7yK3LXwwXVs583C0/vwFhccGWsFODqD/9xHUzsBIshE8HKjdjDi7Y
+ 3BFQzVUQFjBB50NSZfAA/jcdt1blxJouc7z9T8Oklh+V5DDBowgAsrT4b6Z2Fq6/
+ r7D1GqivLK/ypUQmxq2WXWAUBb/Q6xHgxASxI4Br+CByIUQJsm8L2jzc7k+mF4hW
+ ltAIUkbo8fGiVnat0505YJgxWEDKOLc4Gda6d/7GVd5AvKrz242bUqeaWo6e4MTx
+ diku2Ma3rhdcr044Qvfh9hGyjqNjvhWY/I+VRWgihU7JrYvgwFdJqsQ5eiKT4OHi
+ gsejvWwkZzDtiQ+aQTrzM1FsY2swJBJsLSX4ofohlVRlIJCn/ME+XErj553431Lu
+ YQ5SzMd3nXzN78Vj6qzTfMUUY72UoT1/AcFiUMobgIqrrmwuNxfrkbVE2b6Bga74
+ FsJX63prvrJ41kuHK/16RQBM7fcCAwEAAaOCAWAwggFcMB8GA1UdIwQYMBaAFMOc
+ 8/zTRgg0u85Gf6B8W/PiCMtZMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYT
+ aHR0cDovL2d2LnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL2d2LnN5bWNi
+ LmNvbS9ndi5jcnQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+ BggrBgEFBQcDAjAvBgNVHREEKDAmghN3d3cuY3J5cHRvZ3JhcGh5Lmlvgg9jcnlw
+ dG9ncmFwaHkuaW8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2d2LnN5bWNiLmNv
+ bS9ndi5jcmwwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGCmCGSAGG+EUBBzYw
+ LDAqBggrBgEFBQcCARYeaHR0cHM6Ly93d3cucmFwaWRzc2wuY29tL2xlZ2FsMA0G
+ CSqGSIb3DQEBCwUAA4IBAQAzIYO2jx7h17FBT74tJ2zbV9OKqGb7QF8y3wUtP4xc
+ dH80vprI/Cfji8s86kr77aAvAqjDjaVjHn7UzebhSUivvRPmfzRgyWBacomnXTSt
+ Xlt2dp2nDQuwGyK2vB7dMfKnQAkxwq1sYUXznB8i0IhhCAoXp01QGPKq51YoIlnF
+ 7DRMk6iEaL1SJbkIrLsCQyZFDf0xtfW9DqXugMMLoxeCsBhZJQzNyS2ryirrv9LH
+ aK3+6IZjrcyy9bkpz/gzJucyhU+75c4My/mnRCrtItRbCQuiI5pd5poDowm+HH9i
+ GVI9+0lAFwxOUnOnwsoI40iOoxjLMGB+CgFLKCGUcWxP
+ -----END CERTIFICATE-----
+ """.strip()
+
X.509 is an ITU-T standard for a `public key infrastructure`_. X.509v3 is
defined in :rfc:`5280` (which obsoletes :rfc:`2459` and :rfc:`3280`). X.509
certificates are commonly used in protocols like `TLS`_.
@@ -281,6 +317,7 @@ X.509 Certificate Object
>>> for ext in cert.extensions:
... print(ext)
+ <Extension(oid=<ObjectIdentifier(oid=2.5.29.35, name=authorityKeyIdentifier)>, critical=False, value=<AuthorityKeyIdentifier(key_identifier='\xe4}_\xd1\\\x95\x86\x08,\x05\xae\xbeu\xb6e\xa7\xd9]\xa8f', authority_cert_issuer=None, authority_cert_serial_number=None)>)>
<Extension(oid=<ObjectIdentifier(oid=2.5.29.14, name=subjectKeyIdentifier)>, critical=False, value=<SubjectKeyIdentifier(digest='X\x01\x84$\x1b\xbc+R\x94J=\xa5\x10r\x14Q\xf5\xaf:\xc9')>)>
<Extension(oid=<ObjectIdentifier(oid=2.5.29.15, name=keyUsage)>, critical=True, value=<KeyUsage(digital_signature=False, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encipher_only=None, decipher_only=None)>)>
<Extension(oid=<ObjectIdentifier(oid=2.5.29.19, name=basicConstraints)>, critical=True, value=<BasicConstraints(ca=True, path_length=None)>)>
@@ -646,10 +683,10 @@ X.509 Extensions
certificate. This attribute only has meaning if ``ca`` is true.
If ``ca`` is true then a path length of None means there's no
restriction on the number of subordinate CAs in the certificate chain.
- If it is zero or greater then that number defines the maximum length.
- For example, a ``path_length`` of 1 means the certificate can sign a
- subordinate CA, but the subordinate CA is not allowed to create
- subordinates with ``ca`` set to true.
+ If it is zero or greater then it defines the maximum length for a
+ subordinate CA's certificate chain. For example, a ``path_length`` of 1
+ means the certificate can sign a subordinate CA, but the subordinate CA
+ is not allowed to create subordinates with ``ca`` set to true.
.. class:: ExtendedKeyUsage
@@ -718,6 +755,48 @@ X.509 Extensions
:returns: A list of values extracted from the matched general names.
+ .. doctest::
+
+ >>> from cryptography import x509
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> cert = x509.load_pem_x509_certificate(cryptography_cert_pem, default_backend())
+ >>> # Get the subjectAltName extension from the certificate
+ >>> ext = cert.extensions.get_extension_for_oid(x509.OID_SUBJECT_ALTERNATIVE_NAME)
+ >>> # Get the dNSName entries from the SAN extension
+ >>> ext.value.get_values_for_type(x509.DNSName)
+ [u'www.cryptography.io', u'cryptography.io']
+
+
+.. class:: AuthorityInformationAccess
+
+ .. versionadded:: 0.9
+
+ The authority information access extension indicates how to access
+ information and services for the issuer of the certificate in which
+ the extension appears. Information and services may include online
+ validation services (such as OCSP) and issuer data. It is an iterable,
+ containing one or more :class:`AccessDescription` instances.
+
+
+.. class:: AccessDescription
+
+ .. attribute:: access_method
+
+ :type: :class:`ObjectIdentifier`
+
+ The access method defines what the ``access_location`` means. It must
+ be either :data:`OID_OCSP` or :data:`OID_CA_ISSUERS`. If it is
+ :data:`OID_OCSP` the access location will be where to obtain OCSP
+ information for the certificate. If it is :data:`OID_CA_ISSUERS` the
+ access location will provide additional information about the issuing
+ certificate.
+
+ .. attribute:: access_location
+
+ :type: :class:`GeneralName`
+
+ Where to access the information defined by the access method.
Object Identifiers
~~~~~~~~~~~~~~~~~~
@@ -911,6 +990,19 @@ Extended Key Usage OIDs
Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.9"``. This is used to
denote that a certificate may be used for signing OCSP responses.
+Authority Information Access OIDs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. data:: OID_OCSP
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.48.1"``. Used as the
+ identifier for OCSP data in :class:`AccessDescription` objects.
+
+.. data:: OID_CA_ISSUERS
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.48.2"``. Used as the
+ identifier for CA issuer data in :class:`AccessDescription` objects.
+
.. _extension_oids:
Extension OIDs
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 7f633c76..42ca138d 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -15,6 +15,7 @@ from __future__ import absolute_import, division, print_function
import datetime
import ipaddress
+from email.utils import parseaddr
import idna
@@ -107,6 +108,27 @@ def _build_general_name(backend, gn):
return x509.DirectoryName(
_build_x509_name(backend, gn.d.directoryName)
)
+ elif gn.type == backend._lib.GEN_EMAIL:
+ data = backend._ffi.buffer(
+ gn.d.rfc822Name.data, gn.d.rfc822Name.length
+ )[:].decode("ascii")
+ name, address = parseaddr(data)
+ parts = address.split(u"@")
+ if name or len(parts) > 2 or not address:
+ # parseaddr has found a name (e.g. Name <email>) or the split
+ # has found more than 2 parts (which means more than one @ sign)
+ # or the entire value is an empty string.
+ raise ValueError("Invalid rfc822name value")
+ elif len(parts) == 1:
+ # Single label email name. This is valid for local delivery. No
+ # IDNA decoding can be done since there is no domain component.
+ return x509.RFC822Name(address)
+ else:
+ # A normal email of the form user@domain.com. Let's attempt to
+ # decode the domain component and return the entire address.
+ return x509.RFC822Name(
+ parts[0] + u"@" + idna.decode(parts[1])
+ )
else:
# otherName, x400Address or ediPartyName
raise x509.UnsupportedGeneralNameType(
@@ -245,6 +267,12 @@ class _Certificate(object):
value = self._build_key_usage(ext)
elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME:
value = self._build_subject_alt_name(ext)
+ elif oid == x509.OID_EXTENDED_KEY_USAGE:
+ value = self._build_extended_key_usage(ext)
+ elif oid == x509.OID_AUTHORITY_KEY_IDENTIFIER:
+ value = self._build_authority_key_identifier(ext)
+ elif oid == x509.OID_AUTHORITY_INFORMATION_ACCESS:
+ value = self._build_authority_information_access(ext)
elif critical:
raise x509.UnsupportedExtension(
"{0} is not currently supported".format(oid), oid
@@ -297,6 +325,66 @@ class _Certificate(object):
self._backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
)
+ def _build_authority_key_identifier(self, ext):
+ akid = self._backend._lib.X509V3_EXT_d2i(ext)
+ assert akid != self._backend._ffi.NULL
+ akid = self._backend._ffi.cast("AUTHORITY_KEYID *", akid)
+ akid = self._backend._ffi.gc(
+ akid, self._backend._lib.AUTHORITY_KEYID_free
+ )
+ key_identifier = None
+ authority_cert_issuer = None
+ authority_cert_serial_number = None
+
+ if akid.keyid != self._backend._ffi.NULL:
+ key_identifier = self._backend._ffi.buffer(
+ akid.keyid.data, akid.keyid.length
+ )[:]
+
+ if akid.issuer != self._backend._ffi.NULL:
+ authority_cert_issuer = []
+
+ num = self._backend._lib.sk_GENERAL_NAME_num(akid.issuer)
+ for i in range(num):
+ gn = self._backend._lib.sk_GENERAL_NAME_value(akid.issuer, i)
+ assert gn != self._backend._ffi.NULL
+ value = _build_general_name(self._backend, gn)
+
+ authority_cert_issuer.append(value)
+
+ if akid.serial != self._backend._ffi.NULL:
+ bn = self._backend._lib.ASN1_INTEGER_to_BN(
+ akid.serial, self._backend._ffi.NULL
+ )
+ assert bn != self._backend._ffi.NULL
+ bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free)
+ authority_cert_serial_number = self._backend._bn_to_int(bn)
+
+ return x509.AuthorityKeyIdentifier(
+ key_identifier, authority_cert_issuer, authority_cert_serial_number
+ )
+
+ def _build_authority_information_access(self, ext):
+ aia = self._backend._lib.X509V3_EXT_d2i(ext)
+ assert aia != self._backend._ffi.NULL
+ aia = self._backend._ffi.cast(
+ "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia
+ )
+ aia = self._backend._ffi.gc(
+ aia, self._backend._lib.sk_ACCESS_DESCRIPTION_free
+ )
+ num = self._backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
+ access_descriptions = []
+ for i in range(num):
+ ad = self._backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i)
+ assert ad.method != self._backend._ffi.NULL
+ oid = x509.ObjectIdentifier(_obj2txt(self._backend, ad.method))
+ assert ad.location != self._backend._ffi.NULL
+ gn = _build_general_name(self._backend, ad.location)
+ access_descriptions.append(x509.AccessDescription(oid, gn))
+
+ return x509.AuthorityInformationAccess(access_descriptions)
+
def _build_key_usage(self, ext):
bit_string = self._backend._lib.X509V3_EXT_d2i(ext)
assert bit_string != self._backend._ffi.NULL
@@ -344,6 +432,24 @@ class _Certificate(object):
return x509.SubjectAlternativeName(general_names)
+ def _build_extended_key_usage(self, ext):
+ sk = self._backend._ffi.cast(
+ "Cryptography_STACK_OF_ASN1_OBJECT *",
+ self._backend._lib.X509V3_EXT_d2i(ext)
+ )
+ assert sk != self._backend._ffi.NULL
+ sk = self._backend._ffi.gc(sk, self._backend._lib.sk_ASN1_OBJECT_free)
+ num = self._backend._lib.sk_ASN1_OBJECT_num(sk)
+ ekus = []
+
+ for i in range(num):
+ obj = self._backend._lib.sk_ASN1_OBJECT_value(sk, i)
+ assert obj != self._backend._ffi.NULL
+ oid = x509.ObjectIdentifier(_obj2txt(self._backend, obj))
+ ekus.append(oid)
+
+ return x509.ExtendedKeyUsage(ekus)
+
@utils.register_interface(x509.CertificateSigningRequest)
class _CertificateSigningRequest(object):
diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py
index b182180f..6493e867 100644
--- a/src/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/src/cryptography/hazmat/bindings/openssl/ssl.py
@@ -211,6 +211,9 @@ int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *);
int SSL_CTX_use_PrivateKey(SSL_CTX *, EVP_PKEY *);
int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);
int SSL_CTX_check_private_key(const SSL_CTX *);
+void SSL_CTX_set_cert_verify_callback(SSL_CTX *,
+ int (*)(X509_STORE_CTX *,void *),
+ void *);
void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *);
X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *);
@@ -322,6 +325,7 @@ void SSL_CTX_set_tlsext_servername_callback(
is fraught with peril thanks to OS distributions we check some constants
to determine if they are supported or not */
long SSL_set_tlsext_status_ocsp_resp(SSL *, unsigned char *, int);
+long SSL_get_tlsext_status_ocsp_resp(SSL *, const unsigned char **);
long SSL_CTX_set_tlsext_status_cb(SSL_CTX *, int(*)(SSL *, void *));
long SSL_session_reused(SSL *);
@@ -431,6 +435,7 @@ static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 1;
#else
static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 0;
long (*SSL_set_tlsext_status_ocsp_resp)(SSL *, unsigned char *, int) = NULL;
+long (*SSL_get_tlsext_status_ocsp_resp)(SSL *, const unsigned char **) = NULL;
#endif
#ifdef SSL_MODE_RELEASE_BUFFERS
@@ -617,6 +622,7 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_STATUS_REQ_OCSP_RESP": [
"SSL_set_tlsext_status_ocsp_resp",
+ "SSL_get_tlsext_status_ocsp_resp",
],
"Cryptography_HAS_RELEASE_BUFFERS": [
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
index a1fb7ffb..fa6a16b3 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -303,6 +303,11 @@ EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **);
int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *);
EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **);
int i2d_ECPrivateKey_bio(BIO *, EC_KEY *);
+
+// declared in safestack
+int sk_ASN1_OBJECT_num(Cryptography_STACK_OF_ASN1_OBJECT *);
+ASN1_OBJECT *sk_ASN1_OBJECT_value(Cryptography_STACK_OF_ASN1_OBJECT *, int);
+void sk_ASN1_OBJECT_free(Cryptography_STACK_OF_ASN1_OBJECT *);
"""
CUSTOMIZATIONS = """
diff --git a/src/cryptography/hazmat/bindings/openssl/x509v3.py b/src/cryptography/hazmat/bindings/openssl/x509v3.py
index 28dd7f32..c2b6860f 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509v3.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509v3.py
@@ -19,9 +19,12 @@ typedef LHASH_OF(CONF_VALUE) Cryptography_LHASH_OF_CONF_VALUE;
#else
typedef LHASH Cryptography_LHASH_OF_CONF_VALUE;
#endif
+typedef STACK_OF(ACCESS_DESCRIPTION) Cryptography_STACK_OF_ACCESS_DESCRIPTION;
"""
TYPES = """
+typedef ... Cryptography_STACK_OF_ACCESS_DESCRIPTION;
+
typedef struct {
X509 *issuer_cert;
X509 *subject_cert;
@@ -92,6 +95,11 @@ typedef struct {
ASN1_INTEGER *serial;
} AUTHORITY_KEYID;
+typedef struct {
+ ASN1_OBJECT *method;
+ GENERAL_NAME *location;
+} ACCESS_DESCRIPTION;
+
typedef ... Cryptography_LHASH_OF_CONF_VALUE;
"""
@@ -109,11 +117,20 @@ MACROS = """
/* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the
x509v3.h header. */
void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *);
+/* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the
+ x509v3.h header. */
+void AUTHORITY_KEYID_free(AUTHORITY_KEYID *);
void *X509V3_set_ctx_nodb(X509V3_CTX *);
int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *);
int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *);
GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int);
+int sk_ACCESS_DESCRIPTION_num(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
+ACCESS_DESCRIPTION *sk_ACCESS_DESCRIPTION_value(
+ Cryptography_STACK_OF_ACCESS_DESCRIPTION *, int
+);
+void sk_ACCESS_DESCRIPTION_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
+
X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *,
X509V3_CTX *, int, char *);
diff --git a/src/cryptography/hazmat/primitives/kdf/hkdf.py b/src/cryptography/hazmat/primitives/kdf/hkdf.py
index 65b7091a..f738bbdc 100644
--- a/src/cryptography/hazmat/primitives/kdf/hkdf.py
+++ b/src/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -26,7 +26,7 @@ class HKDF(object):
self._algorithm = algorithm
- if not isinstance(salt, bytes) and salt is not None:
+ if not (salt is None or isinstance(salt, bytes)):
raise TypeError("salt must be bytes.")
if salt is None:
@@ -77,7 +77,7 @@ class HKDFExpand(object):
self._length = length
- if not isinstance(info, bytes) and info is not None:
+ if not (info is None or isinstance(info, bytes)):
raise TypeError("info must be bytes.")
if info is None:
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index b22ac8be..0d87cd51 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -67,6 +67,8 @@ _OID_NAMES = {
"1.3.6.1.5.5.7.1.1": "authorityInfoAccess",
"1.3.6.1.5.5.7.1.11": "subjectInfoAccess",
"1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck",
+ "1.3.6.1.5.5.7.48.1": "OCSP",
+ "1.3.6.1.5.5.7.48.2": "caIssuers",
}
@@ -277,11 +279,10 @@ class Extension(object):
class ExtendedKeyUsage(object):
def __init__(self, usages):
- for oid in usages:
- if not isinstance(oid, ObjectIdentifier):
- raise TypeError(
- "Every item in the usages list must be an ObjectIdentifier"
- )
+ if not all(isinstance(x, ObjectIdentifier) for x in usages):
+ raise TypeError(
+ "Every item in the usages list must be an ObjectIdentifier"
+ )
self._usages = usages
@@ -294,6 +295,15 @@ class ExtendedKeyUsage(object):
def __repr__(self):
return "<ExtendedKeyUsage({0})>".format(self._usages)
+ def __eq__(self, other):
+ if not isinstance(other, ExtendedKeyUsage):
+ return NotImplemented
+
+ return self._usages == other._usages
+
+ def __ne__(self, other):
+ return not self == other
+
class BasicConstraints(object):
def __init__(self, ca, path_length):
@@ -386,6 +396,70 @@ class KeyUsage(object):
self, encipher_only, decipher_only)
+class AuthorityInformationAccess(object):
+ def __init__(self, descriptions):
+ if not all(isinstance(x, AccessDescription) for x in descriptions):
+ raise TypeError(
+ "Every item in the descriptions list must be an "
+ "AccessDescription"
+ )
+
+ self._descriptions = descriptions
+
+ def __iter__(self):
+ return iter(self._descriptions)
+
+ def __len__(self):
+ return len(self._descriptions)
+
+ def __repr__(self):
+ return "<AuthorityInformationAccess({0})>".format(self._descriptions)
+
+ def __eq__(self, other):
+ if not isinstance(other, AuthorityInformationAccess):
+ return NotImplemented
+
+ return self._descriptions == other._descriptions
+
+ def __ne__(self, other):
+ return not self == other
+
+
+class AccessDescription(object):
+ def __init__(self, access_method, access_location):
+ if not (access_method == OID_OCSP or access_method == OID_CA_ISSUERS):
+ raise ValueError(
+ "access_method must be OID_OCSP or OID_CA_ISSUERS"
+ )
+
+ if not isinstance(access_location, GeneralName):
+ raise TypeError("access_location must be a GeneralName")
+
+ self._access_method = access_method
+ self._access_location = access_location
+
+ def __repr__(self):
+ return (
+ "<AccessDescription(access_method={0.access_method}, access_locati"
+ "on={0.access_location})>".format(self)
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, AccessDescription):
+ return NotImplemented
+
+ return (
+ self.access_method == other.access_method and
+ self.access_location == other.access_location
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+ access_method = utils.read_only_property("_access_method")
+ access_location = utils.read_only_property("_access_location")
+
+
class SubjectKeyIdentifier(object):
def __init__(self, digest):
self._digest = digest
@@ -592,8 +666,13 @@ class AuthorityKeyIdentifier(object):
"must both be present or both None"
)
- if not isinstance(authority_cert_issuer, Name):
- raise TypeError("authority_cert_issuer must be a Name")
+ if not all(
+ isinstance(x, GeneralName) for x in authority_cert_issuer
+ ):
+ raise TypeError(
+ "authority_cert_issuer must be a list of GeneralName "
+ "objects"
+ )
if not isinstance(authority_cert_serial_number, six.integer_types):
raise TypeError(
@@ -672,6 +751,9 @@ OID_EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4")
OID_TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8")
OID_OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9")
+OID_CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2")
+OID_OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1")
+
@six.add_metaclass(abc.ABCMeta)
class Certificate(object):
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index d38fe573..8a227953 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -221,29 +221,33 @@ class TestSubjectKeyIdentifier(object):
class TestAuthorityKeyIdentifier(object):
- def test_authority_cert_issuer_not_name(self):
+ def test_authority_cert_issuer_not_generalname(self):
with pytest.raises(TypeError):
- x509.AuthorityKeyIdentifier(b"identifier", "notname", 3)
+ x509.AuthorityKeyIdentifier(b"identifier", ["notname"], 3)
def test_authority_cert_serial_number_not_integer(self):
- name = x509.Name([
- x509.NameAttribute(x509.ObjectIdentifier('oid'), 'value1'),
- x509.NameAttribute(x509.ObjectIdentifier('oid2'), 'value2'),
- ])
+ dirname = x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(x509.ObjectIdentifier('oid'), 'value1'),
+ x509.NameAttribute(x509.ObjectIdentifier('oid2'), 'value2'),
+ ])
+ )
with pytest.raises(TypeError):
- x509.AuthorityKeyIdentifier(b"identifier", name, "notanint")
+ x509.AuthorityKeyIdentifier(b"identifier", [dirname], "notanint")
def test_authority_issuer_none_serial_not_none(self):
with pytest.raises(ValueError):
x509.AuthorityKeyIdentifier(b"identifier", None, 3)
def test_authority_issuer_not_none_serial_none(self):
- name = x509.Name([
- x509.NameAttribute(x509.ObjectIdentifier('oid'), 'value1'),
- x509.NameAttribute(x509.ObjectIdentifier('oid2'), 'value2'),
- ])
+ dirname = x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(x509.ObjectIdentifier('oid'), 'value1'),
+ x509.NameAttribute(x509.ObjectIdentifier('oid2'), 'value2'),
+ ])
+ )
with pytest.raises(ValueError):
- x509.AuthorityKeyIdentifier(b"identifier", name, None)
+ x509.AuthorityKeyIdentifier(b"identifier", [dirname], None)
def test_authority_cert_serial_and_issuer_none(self):
aki = x509.AuthorityKeyIdentifier(b"id", None, None)
@@ -252,22 +256,24 @@ class TestAuthorityKeyIdentifier(object):
assert aki.authority_cert_serial_number is None
def test_repr(self):
- name = x509.Name([x509.NameAttribute(x509.OID_COMMON_NAME, 'myCN')])
- aki = x509.AuthorityKeyIdentifier(b"digest", name, 1234)
+ dirname = x509.DirectoryName(
+ x509.Name([x509.NameAttribute(x509.OID_COMMON_NAME, 'myCN')])
+ )
+ aki = x509.AuthorityKeyIdentifier(b"digest", [dirname], 1234)
if six.PY3:
assert repr(aki) == (
"<AuthorityKeyIdentifier(key_identifier=b'digest', authority_"
- "cert_issuer=<Name([<NameAttribute(oid=<ObjectIdentifier(oid="
- "2.5.4.3, name=commonName)>, value='myCN')>])>, authority_cer"
- "t_serial_number=1234)>"
+ "cert_issuer=[<DirectoryName(value=<Name([<NameAttribute(oid="
+ "<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='myC"
+ "N')>])>)>], authority_cert_serial_number=1234)>"
)
else:
assert repr(aki) == (
"<AuthorityKeyIdentifier(key_identifier='digest', authority_ce"
- "rt_issuer=<Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5"
- ".4.3, name=commonName)>, value='myCN')>])>, authority_cert_se"
- "rial_number=1234)>"
+ "rt_issuer=[<DirectoryName(value=<Name([<NameAttribute(oid=<Ob"
+ "jectIdentifier(oid=2.5.4.3, name=commonName)>, value='myCN')>"
+ "])>)>], authority_cert_serial_number=1234)>"
)
@@ -325,6 +331,21 @@ class TestExtendedKeyUsage(object):
"tAuth)>])>"
)
+ def test_eq(self):
+ eku = x509.ExtendedKeyUsage([
+ x509.ObjectIdentifier("1.3.6"), x509.ObjectIdentifier("1.3.7")
+ ])
+ eku2 = x509.ExtendedKeyUsage([
+ x509.ObjectIdentifier("1.3.6"), x509.ObjectIdentifier("1.3.7")
+ ])
+ assert eku == eku2
+
+ def test_ne(self):
+ eku = x509.ExtendedKeyUsage([x509.ObjectIdentifier("1.3.6")])
+ eku2 = x509.ExtendedKeyUsage([x509.ObjectIdentifier("1.3.6.1")])
+ assert eku != eku2
+ assert eku != object()
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
@@ -856,3 +877,444 @@ class TestRSASubjectAlternativeNameExtension(object):
x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
])
] == dirname
+
+ def test_rfc822name(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "san_rfc822_idna.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_ALTERNATIVE_NAME
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ san = ext.value
+
+ rfc822name = san.get_values_for_type(x509.RFC822Name)
+ assert [u"email@em\xe5\xefl.com"] == rfc822name
+
+ def test_unicode_rfc822_name_dns_name_uri(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "san_idna_names.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_ALTERNATIVE_NAME
+ )
+ assert ext is not None
+ rfc822_name = ext.value.get_values_for_type(x509.RFC822Name)
+ dns_name = ext.value.get_values_for_type(x509.DNSName)
+ uri = ext.value.get_values_for_type(x509.UniformResourceIdentifier)
+ assert rfc822_name == [u"email@\u043f\u044b\u043a\u0430.cryptography"]
+ assert dns_name == [u"\u043f\u044b\u043a\u0430.cryptography"]
+ assert uri == [u"https://www.\u043f\u044b\u043a\u0430.cryptography"]
+
+ def test_rfc822name_dnsname_ipaddress_directoryname_uri(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "san_email_dns_ip_dirname_uri.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_ALTERNATIVE_NAME
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ san = ext.value
+
+ rfc822_name = san.get_values_for_type(x509.RFC822Name)
+ uri = san.get_values_for_type(x509.UniformResourceIdentifier)
+ dns = san.get_values_for_type(x509.DNSName)
+ ip = san.get_values_for_type(x509.IPAddress)
+ dirname = san.get_values_for_type(x509.DirectoryName)
+ assert [u"user@cryptography.io"] == rfc822_name
+ assert [u"https://cryptography.io"] == uri
+ assert [u"cryptography.io"] == dns
+ assert [
+ x509.Name([
+ x509.NameAttribute(x509.OID_COMMON_NAME, 'dirCN'),
+ x509.NameAttribute(
+ x509.OID_ORGANIZATION_NAME, 'Cryptographic Authority'
+ ),
+ ])
+ ] == dirname
+ assert [
+ ipaddress.ip_address(u"127.0.0.1"),
+ ipaddress.ip_address(u"ff::")
+ ] == ip
+
+ def test_invalid_rfc822name(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "san_rfc822_names.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ with pytest.raises(ValueError) as exc:
+ cert.extensions
+
+ assert 'Invalid rfc822name value' in str(exc.value)
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestExtendedKeyUsageExtension(object):
+ def test_eku(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "extended_key_usage.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_EXTENDED_KEY_USAGE
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert [
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.1"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.2"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.3"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.4"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.9"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.8"),
+ x509.ObjectIdentifier("2.5.29.37.0"),
+ x509.ObjectIdentifier("2.16.840.1.113730.4.1"),
+ ] == list(ext.value)
+
+
+class TestAccessDescription(object):
+ def test_invalid_access_method(self):
+ with pytest.raises(ValueError):
+ x509.AccessDescription("notanoid", x509.DNSName(u"test"))
+
+ def test_invalid_access_location(self):
+ with pytest.raises(TypeError):
+ x509.AccessDescription(x509.OID_CA_ISSUERS, "invalid")
+
+ def test_repr(self):
+ ad = x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ )
+ assert repr(ad) == (
+ "<AccessDescription(access_method=<ObjectIdentifier(oid=1.3.6.1.5."
+ "5.7.48.1, name=OCSP)>, access_location=<UniformResourceIdentifier"
+ "(value=http://ocsp.domain.com)>)>"
+ )
+
+ def test_eq(self):
+ ad = x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ )
+ ad2 = x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ )
+ assert ad == ad2
+
+ def test_ne(self):
+ ad = x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ )
+ ad2 = x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ )
+ ad3 = x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://notthesame")
+ )
+ assert ad != ad2
+ assert ad != ad3
+ assert ad != object()
+
+
+class TestAuthorityInformationAccess(object):
+ def test_invalid_descriptions(self):
+ with pytest.raises(TypeError):
+ x509.AuthorityInformationAccess(["notanAccessDescription"])
+
+ def test_iter_len(self):
+ aia = x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+ )
+ ])
+ assert len(aia) == 2
+ assert list(aia) == [
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+ )
+ ]
+
+ def test_repr(self):
+ aia = x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+ )
+ ])
+ assert repr(aia) == (
+ "<AuthorityInformationAccess([<AccessDescription(access_method=<Ob"
+ "jectIdentifier(oid=1.3.6.1.5.5.7.48.1, name=OCSP)>, access_locati"
+ "on=<UniformResourceIdentifier(value=http://ocsp.domain.com)>)>, <"
+ "AccessDescription(access_method=<ObjectIdentifier(oid=1.3.6.1.5.5"
+ ".7.48.2, name=caIssuers)>, access_location=<UniformResourceIdenti"
+ "fier(value=http://domain.com/ca.crt)>)>])>"
+ )
+
+ def test_eq(self):
+ aia = x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+ )
+ ])
+ aia2 = x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+ )
+ ])
+ assert aia == aia2
+
+ def test_ne(self):
+ aia = x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+ )
+ ])
+ aia2 = x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ ])
+
+ assert aia != aia2
+ assert aia != object()
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestAuthorityInformationAccessExtension(object):
+ def test_aia_ocsp_ca_issuers(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "cryptography.io.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_INFORMATION_ACCESS
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert ext.value == x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://gv.symcd.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://gv.symcb.com/gv.crt")
+ ),
+ ])
+
+ def test_aia_multiple_ocsp_ca_issuers(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "custom", "aia_ocsp_ca_issuers.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_INFORMATION_ACCESS
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert ext.value == x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp2.domain.com")
+ ),
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.DirectoryName(x509.Name([
+ x509.NameAttribute(x509.OID_COMMON_NAME, "myCN"),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, "some Org"),
+ ]))
+ ),
+ ])
+
+ def test_aia_ocsp_only(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "custom", "aia_ocsp.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_INFORMATION_ACCESS
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert ext.value == x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ ])
+
+ def test_aia_ca_issuers_only(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "custom", "aia_ca_issuers.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_INFORMATION_ACCESS
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert ext.value == x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ x509.OID_CA_ISSUERS,
+ x509.DirectoryName(x509.Name([
+ x509.NameAttribute(x509.OID_COMMON_NAME, "myCN"),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, "some Org"),
+ ]))
+ ),
+ ])
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestAuthorityKeyIdentifierExtension(object):
+ def test_aki_keyid(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "cryptography.io.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_KEY_IDENTIFIER
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert ext.value.key_identifier == (
+ b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08\xcbY"
+ )
+ assert ext.value.authority_cert_issuer is None
+ assert ext.value.authority_cert_serial_number is None
+
+ def test_aki_all_fields(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "authority_key_identifier.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_KEY_IDENTIFIER
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert ext.value.key_identifier == (
+ b"9E>\xca=b\x1d\xea\x86I\xf6Z\xab@\xb7\xa4p\x98\xf1\xec"
+ )
+ assert ext.value.authority_cert_issuer == [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_ORGANIZATION_NAME, u"PyCA"
+ ),
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, u"cryptography.io"
+ )
+ ])
+ )
+ ]
+ assert ext.value.authority_cert_serial_number == 3
+
+ def test_aki_no_keyid(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "authority_key_identifier_no_keyid.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_KEY_IDENTIFIER
+ )
+ assert ext is not None
+ assert ext.critical is False
+
+ assert ext.value.key_identifier is None
+ assert ext.value.authority_cert_issuer == [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_ORGANIZATION_NAME, u"PyCA"
+ ),
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, u"cryptography.io"
+ )
+ ])
+ )
+ ]
+ assert ext.value.authority_cert_serial_number == 3
diff --git a/tests/utils.py b/tests/utils.py
index 4c74ca7c..ab922c94 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -498,6 +498,7 @@ _ECDSA_CURVE_NAMES = {
"K-163": "sect163k1",
"K-233": "sect233k1",
+ "K-256": "secp256k1",
"K-283": "sect283k1",
"K-409": "sect409k1",
"K-571": "sect571k1",
diff --git a/vectors/cryptography_vectors/x509/custom/aia_ca_issuers.pem b/vectors/cryptography_vectors/x509/custom/aia_ca_issuers.pem
new file mode 100644
index 00000000..8e7d6233
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/aia_ca_issuers.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDIzCCAgugAwIBAgITBmpzihaCgW4OeroV/vOMmWeoVTANBgkqhkiG9w0BAQUF
+ADApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcN
+MTUwNTA0MTYwMTM1WhcNMTYwNTAzMTYwMTM1WjApMQ0wCwYDVQQKDARQeUNBMRgw
+FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDCadi1UZioxdnPajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLO
+hjNLz6PO6KeRbjz9GhTA2hdkxtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4c
+MwMe5NGeeg1tfzbJwldIN+cKvabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr
+6E9puord3h6Mh8Jfnc3TDAq8Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk
+/XhRzt8Xcfc3yAXIwazvLf8bYP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+
+qUYpxA7ZJ/VAGadMulYbXaO8Syi39HTpAgMBAAGjRDBCMEAGCCsGAQUFBwEBBDQw
+MjAwBggrBgEFBQcwAqQkMCIxDTALBgNVBAMMBG15Q04xETAPBgNVBAoMCHNvbWUg
+T3JnMA0GCSqGSIb3DQEBBQUAA4IBAQA77ax7iuD6Ood7lCfqGkUAHnZCTRBTaIBf
+Ob8fDYdXWe7nr3dAq+M14GNMIvBURixL8xnb+AameJdGzzxEIkmzYpJmeVM+UCHJ
+oOO2Q2JuHOvULOxQjrYdwBsb9jgiwWGuAfFA2nV+zO1/zjQ0meOL7p98ulaDbZKi
+JMMNtKA2P3XnSeKXBN0HSKUk0DCpIeEpMNx3KOpzx1QtVkXUDeAXb8R+CPWHkKAp
+9v5lV/Q2UG39/Mb1QdlNksoVkti0/wIXFUQy2z1ZwfP2Ty/gloZqYVaXcD3qFHea
+XtzbzmYSHqRUVug3+yg1n6qBDl6YblHwr9EwtPAreMzk6FEjjwVE
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/aia_ocsp.pem b/vectors/cryptography_vectors/x509/custom/aia_ocsp.pem
new file mode 100644
index 00000000..9bdb5d1a
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/aia_ocsp.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDFTCCAf2gAwIBAgITBmpzjEDbt6vFUmUSgFfQY90MnjANBgkqhkiG9w0BAQUF
+ADApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcN
+MTUwNTA0MTYwMjA0WhcNMTYwNTAzMTYwMjA0WjApMQ0wCwYDVQQKDARQeUNBMRgw
+FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDCadi1UZioxdnPajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLO
+hjNLz6PO6KeRbjz9GhTA2hdkxtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4c
+MwMe5NGeeg1tfzbJwldIN+cKvabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr
+6E9puord3h6Mh8Jfnc3TDAq8Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk
+/XhRzt8Xcfc3yAXIwazvLf8bYP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+
+qUYpxA7ZJ/VAGadMulYbXaO8Syi39HTpAgMBAAGjNjA0MDIGCCsGAQUFBwEBBCYw
+JDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AuZG9tYWluLmNvbTANBgkqhkiG9w0B
+AQUFAAOCAQEAN3+EuDWKytFLzHO21aZHCQOSZhXLf+Vv9epdI9mgLUkZ712ntkjY
+obJVQ5YK9viAutiRArTyEHRT2YMPYV7gaBof5bxKmrSflUNKmPMd9U6zDU2BDuRe
+mLOo9S0wIh0Qfm1mhD27waKy/HhjZ3nkeORICmej3GWIK8mo+fL0LiDKTeoZdQ4B
+ENWj+xMOcaOlhBeq9NBNawPR/YihAID/m/em5kvq4dMoVpASNPpK/gTQb7wGHSh9
+59TUBUyS6ilmYrku4y+kdDG4uommrgGkqbX/xixug623GnGvGQKvPeZ0CoyKTzgQ
+6145cQvWl04a7TQqqgCCFQexn3O+kvJVJA==
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/aia_ocsp_ca_issuers.pem b/vectors/cryptography_vectors/x509/custom/aia_ocsp_ca_issuers.pem
new file mode 100644
index 00000000..1a2b5a23
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/aia_ocsp_ca_issuers.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbzCCAlegAwIBAgITBmpzhaASIzPUgqgz8AKBt68y2zANBgkqhkiG9w0BAQUF
+ADApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcN
+MTUwNTA0MTYwMDM1WhcNMTYwNTAzMTYwMDM1WjApMQ0wCwYDVQQKDARQeUNBMRgw
+FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDCadi1UZioxdnPajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLO
+hjNLz6PO6KeRbjz9GhTA2hdkxtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4c
+MwMe5NGeeg1tfzbJwldIN+cKvabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr
+6E9puord3h6Mh8Jfnc3TDAq8Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk
+/XhRzt8Xcfc3yAXIwazvLf8bYP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+
+qUYpxA7ZJ/VAGadMulYbXaO8Syi39HTpAgMBAAGjgY8wgYwwgYkGCCsGAQUFBwEB
+BH0wezAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AuZG9tYWluLmNvbTAjBggrBgEF
+BQcwAYYXaHR0cDovL29jc3AyLmRvbWFpbi5jb20wMAYIKwYBBQUHMAKkJDAiMQ0w
+CwYDVQQDDARteUNOMREwDwYDVQQKDAhzb21lIE9yZzANBgkqhkiG9w0BAQUFAAOC
+AQEAG6t7q/a5SzRhXSjl29eK+psDadkm4tS1LC2aVuNexO2ZggK/0n3wkCa7M0jZ
+90DRTWeClBekhx5U0jDNAbzOlb64pnwAxoj+Vw3O5Jn6LoF2il5kpQMcvLII1bh9
+V6iXgEDRZxlAPoVkSMN4bGUKLRmKK0MGWTniw/2eSPOr6wqbzgXytPrPjOVP1XYF
+njasNI/LdXZBxe7AmlNziYMMBt1fII0c6W3PXgTHmGbHAocz+rbWI0ENax8YmKKd
+k99fYtY2675va+a9CVXR2Cr3B86OwltAqvW6iVZP0qF+A+QqrAaZOghCwATu/6Ho
+8OrvFjYaK5wyyBzvUbAs9L7mqg==
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/authority_key_identifier.pem b/vectors/cryptography_vectors/x509/custom/authority_key_identifier.pem
new file mode 100644
index 00000000..cbe9169f
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/authority_key_identifier.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDIjCCAgqgAwIBAgIBAzANBgkqhkiG9w0BAQUFADApMQ0wCwYDVQQKDARQeUNB
+MRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcNMTUwNTAzMDk0OTU2WhcNMTYw
+NTAyMDk0OTU2WjApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFw
+aHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCadi1UZioxdnP
+ajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLOhjNLz6PO6KeRbjz9GhTA2hdk
+xtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4cMwMe5NGeeg1tfzbJwldIN+cK
+vabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr6E9puord3h6Mh8Jfnc3TDAq8
+Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk/XhRzt8Xcfc3yAXIwazvLf8b
+YP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+qUYpxA7ZJ/VAGadMulYbXaO8
+Syi39HTpAgMBAAGjVTBTMFEGA1UdIwRKMEiAFDlFPso9Yh3qhkn2WqtAt6RwmPHs
+oS2kKzApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW+C
+AQMwDQYJKoZIhvcNAQEFBQADggEBAFbZYy6aZJUK/f7nJx2Rs/ht6hMbM32/RoXZ
+JGbYapNVqVu/vymcfc/se3FHS5OVmPsnRlo/FIKDn/r5DGl73Sn/FvDJiLJZFucT
+msyYuHZ+ZRYWzWmN2fcB3cfxj0s3qps6f5OoCOqoINOSe4HRGlw4X9keZSD+3xAt
+vHNwQdlPC7zWbPdrzLT+FqR0e/O81vFJJS6drHJWqPcR3NQVtZw+UF7A/HKwbfeL
+Nu2zj6165hzOi9HUxa2/mPr/eLUUV1sTzXp2+TFjt3rVCjW1XnpMLdwNBHzjpyAB
+dTOX3iw0+BPy3s2jtnCW1PLpc74kvSTaBwhg74sq39EXfIKax00=
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/authority_key_identifier_no_keyid.pem b/vectors/cryptography_vectors/x509/custom/authority_key_identifier_no_keyid.pem
new file mode 100644
index 00000000..fffcc653
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/authority_key_identifier_no_keyid.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADApMQ0wCwYDVQQKDARQeUNB
+MRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcNMTUwNTAzMTAxNTU2WhcNMTYw
+NTAyMTAxNTU2WjApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFw
+aHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCadi1UZioxdnP
+ajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLOhjNLz6PO6KeRbjz9GhTA2hdk
+xtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4cMwMe5NGeeg1tfzbJwldIN+cK
+vabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr6E9puord3h6Mh8Jfnc3TDAq8
+Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk/XhRzt8Xcfc3yAXIwazvLf8b
+YP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+qUYpxA7ZJ/VAGadMulYbXaO8
+Syi39HTpAgMBAAGjPzA9MDsGA1UdIwQ0MDKhLaQrMCkxDTALBgNVBAoMBFB5Q0Ex
+GDAWBgNVBAMMD2NyeXB0b2dyYXBoeS5pb4IBAzANBgkqhkiG9w0BAQUFAAOCAQEA
+AViX0VIVQW2xyf0lfLiuFhrpdgX9i49StZvs+n/qH5yvWxfqRJAyVT1pk2Xs0Goj
+ul7vYMfIGU0nIr8eLMlAH9j6lkllAd/oO1BDONZ1kH6PMdkOdvgz5gmhMQx6MFr6
+zMzzQ+JOAnXKFFUEycOiRJyh3VXiTY1M1IG1kWY+LoqB72S7y9c25yFoHqUNi2Xf
+rbuaR7gNS/4z7XvLJkbNbVS2+y69gQGL+8vk5AG7MiZ1mzUQ44r/zy6HNDBb55kK
+H+YTYavijRApH5hccJBXyoIM0x9ZtKdcrV0h+J2KOFGEyHp3FXViFEB2IZUpJNA/
+aduVbH8gZy5Y+cHzenwzBg==
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/extended_key_usage.pem b/vectors/cryptography_vectors/x509/custom/extended_key_usage.pem
new file mode 100644
index 00000000..e17c87e0
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/extended_key_usage.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDOTCCAiGgAwIBAgITBmouGmlz8B0jbn/oy5Zm347WAjANBgkqhkiG9w0BAQUF
+ADApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcN
+MTUwNTAxMjIyMzM1WhcNMTYwNDMwMjIyMzM1WjApMQ0wCwYDVQQKDARQeUNBMRgw
+FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDCadi1UZioxdnPajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLO
+hjNLz6PO6KeRbjz9GhTA2hdkxtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4c
+MwMe5NGeeg1tfzbJwldIN+cKvabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr
+6E9puord3h6Mh8Jfnc3TDAq8Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk
+/XhRzt8Xcfc3yAXIwazvLf8bYP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+
+qUYpxA7ZJ/VAGadMulYbXaO8Syi39HTpAgMBAAGjWjBYMFYGA1UdJQRPME0GCCsG
+AQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCQYI
+KwYBBQUHAwgGBFUdJQAGCWCGSAGG+EIEATANBgkqhkiG9w0BAQUFAAOCAQEAZEnO
+bpVLLVFVihyp3wX8JiPgeHAiAexs2KuVD2yVYhzSLm3f1k580mK1VDtW0Cn3GEiQ
+qZax/KHN7WmZXdqNHQ3qJp86QXR7BATD1hgUOW1H62jRaH82OKl0o/LxeLKKJyt8
+YehtxE0If+HL0dDL1KdNgEggcG4iVG5QS5PJNZ/2j2ZqjT+PTy96++L8BhdEfrAT
+PNpWyAB31Tyx3P4iQOT7WOgbXCp7/a2piSRaaqcWHioQ6OtAujEJ8Yu22IuwwyNZ
+gZNEB57kX0L3t9dg1ojVGHtaUql8aWf+OiFBjzZH+YcgNDyqAJWhy3d7T4f6VC+S
+kHrqKK+Nv7i6s/qH5Q==
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/inhibit_any_policy_5.pem b/vectors/cryptography_vectors/x509/custom/inhibit_any_policy_5.pem
new file mode 100644
index 00000000..681770cf
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/inhibit_any_policy_5.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8DCCAdigAwIBAgITBmot4z1/TiVQVVjARir3nGQGETANBgkqhkiG9w0BAQUF
+ADApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcN
+MTUwNTAxMjIxMTIyWhcNMTYwNDMwMjIxMTIyWjApMQ0wCwYDVQQKDARQeUNBMRgw
+FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCZcYt4uuICKbglCg0qlreb8P52FjWhdrJ7GNbZBDw5iwsKoLb191YI
+6cXRk+FMtMJJ7aqjoXxpHDsad2iDsMRtj9ZjUMRxZTJyZVvmYCm4FP4bP1GE+W48
+N3hiUq8hv3UX4nAzLMIniXfQANYpHDZxh0EkX3vQaCEi5abwbzb0Ra7sUWNAPUVF
+Ag3IyNkYqXm1lveWkogkXnBgr8RH7dN59hGHYbKU0aRpmHBB82NyJUkDCl47ybNJ
+xaRS9M/QOMg2FCXgQGJzDcLvafIeEqcoy6jq9NwzydafHeYc6QY5P7pl7AqwEYeL
+AYC3i0eTtRCP64ChL4eA9VKREiwesKS5AgMBAAGjETAPMA0GA1UdNgEB/wQDAgEF
+MA0GCSqGSIb3DQEBBQUAA4IBAQCYMu1zYjS7pDmJE+RJtIHxzUNrfBs9mV58H9PN
+UgUvttCWdD+6U9v5mOJS5HVl9wiR8Slf9lz9KuTJkT0K5qmcn0PZpo/eJZd7yDYK
+hRfQ8xQapA/zK4u1S/kfflXmvvwvCaAn3fEfqOrylPrtCQBFwLZDo88a1Fmjmjti
+ipwxCkGMrwRaWQtIzEB1T0lCEGSfNtI4pcNM84RrlW+WYUBjsNm4X3kPnLLl8BY3
+xcUPQgBZpFdLDcOLrd0XeTRpMenzAZ/ksYEpvDlnlk54Pfe+I8dXF9oe9LhFUfjx
+rbvJIJgRkQeJl9hwqQyqgnpaIZHA5opoMkjeYxihsR5pQ+ag
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/inhibit_any_policy_negative.pem b/vectors/cryptography_vectors/x509/custom/inhibit_any_policy_negative.pem
new file mode 100644
index 00000000..3d610e6e
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/inhibit_any_policy_negative.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8DCCAdigAwIBAgITBmot7URN56N4n8y2DDs8wRpz6zANBgkqhkiG9w0BAQUF
+ADApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcN
+MTUwNTAxMjIxMzM1WhcNMTYwNDMwMjIxMzM1WjApMQ0wCwYDVQQKDARQeUNBMRgw
+FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDCadi1UZioxdnPajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLO
+hjNLz6PO6KeRbjz9GhTA2hdkxtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4c
+MwMe5NGeeg1tfzbJwldIN+cKvabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr
+6E9puord3h6Mh8Jfnc3TDAq8Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk
+/XhRzt8Xcfc3yAXIwazvLf8bYP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+
+qUYpxA7ZJ/VAGadMulYbXaO8Syi39HTpAgMBAAGjETAPMA0GA1UdNgEB/wQDAgH/
+MA0GCSqGSIb3DQEBBQUAA4IBAQBbFY9BzxtBqovxCIhxhaiSemoAbIPJx/j8i0fn
+YYzcL8CDWO1tjb3m2w4tFRtdx16xFPfvN30FWWD0925uNK0CPxcPsisLB4a6mLBU
+1Epy9/0Zwfw+lF73lpCBeJ5PGw/yqNyV6fLWzJN1q1q9KJfdAMqm+4YZHEjM9s5q
+h+SsDrtDghLR97xyEwLzI4wkByFSOlk/f80Y56V9uftihbpDkNp8ujVMfFqpchzo
+kQ20U/wwhmkAP1iEfacCY8eGngJ5DQEkEN5pbOvZGj2ZFAExfFlF58CpBH+Hy9+I
+NeelamzVj+GvcPzICMkU31ESyjajSJ6Ta4yssn8FH8B0hp/z
+-----END CERTIFICATE-----