aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst8
-rw-r--r--docs/development/test-vectors.rst2
-rw-r--r--docs/fernet.rst6
-rw-r--r--docs/hazmat/primitives/asymmetric/dh.rst98
-rw-r--r--docs/hazmat/primitives/asymmetric/interfaces.rst1
-rw-r--r--docs/spelling_wordlist.txt1
-rw-r--r--docs/x509.rst14
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py15
-rw-r--r--src/cryptography/hazmat/bindings/openssl/asn1.py4
-rw-r--r--src/cryptography/hazmat/bindings/openssl/pem.py4
-rw-r--r--src/cryptography/hazmat/bindings/openssl/pkcs7.py30
-rw-r--r--src/cryptography/hazmat/bindings/openssl/ssl.py6
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509.py17
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dh.py89
-rw-r--r--src/cryptography/x509.py61
-rw-r--r--tests/test_x509.py13
-rw-r--r--tests/test_x509_ext.py91
-rw-r--r--vectors/cryptography_vectors/x509/custom/all_key_usages.pem18
18 files changed, 454 insertions, 24 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b520e48e..fd92a56b 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -10,6 +10,8 @@ Changelog
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 @@ Changelog
: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 e2b621c3..5353b1d2 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -132,6 +132,8 @@ Custom X.509 Vectors
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 f1a4c748..eacbc2ae 100644
--- a/docs/fernet.rst
+++ b/docs/fernet.rst
@@ -92,8 +92,10 @@ has support for implementing key rotation via :class:`MultiFernet`.
>>> 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 fdf113f7..dde18cf7 100644
--- a/docs/hazmat/primitives/asymmetric/dh.rst
+++ b/docs/hazmat/primitives/asymmetric/dh.rst
@@ -6,6 +6,9 @@ Diffie-Hellman key exchange
.. currentmodule:: cryptography.hazmat.primitives.asymmetric.dh
+Numbers
+~~~~~~~
+
.. class:: DHPrivateNumbers(x, public_numbers)
.. versionadded:: 0.8
@@ -62,3 +65,98 @@ Diffie-Hellman key exchange
: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 4932faa5..c4f176c6 100644
--- a/docs/hazmat/primitives/asymmetric/interfaces.rst
+++ b/docs/hazmat/primitives/asymmetric/interfaces.rst
@@ -30,4 +30,3 @@ Signature Interfaces
:raises cryptography.exceptions.InvalidSignature: If the signature does
not validate.
-
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index c029b5df..f7b73b38 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -20,6 +20,7 @@ decrypted
decrypting
deserialize
deserialized
+Diffie
Docstrings
Encodings
fernet
diff --git a/docs/x509.rst b/docs/x509.rst
index afc9620a..d09651fb 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -275,6 +275,7 @@ X.509 Certificate Object
>>> 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 @@ X.509 Extensions
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 6a7032ba..5d47c5ea 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -170,6 +170,8 @@ class _Certificate(object):
)
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 @@ class _Certificate(object):
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 d8b8331e..45dfe758 100644
--- a/src/cryptography/hazmat/bindings/openssl/asn1.py
+++ b/src/cryptography/hazmat/bindings/openssl/asn1.py
@@ -40,6 +40,7 @@ struct asn1_string_st {
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 @@ int ASN1_ENUMERATED_set(ASN1_ENUMERATED *, long);
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 98c7648f..8ec3fefd 100644
--- a/src/cryptography/hazmat/bindings/openssl/pem.py
+++ b/src/cryptography/hazmat/bindings/openssl/pem.py
@@ -32,7 +32,9 @@ int i2d_PKCS8PrivateKey_bio(BIO *, EVP_PKEY *, const EVP_CIPHER *,
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 @@ X509_CRL *PEM_read_bio_X509_CRL(BIO *, X509_CRL **, pem_password_cb *, void *);
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 3196fa13..df82afef 100644
--- a/src/cryptography/hazmat/bindings/openssl/pkcs7.py
+++ b/src/cryptography/hazmat/bindings/openssl/pkcs7.py
@@ -13,10 +13,37 @@ typedef struct {
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_signedAndEnveloped(PKCS7 *);
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 bc4b2e79..d680c3a5 100644
--- a/src/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/src/cryptography/hazmat/bindings/openssl/ssl.py
@@ -546,11 +546,7 @@ static const long Cryptography_HAS_ALPN = 0;
#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 e867450b..b5c9ee14 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -168,6 +168,9 @@ void X509_REVOKED_free(X509_REVOKED *);
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 i2d_X509_CRL_bio(BIO *, X509_CRL *);
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 @@ void sk_X509_EXTENSION_free(X509_EXTENSIONS *);
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 61556efb..12d53eed 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 @@ class DHParameterNumbers(object):
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 17ef6ddd..a9b6f8bd 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -48,9 +48,24 @@ _OID_NAMES = {
"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 @@ class Name(object):
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 @@ class ExtendedKeyUsage(object):
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 @@ class KeyUsage(object):
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 dc148d9d..d8a4cc02 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -607,3 +607,16 @@ class TestName(object):
])
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 7fccaba6..c2d33d92 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 TestKeyUsage(object):
)
+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 @@ class TestExtendedKeyUsage(object):
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 @@ class TestBasicConstraintsExtension(object):
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 00000000..e24caf28
--- /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-----