Merge pull request #1814 from reaperhulk/keyusage-repr
add repr to KeyUsage
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b520e48..fd92a56 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -10,6 +10,8 @@
actually dropping support, however we strongly encourage all users to upgrade
their Python, as Python 2.6 no longer receives support from the Python core
team.
+* Fixed compilation when using an OpenSSL which was compiled with the
+ ``no-comp`` (``OPENSSL_NO_COMP``) option.
* Support :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`
serialization of public keys using the ``public_bytes`` method of
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`,
@@ -26,6 +28,12 @@
:func:`~cryptography.x509.load_pem_x509_csr` and
:func:`~cryptography.x509.load_der_x509_csr`.
+0.8.2 - 2015-04-10
+~~~~~~~~~~~~~~~~~~
+
+* Fixed a race condition when initializing the OpenSSL or CommonCrypto backends
+ in a multi-threaded scenario.
+
0.8.1 - 2015-03-20
~~~~~~~~~~~~~~~~~~
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index e2b621c..5353b1d 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -132,6 +132,8 @@
a subject alternative name extension with the ``otherName`` general name.
* ``san_registered_id.pem`` - An RSA 1024 bit certificate containing a
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.
Custom X.509 Request Vectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/fernet.rst b/docs/fernet.rst
index f1a4c74..eacbc2a 100644
--- a/docs/fernet.rst
+++ b/docs/fernet.rst
@@ -92,8 +92,10 @@
>>> f.decrypt(token)
'Secret message!'
- Fernet performs all encryption options using the *first* key in the
- ``list`` provided. Decryption supports using *any* of constituent keys.
+ MultiFernet performs all encryption options using the *first* key in the
+ ``list`` provided. MultiFernet attempts to decrypt tokens with each key in
+ turn. A :class:`cryptography.fernet.InvalidToken` exception is raised if
+ the correct key is not found in the ``list`` provided.
Key rotation makes it easy to replace old keys. You can add your new key at
the front of the list to start encrypting new messages, and remove old keys
diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst
index fdf113f..dde18cf 100644
--- a/docs/hazmat/primitives/asymmetric/dh.rst
+++ b/docs/hazmat/primitives/asymmetric/dh.rst
@@ -6,6 +6,9 @@
.. currentmodule:: cryptography.hazmat.primitives.asymmetric.dh
+Numbers
+~~~~~~~
+
.. class:: DHPrivateNumbers(x, public_numbers)
.. versionadded:: 0.8
@@ -62,3 +65,98 @@
:type: int
The generator value.
+
+
+Key interfaces
+~~~~~~~~~~~~~~
+
+.. class:: DHParameters
+
+ .. versionadded:: 0.9
+
+
+ .. method:: generate_private_key()
+
+ .. versionadded:: 0.9
+
+ Generate a DH private key. This method can be used to generate many
+ new private keys from a single set of parameters.
+
+ :return: A
+ :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`
+ provider.
+
+
+.. class:: DHParametersWithSerialization
+
+ .. versionadded:: 0.9
+
+ Inherits from :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`.
+
+ .. method:: parameter_numbers()
+
+ Return the numbers that make up this set of parameters.
+
+ :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers`.
+
+
+.. class:: DHPrivateKey
+
+ .. versionadded:: 0.9
+
+ .. attribute:: key_size
+
+ The bit length of the prime modulus.
+
+ .. method:: public_key()
+
+ Return the public key associated with this private key.
+
+ :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`.
+
+ .. method:: parameters()
+
+ Return the parameters associated with this private key.
+
+ :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`.
+
+
+.. class:: DHPrivateKeyWithSerialization
+
+ .. versionadded:: 0.9
+
+ Inherits from :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`.
+
+ .. method:: private_numbers()
+
+ Return the numbers that make up this private key.
+
+ :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateNumbers`.
+
+
+.. class:: DHPublicKey
+
+ .. versionadded:: 0.9
+
+ .. attribute:: key_size
+
+ The bit length of the prime modulus.
+
+ .. method:: parameters()
+
+ Return the parameters associated with this private key.
+
+ :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`.
+
+
+.. class:: DHPublicKeyWithSerialization
+
+ .. versionadded:: 0.9
+
+ Inherits from :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`.
+
+ .. method:: public_numbers()
+
+ Return the numbers that make up this public key.
+
+ :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers`.
diff --git a/docs/hazmat/primitives/asymmetric/interfaces.rst b/docs/hazmat/primitives/asymmetric/interfaces.rst
index 4932faa..c4f176c 100644
--- a/docs/hazmat/primitives/asymmetric/interfaces.rst
+++ b/docs/hazmat/primitives/asymmetric/interfaces.rst
@@ -30,4 +30,3 @@
:raises cryptography.exceptions.InvalidSignature: If the signature does
not validate.
-
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index c029b5d..f7b73b3 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -20,6 +20,7 @@
decrypting
deserialize
deserialized
+Diffie
Docstrings
Encodings
fernet
diff --git a/docs/x509.rst b/docs/x509.rst
index afc9620..d09651f 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -275,6 +275,7 @@
>>> for ext in cert.extensions:
... print(ext)
+ <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.19, name=basicConstraints)>, critical=True, value=<BasicConstraints(ca=True, path_length=None)>)>
X.509 CSR (Certificate Signing Request) Object
@@ -576,6 +577,19 @@
purposes indicated in the key usage extension. The object is
iterable to obtain the list of :ref:`extended key usage OIDs <eku_oids>`.
+.. class:: SubjectKeyIdentifier
+
+ .. versionadded:: 0.9
+
+ The subject key identifier extension provides a means of identifying
+ certificates that contain a particular public key.
+
+ .. attribute:: digest
+
+ :type: bytes
+
+ The binary value of the identifier.
+
Object Identifiers
~~~~~~~~~~~~~~~~~~
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 6a7032b..5d47c5e 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -170,6 +170,8 @@
)
elif oid == x509.OID_BASIC_CONSTRAINTS:
value = self._build_basic_constraints(ext)
+ elif oid == x509.OID_SUBJECT_KEY_IDENTIFIER:
+ value = self._build_subject_key_identifier(ext)
elif oid == x509.OID_KEY_USAGE and critical:
# TODO: remove this obviously.
warnings.warn(
@@ -217,6 +219,19 @@
return x509.BasicConstraints(ca, path_length)
+ def _build_subject_key_identifier(self, ext):
+ asn1_string = self._backend._lib.X509V3_EXT_d2i(ext)
+ assert asn1_string != self._backend._ffi.NULL
+ asn1_string = self._backend._ffi.cast(
+ "ASN1_OCTET_STRING *", asn1_string
+ )
+ asn1_string = self._backend._ffi.gc(
+ asn1_string, self._backend._lib.ASN1_OCTET_STRING_free
+ )
+ return x509.SubjectKeyIdentifier(
+ self._backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
+ )
+
@utils.register_interface(x509.CertificateSigningRequest)
class _CertificateSigningRequest(object):
diff --git a/src/cryptography/hazmat/bindings/openssl/asn1.py b/src/cryptography/hazmat/bindings/openssl/asn1.py
index d8b8331..45dfe75 100644
--- a/src/cryptography/hazmat/bindings/openssl/asn1.py
+++ b/src/cryptography/hazmat/bindings/openssl/asn1.py
@@ -40,6 +40,7 @@
typedef struct asn1_string_st ASN1_OCTET_STRING;
typedef struct asn1_string_st ASN1_IA5STRING;
+typedef ... ASN1_BIT_STRING;
typedef ... ASN1_OBJECT;
typedef ... ASN1_STRING;
typedef ... ASN1_TYPE;
@@ -115,9 +116,12 @@
ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **, const unsigned char **, long,
const ASN1_ITEM *);
+int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *, int, int);
"""
MACROS = """
+/* This is not a macro, but is const on some versions of OpenSSL */
+int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *, int);
ASN1_TIME *M_ASN1_TIME_dup(void *);
const ASN1_ITEM *ASN1_ITEM_ptr(ASN1_ITEM_EXP *);
diff --git a/src/cryptography/hazmat/bindings/openssl/pem.py b/src/cryptography/hazmat/bindings/openssl/pem.py
index 98c7648..8ec3fef 100644
--- a/src/cryptography/hazmat/bindings/openssl/pem.py
+++ b/src/cryptography/hazmat/bindings/openssl/pem.py
@@ -32,7 +32,9 @@
int i2d_PKCS8PrivateKey_nid_bio(BIO *, EVP_PKEY *, int,
char *, int, pem_password_cb *, void *);
+int i2d_PKCS7_bio(BIO *, PKCS7 *);
PKCS7 *d2i_PKCS7_bio(BIO *, PKCS7 **);
+
EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *, EVP_PKEY **, pem_password_cb *,
void *);
@@ -45,6 +47,8 @@
int PEM_write_bio_X509_CRL(BIO *, X509_CRL *);
PKCS7 *PEM_read_bio_PKCS7(BIO *, PKCS7 **, pem_password_cb *, void *);
+int PEM_write_bio_PKCS7(BIO *, PKCS7 *);
+
DH *PEM_read_bio_DHparams(BIO *, DH **, pem_password_cb *, void *);
DSA *PEM_read_bio_DSAPrivateKey(BIO *, DSA **, pem_password_cb *, void *);
diff --git a/src/cryptography/hazmat/bindings/openssl/pkcs7.py b/src/cryptography/hazmat/bindings/openssl/pkcs7.py
index 3196fa1..df82afe 100644
--- a/src/cryptography/hazmat/bindings/openssl/pkcs7.py
+++ b/src/cryptography/hazmat/bindings/openssl/pkcs7.py
@@ -13,10 +13,37 @@
ASN1_OBJECT *type;
...;
} PKCS7;
+
+static const int PKCS7_BINARY;
+static const int PKCS7_DETACHED;
+static const int PKCS7_NOATTR;
+static const int PKCS7_NOCERTS;
+static const int PKCS7_NOCHAIN;
+static const int PKCS7_NOINTERN;
+static const int PKCS7_NOSIGS;
+static const int PKCS7_NOSMIMECAP;
+static const int PKCS7_NOVERIFY;
+static const int PKCS7_STREAM;
+static const int PKCS7_TEXT;
"""
FUNCTIONS = """
+PKCS7 *SMIME_read_PKCS7(BIO *, BIO **);
+int SMIME_write_PKCS7(BIO *, PKCS7 *, BIO *, int);
+
void PKCS7_free(PKCS7 *);
+
+PKCS7 *PKCS7_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *,
+ BIO *, int);
+int PKCS7_verify(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *,
+ BIO *, int);
+Cryptography_STACK_OF_X509 *PKCS7_get0_signers(PKCS7 *,
+ Cryptography_STACK_OF_X509 *,
+ int);
+
+PKCS7 *PKCS7_encrypt(Cryptography_STACK_OF_X509 *, BIO *,
+ const EVP_CIPHER *, int);
+int PKCS7_decrypt(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int);
"""
MACROS = """
@@ -26,7 +53,6 @@
int PKCS7_type_is_data(PKCS7 *);
"""
-CUSTOMIZATIONS = """
-"""
+CUSTOMIZATIONS = ""
CONDITIONAL_NAMES = {}
diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py
index bc4b2e7..d680c3a 100644
--- a/src/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/src/cryptography/hazmat/bindings/openssl/ssl.py
@@ -546,11 +546,7 @@
#else
static const long Cryptography_HAS_ALPN = 1;
#endif
-/* LibreSSL has removed support for compression, and with it the
- * COMP_METHOD use in ssl.h. This is a hack to make the function types
- * in this code match those in ssl.h.
- */
-#ifdef LIBRESSL_VERSION_NUMBER
+#if defined(OPENSSL_NO_COMP) || defined(LIBRESSL_VERSION_NUMBER)
static const long Cryptography_HAS_COMPRESSION = 0;
typedef void COMP_METHOD;
#else
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
index e867450..b5c9ee1 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -168,6 +168,9 @@
int X509_REVOKED_set_serialNumber(X509_REVOKED *, ASN1_INTEGER *);
+int X509_REVOKED_get_ext_count(X509_REVOKED *);
+X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *, int);
+int X509_REVOKED_add_ext(X509_REVOKED *, X509_EXTENSION*, int);
int X509_REVOKED_add1_ext_i2d(X509_REVOKED *, int, void *, int, unsigned long);
X509_CRL *d2i_X509_CRL_bio(BIO *, X509_CRL **);
@@ -178,6 +181,9 @@
int X509_CRL_print(BIO *, X509_CRL *);
int X509_CRL_set_issuer_name(X509_CRL *, X509_NAME *);
int X509_CRL_sign(X509_CRL *, EVP_PKEY *, const EVP_MD *);
+int X509_CRL_get_ext_count(X509_CRL *);
+X509_EXTENSION *X509_CRL_get_ext(X509_CRL *, int);
+int X509_CRL_add_ext(X509_CRL *, X509_EXTENSION *, int);
int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *, EVP_PKEY *);
int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *, EVP_PKEY *, const EVP_MD *);
@@ -262,11 +268,22 @@
int sk_X509_REVOKED_num(Cryptography_STACK_OF_X509_REVOKED *);
X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int);
+Cryptography_STACK_OF_X509_CRL *sk_X509_CRL_new_null(void);
+void sk_X509_CRL_free(Cryptography_STACK_OF_X509_CRL *);
+int sk_X509_CRL_num(Cryptography_STACK_OF_X509_CRL *);
+int sk_X509_CRL_push(Cryptography_STACK_OF_X509_CRL *, X509_CRL *);
+X509_CRL *sk_X509_CRL_value(Cryptography_STACK_OF_X509_CRL *, int);
+
int i2d_RSAPublicKey(RSA *, unsigned char **);
int i2d_RSAPrivateKey(RSA *, unsigned char **);
int i2d_DSAPublicKey(DSA *, unsigned char **);
int i2d_DSAPrivateKey(DSA *, unsigned char **);
+int X509_CRL_get_version(X509_CRL *);
+ASN1_TIME *X509_CRL_get_lastUpdate(X509_CRL *);
+ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *);
+X509_NAME *X509_CRL_get_issuer(X509_CRL *);
+
/* These aren't macros these arguments are all const X on openssl > 1.0.x */
int X509_CRL_set_lastUpdate(X509_CRL *, ASN1_TIME *);
int X509_CRL_set_nextUpdate(X509_CRL *, ASN1_TIME *);
diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py
index 61556ef..12d53ee 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/dh.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py
@@ -1,18 +1,11 @@
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
from __future__ import absolute_import, division, print_function
+import abc
+
import six
from cryptography import utils
@@ -99,3 +92,75 @@
p = utils.read_only_property("_p")
g = utils.read_only_property("_g")
+
+
+@six.add_metaclass(abc.ABCMeta)
+class DHParameters(object):
+ @abc.abstractmethod
+ def generate_private_key(self):
+ """
+ Generates and returns a DHPrivateKey.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class DHParametersWithSerialization(DHParameters):
+ @abc.abstractmethod
+ def parameter_numbers(self):
+ """
+ Returns a DHParameterNumbers.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class DHPrivateKey(object):
+ @abc.abstractproperty
+ def key_size(self):
+ """
+ The bit length of the prime modulus.
+ """
+
+ @abc.abstractmethod
+ def public_key(self):
+ """
+ The DHPublicKey associated with this private key.
+ """
+
+ @abc.abstractmethod
+ def parameters(self):
+ """
+ The DHParameters object associated with this private key.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class DHPrivateKeyWithSerialization(DHPrivateKey):
+ @abc.abstractmethod
+ def private_numbers(self):
+ """
+ Returns a DHPrivateNumbers.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class DHPublicKey(object):
+ @abc.abstractproperty
+ def key_size(self):
+ """
+ The bit length of the prime modulus.
+ """
+
+ @abc.abstractmethod
+ def parameters(self):
+ """
+ The DHParameters object associated with this public key.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class DHPublicKeyWithSerialization(DHPublicKey):
+ @abc.abstractmethod
+ def public_numbers(self):
+ """
+ Returns a DHPublicNumbers.
+ """
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 17ef6dd..a9b6f8b 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -48,9 +48,24 @@
"1.3.6.1.5.5.7.3.4": "emailProtection",
"1.3.6.1.5.5.7.3.8": "timeStamping",
"1.3.6.1.5.5.7.3.9": "OCSPSigning",
- "2.5.29.19": "basicConstraints",
+ "2.5.29.9": "subjectDirectoryAttributes",
+ "2.5.29.14": "subjectKeyIdentifier",
"2.5.29.15": "keyUsage",
+ "2.5.29.17": "subjectAltName",
+ "2.5.29.18": "issuerAltName",
+ "2.5.29.19": "basicConstraints",
+ "2.5.29.30": "nameConstraints",
+ "2.5.29.31": "cRLDistributionPoints",
+ "2.5.29.32": "certificatePolicies",
+ "2.5.29.33": "policyMappings",
+ "2.5.29.35": "authorityKeyIdentifier",
+ "2.5.29.36": "policyConstraints",
"2.5.29.37": "extendedKeyUsage",
+ "2.5.29.46": "freshestCRL",
+ "2.5.29.54": "inhibitAnyPolicy",
+ "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",
}
@@ -175,10 +190,28 @@
def __len__(self):
return len(self._attributes)
+ def __repr__(self):
+ return "<Name({0!r})>".format(self._attributes)
+
+OID_SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9")
+OID_SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14")
OID_KEY_USAGE = ObjectIdentifier("2.5.29.15")
-OID_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37")
+OID_SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17")
+OID_ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18")
OID_BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19")
+OID_NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30")
+OID_CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31")
+OID_CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32")
+OID_POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33")
+OID_AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35")
+OID_POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36")
+OID_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37")
+OID_FRESHEST_CRL = ObjectIdentifier("2.5.29.46")
+OID_INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54")
+OID_AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1")
+OID_SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11")
+OID_OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5")
class Extensions(object):
@@ -238,6 +271,9 @@
def __len__(self):
return len(self._usages)
+ def __repr__(self):
+ return "<ExtendedKeyUsage({0})>".format(self._usages)
+
class BasicConstraints(object):
def __init__(self, ca, path_length):
@@ -330,6 +366,27 @@
self, encipher_only, decipher_only)
+class SubjectKeyIdentifier(object):
+ def __init__(self, digest):
+ self._digest = digest
+
+ digest = utils.read_only_property("_digest")
+
+ def __repr__(self):
+ return "<SubjectKeyIdentifier(digest={0!r})>".format(self.digest)
+
+ def __eq__(self, other):
+ if not isinstance(other, SubjectKeyIdentifier):
+ return NotImplemented
+
+ return (
+ self.digest == other.digest
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+
OID_COMMON_NAME = ObjectIdentifier("2.5.4.3")
OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7")
diff --git a/tests/test_x509.py b/tests/test_x509.py
index dc148d9..d8a4cc0 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -607,3 +607,16 @@
])
assert name1 != name2
assert name1 != object()
+
+ def test_repr(self):
+ name = x509.Name([
+ x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
+ ])
+
+ assert repr(name) == (
+ "<Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=com"
+ "monName)>, value='cryptography.io')>, <NameAttribute(oid=<ObjectI"
+ "dentifier(oid=2.5.4.10, name=organizationName)>, value='PyCA')>])"
+ ">"
+ )
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 7fccaba..c2d33d9 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -4,10 +4,13 @@
from __future__ import absolute_import, division, print_function
+import binascii
import os
import pytest
+import six
+
from cryptography import x509
from cryptography.hazmat.backends.interfaces import RSABackend, X509Backend
@@ -170,6 +173,52 @@
)
+class TestSubjectKeyIdentifier(object):
+ def test_properties(self):
+ value = binascii.unhexlify(b"092384932230498bc980aa8098456f6ff7ff3ac9")
+ ski = x509.SubjectKeyIdentifier(value)
+ assert ski.digest == value
+
+ def test_repr(self):
+ ski = x509.SubjectKeyIdentifier(
+ binascii.unhexlify(b"092384932230498bc980aa8098456f6ff7ff3ac9")
+ )
+ ext = x509.Extension(x509.OID_SUBJECT_KEY_IDENTIFIER, False, ski)
+ if six.PY3:
+ assert repr(ext) == (
+ "<Extension(oid=<ObjectIdentifier(oid=2.5.29.14, name=subjectK"
+ "eyIdentifier)>, critical=False, value=<SubjectKeyIdentifier(d"
+ "igest=b\'\\t#\\x84\\x93\"0I\\x8b\\xc9\\x80\\xaa\\x80\\x98Eoo"
+ "\\xf7\\xff:\\xc9\')>)>"
+ )
+ else:
+ assert repr(ext) == (
+ "<Extension(oid=<ObjectIdentifier(oid=2.5.29.14, name=subjectK"
+ "eyIdentifier)>, critical=False, value=<SubjectKeyIdentifier(d"
+ "igest=\'\\t#\\x84\\x93\"0I\\x8b\\xc9\\x80\\xaa\\x80\\x98Eoo"
+ "\\xf7\\xff:\\xc9\')>)>"
+ )
+
+ def test_eq(self):
+ ski = x509.SubjectKeyIdentifier(
+ binascii.unhexlify(b"092384932230498bc980aa8098456f6ff7ff3ac9")
+ )
+ ski2 = x509.SubjectKeyIdentifier(
+ binascii.unhexlify(b"092384932230498bc980aa8098456f6ff7ff3ac9")
+ )
+ assert ski == ski2
+
+ def test_ne(self):
+ ski = x509.SubjectKeyIdentifier(
+ binascii.unhexlify(b"092384932230498bc980aa8098456f6ff7ff3ac9")
+ )
+ ski2 = x509.SubjectKeyIdentifier(
+ binascii.unhexlify(b"aa8098456f6ff7ff3ac9092384932230498bc980")
+ )
+ assert ski != ski2
+ assert ski != object()
+
+
class TestBasicConstraints(object):
def test_ca_not_boolean(self):
with pytest.raises(TypeError):
@@ -213,6 +262,17 @@
x509.OID_CLIENT_AUTH
]
+ def test_repr(self):
+ eku = x509.ExtendedKeyUsage([
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.1"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.2"),
+ ])
+ assert repr(eku) == (
+ "<ExtendedKeyUsage([<ObjectIdentifier(oid=1.3.6.1.5.5.7.3.1, name="
+ "serverAuth)>, <ObjectIdentifier(oid=1.3.6.1.5.5.7.3.2, name=clien"
+ "tAuth)>])>"
+ )
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
@@ -372,3 +432,34 @@
assert ext is not None
assert ext.critical is False
assert ext.value.ca is False
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestSubjectKeyIdentifierExtension(object):
+ def test_subject_key_identifier(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
+ x509.load_der_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_KEY_IDENTIFIER
+ )
+ ski = ext.value
+ assert ext is not None
+ assert ext.critical is False
+ assert ski.digest == binascii.unhexlify(
+ b"580184241bbc2b52944a3da510721451f5af3ac9"
+ )
+
+ def test_no_subject_key_identifier(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "custom", "bc_path_length_zero.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ with pytest.raises(x509.ExtensionNotFound):
+ cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_KEY_IDENTIFIER
+ )
diff --git a/vectors/cryptography_vectors/x509/custom/all_key_usages.pem b/vectors/cryptography_vectors/x509/custom/all_key_usages.pem
new file mode 100644
index 0000000..e24caf2
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/all_key_usages.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6TCCAdGgAwIBAgITBmdJiX54UcRwde48tbHzferkszANBgkqhkiG9w0BAQUF
+ADAmMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkxDTALBgNVBAoMBFB5Q0EwHhcNMTUw
+NDAyMTgyMzIwWhcNMTYwNDAxMTgyMzIwWjAmMRUwEwYDVQQDDAxjcnlwdG9ncmFw
+aHkxDTALBgNVBAoMBFB5Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCtKAh5oo9zN6ATc8c7+kJNpi9uOOE1650X6H6F4XpDQGmg7uoOGpvZqi4BwkiJ
+6tE6IR9qmfuksrSmVraBFGfXLKVknLBpB9aAAeBv7Kh7nrPfd39fbliwbW7NJIHH
++nG02FLPPMOdUKNyfTY2+kthkrjGCjYqOTEIUkFLgxk3/V2bmTSEiNGmi/1qQl9Y
+pRJlaMYhDkS01Ox7/20uYI/S8EAXWQefV23szoesiiS6QvVncBRUeYCj+rQsr2+p
+wXz1TWypLEukjo+C6SyjVEYUMUwYKg/0hBMQIpnAWuiNiiJArWf+l9a4O1SgsPG7
+nwWClir6jr33LBD5DF1cJCS3AgMBAAGjEDAOMAwGA1UdDwQFAwMH/4AwDQYJKoZI
+hvcNAQEFBQADggEBAEJUuCiqMQYZowPi9OmyHGk7vAxh2MCKDQJDI1DhNdCPNoOl
+nGSrNiFVRh6PAh3i+QSoh3pvbFvP0pCgasoaukqxKPK9pCKzBrwwsA7U7hvtJlIp
+gOb5RG55mPDl5SxSJyHlOPHotG9ACeQOvbfqn3KM9Jn5aBir/laRKsSrM/daeeZ8
+4LQOb5pSNK41NKxeidm1AdNEMt33duYkhWZ63gviYvr6ri+3OOHhlFZeCI297TW9
+dHZpYMwi3hN7jYJLh5NFBNlnngG92lMcYfSBntjeCN2uPwO72utKMYb3kF/JxglX
+dxeA+zWQjvhx3s2Zt8/N10JnnbKImPJdCA59X3M=
+-----END CERTIFICATE-----