aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/_cffi_src/build_commoncrypto.py1
-rw-r--r--src/_cffi_src/build_constant_time.py5
-rw-r--r--src/_cffi_src/build_openssl.py6
-rw-r--r--src/_cffi_src/build_padding.py5
-rw-r--r--src/_cffi_src/commoncrypto/sectrust.py22
-rw-r--r--src/_cffi_src/openssl/asn1.py3
-rw-r--r--src/_cffi_src/openssl/bignum.py2
-rw-r--r--src/_cffi_src/openssl/err.py1
-rw-r--r--src/_cffi_src/openssl/pem.py1
-rw-r--r--src/_cffi_src/openssl/x509.py13
-rw-r--r--src/_cffi_src/utils.py27
-rw-r--r--src/cryptography/__about__.py2
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py14
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py18
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py366
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py1
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py108
-rw-r--r--src/cryptography/hazmat/bindings/openssl/binding.py9
-rw-r--r--src/cryptography/utils.py3
-rw-r--r--src/cryptography/x509/__init__.py31
-rw-r--r--src/cryptography/x509/base.py156
-rw-r--r--src/cryptography/x509/extensions.py163
-rw-r--r--src/cryptography/x509/oid.py16
23 files changed, 797 insertions, 176 deletions
diff --git a/src/_cffi_src/build_commoncrypto.py b/src/_cffi_src/build_commoncrypto.py
index 1c2692a7..4e69b6d1 100644
--- a/src/_cffi_src/build_commoncrypto.py
+++ b/src/_cffi_src/build_commoncrypto.py
@@ -22,6 +22,7 @@ ffi = build_ffi_for_binding(
"seckey",
"seckeychain",
"sectransform",
+ "sectrust",
],
extra_link_args=[
"-framework", "Security", "-framework", "CoreFoundation"
diff --git a/src/_cffi_src/build_constant_time.py b/src/_cffi_src/build_constant_time.py
index 6d9a8f54..7a11f7b5 100644
--- a/src/_cffi_src/build_constant_time.py
+++ b/src/_cffi_src/build_constant_time.py
@@ -5,9 +5,8 @@
from __future__ import absolute_import, division, print_function
import os
-import sys
-from _cffi_src.utils import build_ffi, extra_link_args
+from _cffi_src.utils import build_ffi, compiler_type, extra_link_args
with open(os.path.join(
@@ -24,5 +23,5 @@ ffi = build_ffi(
module_name="_constant_time",
cdef_source=types,
verify_source=functions,
- extra_link_args=extra_link_args(sys.platform),
+ extra_link_args=extra_link_args(compiler_type()),
)
diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py
index c856e3d9..c47b3082 100644
--- a/src/_cffi_src/build_openssl.py
+++ b/src/_cffi_src/build_openssl.py
@@ -7,7 +7,9 @@ from __future__ import absolute_import, division, print_function
import os
import sys
-from _cffi_src.utils import build_ffi_for_binding, extra_link_args
+from _cffi_src.utils import (
+ build_ffi_for_binding, compiler_type, extra_link_args
+)
def _get_openssl_libraries(platform):
@@ -92,5 +94,5 @@ ffi = build_ffi_for_binding(
pre_include=_OSX_PRE_INCLUDE,
post_include=_OSX_POST_INCLUDE,
libraries=_get_openssl_libraries(sys.platform),
- extra_link_args=extra_link_args(sys.platform),
+ extra_link_args=extra_link_args(compiler_type()),
)
diff --git a/src/_cffi_src/build_padding.py b/src/_cffi_src/build_padding.py
index 5df93d80..4c5096a1 100644
--- a/src/_cffi_src/build_padding.py
+++ b/src/_cffi_src/build_padding.py
@@ -5,9 +5,8 @@
from __future__ import absolute_import, division, print_function
import os
-import sys
-from _cffi_src.utils import build_ffi, extra_link_args
+from _cffi_src.utils import build_ffi, compiler_type, extra_link_args
with open(os.path.join(
@@ -24,5 +23,5 @@ ffi = build_ffi(
module_name="_padding",
cdef_source=types,
verify_source=functions,
- extra_link_args=extra_link_args(sys.platform),
+ extra_link_args=extra_link_args(compiler_type()),
)
diff --git a/src/_cffi_src/commoncrypto/sectrust.py b/src/_cffi_src/commoncrypto/sectrust.py
new file mode 100644
index 00000000..b787afad
--- /dev/null
+++ b/src/_cffi_src/commoncrypto/sectrust.py
@@ -0,0 +1,22 @@
+# 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
+
+INCLUDES = """
+#include <Security/SecTrust.h>
+"""
+
+TYPES = """
+"""
+
+FUNCTIONS = """
+OSStatus SecTrustCopyAnchorCertificates(CFArrayRef *);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py
index ddf4b9c5..30bd2451 100644
--- a/src/_cffi_src/openssl/asn1.py
+++ b/src/_cffi_src/openssl/asn1.py
@@ -95,13 +95,16 @@ ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *, time_t);
/* ASN1 GENERALIZEDTIME */
int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *, const char *);
+ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *, time_t);
void ASN1_GENERALIZEDTIME_free(ASN1_GENERALIZEDTIME *);
+int i2d_ASN1_GENERALIZEDTIME(ASN1_GENERALIZEDTIME *, unsigned char **);
/* ASN1 ENUMERATED */
ASN1_ENUMERATED *ASN1_ENUMERATED_new(void);
void ASN1_ENUMERATED_free(ASN1_ENUMERATED *);
int ASN1_ENUMERATED_set(ASN1_ENUMERATED *, long);
long ASN1_ENUMERATED_get(ASN1_ENUMERATED *);
+int i2d_ASN1_ENUMERATED(ASN1_ENUMERATED *, unsigned char **);
ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **, const unsigned char **, long,
const ASN1_ITEM *);
diff --git a/src/_cffi_src/openssl/bignum.py b/src/_cffi_src/openssl/bignum.py
index ae035007..455afdc1 100644
--- a/src/_cffi_src/openssl/bignum.py
+++ b/src/_cffi_src/openssl/bignum.py
@@ -71,6 +71,8 @@ int BN_mask_bits(BIGNUM *, int);
"""
MACROS = """
+int BN_num_bytes(const BIGNUM *);
+
int BN_zero(BIGNUM *);
int BN_one(BIGNUM *);
int BN_mod(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *);
diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py
index 6ec13775..9d97be16 100644
--- a/src/_cffi_src/openssl/err.py
+++ b/src/_cffi_src/openssl/err.py
@@ -230,6 +230,7 @@ static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY;
static const int RSA_R_BLOCK_TYPE_IS_NOT_01;
static const int RSA_R_BLOCK_TYPE_IS_NOT_02;
static const int RSA_R_PKCS_DECODING_ERROR;
+static const int RSA_R_OAEP_DECODING_ERROR;
static const int RSA_F_RSA_SIGN;
"""
diff --git a/src/_cffi_src/openssl/pem.py b/src/_cffi_src/openssl/pem.py
index 846e64e3..4eb6bb45 100644
--- a/src/_cffi_src/openssl/pem.py
+++ b/src/_cffi_src/openssl/pem.py
@@ -79,6 +79,7 @@ MACROS = """
int PEM_write_bio_ECPrivateKey(BIO *, EC_KEY *, const EVP_CIPHER *,
unsigned char *, int, pem_password_cb *,
void *);
+int PEM_write_bio_DHparams(BIO *, DH *);
"""
CUSTOMIZATIONS = """
diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py
index 0fc49ac5..c5eb600a 100644
--- a/src/_cffi_src/openssl/x509.py
+++ b/src/_cffi_src/openssl/x509.py
@@ -193,6 +193,8 @@ 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);
+int X509_REVOKED_set_revocationDate(X509_REVOKED *, ASN1_TIME *);
+
X509_CRL *X509_CRL_new(void);
X509_CRL *d2i_X509_CRL_bio(BIO *, X509_CRL **);
X509_EXTENSION *X509_CRL_get_ext(X509_CRL *, int);
@@ -268,6 +270,8 @@ void PKCS8_PRIV_KEY_INFO_free(PKCS8_PRIV_KEY_INFO *);
"""
MACROS = """
+X509_REVOKED *Cryptography_X509_REVOKED_dup(X509_REVOKED *);
+
int i2d_X509_CINF(X509_CINF *, unsigned char **);
int i2d_X509_CRL_INFO(X509_CRL_INFO *, unsigned char **);
int i2d_X509_REQ_INFO(X509_REQ_INFO *, unsigned char **);
@@ -290,6 +294,7 @@ X509_EXTENSIONS *sk_X509_EXTENSION_new_null(void);
int sk_X509_EXTENSION_num(X509_EXTENSIONS *);
X509_EXTENSION *sk_X509_EXTENSION_value(X509_EXTENSIONS *, int);
int sk_X509_EXTENSION_push(X509_EXTENSIONS *, X509_EXTENSION *);
+int sk_X509_EXTENSION_insert(X509_EXTENSIONS *, X509_EXTENSION *, int);
X509_EXTENSION *sk_X509_EXTENSION_delete(X509_EXTENSIONS *, int);
void sk_X509_EXTENSION_free(X509_EXTENSIONS *);
@@ -362,4 +367,12 @@ int (*i2d_ECPrivateKey_bio)(BIO *, EC_KEY *) = NULL;
EC_KEY *(*o2i_ECPublicKey)(EC_KEY **, const unsigned char **, long) = NULL;
int (*i2o_ECPublicKey)(EC_KEY *, unsigned char **) = NULL;
#endif
+
+/* X509_REVOKED_dup only exists on 1.0.2+. It is implemented using
+ IMPLEMENT_ASN1_DUP_FUNCTION. The below is the equivalent so we have
+ it available on all OpenSSLs. */
+X509_REVOKED *Cryptography_X509_REVOKED_dup(X509_REVOKED *rev) {
+ return ASN1_item_dup(ASN1_ITEM_rptr(X509_REVOKED), rev);
+}
+
"""
diff --git a/src/_cffi_src/utils.py b/src/_cffi_src/utils.py
index 0b00353e..bdce2f3b 100644
--- a/src/_cffi_src/utils.py
+++ b/src/_cffi_src/utils.py
@@ -5,6 +5,8 @@
from __future__ import absolute_import, division, print_function
import sys
+from distutils.ccompiler import new_compiler
+from distutils.dist import Distribution
from cffi import FFI
@@ -79,10 +81,23 @@ def build_ffi(module_name, cdef_source, verify_source, libraries=[],
return ffi
-def extra_link_args(platform):
- if platform != "win32":
- return []
+def extra_link_args(compiler_type):
+ if compiler_type == 'msvc':
+ # Enable NX and ASLR for Windows builds on MSVC. These are enabled by
+ # default on Python 3.3+ but not on 2.x.
+ return ['/NXCOMPAT', '/DYNAMICBASE']
else:
- # Enable NX and ASLR for Windows builds. These are enabled by default
- # on Python 3.3+ but not on 2.x.
- return ["/NXCOMPAT", "/DYNAMICBASE"]
+ return []
+
+
+def compiler_type():
+ """
+ Gets the compiler type from distutils. On Windows with MSVC it will be
+ "msvc". On OS X and linux it is "unix".
+ """
+ dist = Distribution()
+ dist.parse_config_files()
+ cmd = dist.get_command_obj('build')
+ cmd.ensure_finalized()
+ compiler = new_compiler(compiler=cmd.compiler)
+ return compiler.compiler_type
diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py
index fbc1e735..f25cbc89 100644
--- a/src/cryptography/__about__.py
+++ b/src/cryptography/__about__.py
@@ -20,4 +20,4 @@ __author__ = "The cryptography developers"
__email__ = "cryptography-dev@python.org"
__license__ = "BSD or Apache License, Version 2.0"
-__copyright__ = "Copyright 2013-2015 {0}".format(__author__)
+__copyright__ = "Copyright 2013-2016 {0}".format(__author__)
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 92d9653a..5b9e6f38 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -292,6 +292,20 @@ class X509Backend(object):
Create and sign an X.509 certificate from a CertificateBuilder object.
"""
+ @abc.abstractmethod
+ def create_x509_crl(self, builder, private_key, algorithm):
+ """
+ Create and sign an X.509 CertificateRevocationList from a
+ CertificateRevocationListBuilder object.
+ """
+
+ @abc.abstractmethod
+ def create_x509_revoked_certificate(self, builder):
+ """
+ Create a RevokedCertificate object from a RevokedCertificateBuilder
+ object.
+ """
+
@six.add_metaclass(abc.ABCMeta)
class DHBackend(object):
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index bbaaf424..65f18531 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -384,3 +384,21 @@ class MultiBackend(object):
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
+
+ def create_x509_crl(self, builder, private_key, algorithm):
+ for b in self._filtered_backends(X509Backend):
+ return b.create_x509_crl(builder, private_key, algorithm)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support X.509.",
+ _Reasons.UNSUPPORTED_X509
+ )
+
+ def create_x509_revoked_certificate(self, builder):
+ for b in self._filtered_backends(X509Backend):
+ return b.create_x509_revoked_certificate(builder)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support X.509.",
+ _Reasons.UNSUPPORTED_X509
+ )
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index e69554f9..e8b0322e 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -37,8 +37,9 @@ from cryptography.hazmat.backends.openssl.rsa import (
_RSAPrivateKey, _RSAPublicKey
)
from cryptography.hazmat.backends.openssl.x509 import (
- _Certificate, _CertificateRevocationList, _CertificateSigningRequest,
- _DISTPOINT_TYPE_FULLNAME, _DISTPOINT_TYPE_RELATIVENAME
+ _CRL_ENTRY_REASON_ENUM_TO_CODE, _Certificate, _CertificateRevocationList,
+ _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME,
+ _DISTPOINT_TYPE_RELATIVENAME, _RevokedCertificate
)
from cryptography.hazmat.bindings._openssl import ffi as _ffi
from cryptography.hazmat.bindings.openssl import binding
@@ -53,7 +54,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import (
from cryptography.hazmat.primitives.ciphers.modes import (
CBC, CFB, CFB8, CTR, ECB, GCM, OFB
)
-from cryptography.x509.oid import ExtensionOID, NameOID
+from cryptography.x509.oid import CRLEntryExtensionOID, ExtensionOID, NameOID
_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])
@@ -115,10 +116,9 @@ def _encode_asn1_str_gc(backend, data, length):
return s
-def _encode_inhibit_any_policy(backend, inhibit_any_policy):
- asn1int = _encode_asn1_int_gc(backend, inhibit_any_policy.skip_certs)
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_ASN1_INTEGER(asn1int, pp)
+def _encode_extension_to_der(backend, i2d_func, value):
+ pp = backend._ffi.new("unsigned char **")
+ r = i2d_func(value, pp)
backend.openssl_assert(r > 0)
pp = backend._ffi.gc(
pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
@@ -126,6 +126,13 @@ def _encode_inhibit_any_policy(backend, inhibit_any_policy):
return pp, r
+def _encode_inhibit_any_policy(backend, inhibit_any_policy):
+ asn1int = _encode_asn1_int_gc(backend, inhibit_any_policy.skip_certs)
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_ASN1_INTEGER, asn1int
+ )
+
+
def _encode_name(backend, attributes):
"""
The X509_NAME created will not be gc'd. Use _encode_name_gc if needed.
@@ -153,6 +160,41 @@ def _encode_name_gc(backend, attributes):
return subject
+def _encode_crl_number(backend, crl_number):
+ asn1int = _encode_asn1_int_gc(backend, crl_number.crl_number)
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_ASN1_INTEGER, asn1int
+ )
+
+
+def _encode_crl_reason(backend, crl_reason):
+ asn1enum = backend._lib.ASN1_ENUMERATED_new()
+ backend.openssl_assert(asn1enum != backend._ffi.NULL)
+ asn1enum = backend._ffi.gc(asn1enum, backend._lib.ASN1_ENUMERATED_free)
+ res = backend._lib.ASN1_ENUMERATED_set(
+ asn1enum, _CRL_ENTRY_REASON_ENUM_TO_CODE[crl_reason.reason]
+ )
+ backend.openssl_assert(res == 1)
+
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_ASN1_ENUMERATED, asn1enum
+ )
+
+
+def _encode_invalidity_date(backend, invalidity_date):
+ time = backend._lib.ASN1_GENERALIZEDTIME_set(
+ backend._ffi.NULL, calendar.timegm(
+ invalidity_date.invalidity_date.timetuple()
+ )
+ )
+ backend.openssl_assert(time != backend._ffi.NULL)
+ time = backend._ffi.gc(time, backend._lib.ASN1_GENERALIZEDTIME_free)
+
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_ASN1_GENERALIZEDTIME, time
+ )
+
+
def _encode_certificate_policies(backend, certificate_policies):
cp = backend._lib.sk_POLICYINFO_new_null()
backend.openssl_assert(cp != backend._ffi.NULL)
@@ -200,13 +242,9 @@ def _encode_certificate_policies(backend, certificate_policies):
pi.qualifiers = pqis
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_CERTIFICATEPOLICIES(cp, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_CERTIFICATEPOLICIES, cp
)
- return pp, r
def _encode_notice_reference(backend, notice):
@@ -282,13 +320,9 @@ def _encode_key_usage(backend, key_usage):
res = set_bit(ku, 8, 0)
backend.openssl_assert(res == 1)
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_ASN1_BIT_STRING(ku, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_ASN1_BIT_STRING, ku
)
- return pp, r
def _encode_authority_key_identifier(backend, authority_keyid):
@@ -312,13 +346,9 @@ def _encode_authority_key_identifier(backend, authority_keyid):
backend, authority_keyid.authority_cert_serial_number
)
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_AUTHORITY_KEYID(akid, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_AUTHORITY_KEYID, akid
)
- return pp, r
def _encode_basic_constraints(backend, basic_constraints):
@@ -332,14 +362,9 @@ def _encode_basic_constraints(backend, basic_constraints):
backend, basic_constraints.path_length
)
- # Fetch the encoded payload.
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_BASIC_CONSTRAINTS, constraints
)
- return pp, r
def _encode_authority_information_access(backend, authority_info_access):
@@ -359,13 +384,9 @@ def _encode_authority_information_access(backend, authority_info_access):
res = backend._lib.sk_ACCESS_DESCRIPTION_push(aia, ad)
backend.openssl_assert(res >= 1)
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_AUTHORITY_INFO_ACCESS(aia, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_AUTHORITY_INFO_ACCESS, aia
)
- return pp, r
def _encode_general_names(backend, names):
@@ -384,24 +405,16 @@ def _encode_alt_name(backend, san):
general_names = backend._ffi.gc(
general_names, backend._lib.GENERAL_NAMES_free
)
- pp = backend._ffi.new("unsigned char **")
- r = backend._lib.i2d_GENERAL_NAMES(general_names, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_GENERAL_NAMES, general_names
)
- return pp, r
def _encode_subject_key_identifier(backend, ski):
asn1_str = _encode_asn1_str_gc(backend, ski.digest, len(ski.digest))
- pp = backend._ffi.new("unsigned char **")
- r = backend._lib.i2d_ASN1_OCTET_STRING(asn1_str, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_ASN1_OCTET_STRING, asn1_str
)
- return pp, r
def _encode_general_name(backend, name):
@@ -499,15 +512,10 @@ def _encode_extended_key_usage(backend, extended_key_usage):
res = backend._lib.sk_ASN1_OBJECT_push(eku, obj)
backend.openssl_assert(res >= 1)
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_EXTENDED_KEY_USAGE(
- backend._ffi.cast("EXTENDED_KEY_USAGE *", eku), pp
- )
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ eku_ptr = backend._ffi.cast("EXTENDED_KEY_USAGE *", eku)
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_EXTENDED_KEY_USAGE, eku_ptr
)
- return pp, r
_CRLREASONFLAGS = {
@@ -562,13 +570,9 @@ def _encode_crl_distribution_points(backend, crl_distribution_points):
res = backend._lib.sk_DIST_POINT_push(cdp, dp)
backend.openssl_assert(res >= 1)
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.i2d_CRL_DIST_POINTS(cdp, pp)
- backend.openssl_assert(r > 0)
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.i2d_CRL_DIST_POINTS, cdp
)
- return pp, r
def _encode_name_constraints(backend, name_constraints):
@@ -584,13 +588,9 @@ def _encode_name_constraints(backend, name_constraints):
)
nc.excludedSubtrees = excluded
- pp = backend._ffi.new('unsigned char **')
- r = backend._lib.Cryptography_i2d_NAME_CONSTRAINTS(nc, pp)
- assert r > 0
- pp = backend._ffi.gc(
- pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ return _encode_extension_to_der(
+ backend, backend._lib.Cryptography_i2d_NAME_CONSTRAINTS, nc
)
- return pp, r
def _encode_general_subtree(backend, subtrees):
@@ -625,6 +625,21 @@ _EXTENSION_ENCODE_HANDLERS = {
ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints,
}
+_CRL_EXTENSION_ENCODE_HANDLERS = {
+ ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
+ ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
+ ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
+ _encode_authority_information_access
+ ),
+ ExtensionOID.CRL_NUMBER: _encode_crl_number,
+}
+
+_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = {
+ CRLEntryExtensionOID.CERTIFICATE_ISSUER: _encode_alt_name,
+ CRLEntryExtensionOID.CRL_REASON: _encode_crl_reason,
+ CRLEntryExtensionOID.INVALIDITY_DATE: _encode_invalidity_date,
+}
+
class _PasswordUserdata(object):
def __init__(self, password):
@@ -899,17 +914,14 @@ class Backend(object):
assert bn != self._ffi.NULL
if six.PY3:
# Python 3 has constant time from_bytes, so use that.
-
- bn_num_bytes = (self._lib.BN_num_bits(bn) + 7) // 8
+ bn_num_bytes = self._lib.BN_num_bytes(bn)
bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes)
bin_len = self._lib.BN_bn2bin(bn, bin_ptr)
# A zero length means the BN has value 0
self.openssl_assert(bin_len >= 0)
return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")
-
else:
# Under Python 2 the best we can do is hex()
-
hex_cdata = self._lib.BN_bn2hex(bn)
self.openssl_assert(hex_cdata != self._ffi.NULL)
hex_str = self._ffi.string(hex_cdata)
@@ -1312,30 +1324,21 @@ class Backend(object):
self.openssl_assert(res == 1)
# Add extensions.
- extensions = self._lib.sk_X509_EXTENSION_new_null()
- self.openssl_assert(extensions != self._ffi.NULL)
- extensions = self._ffi.gc(
- extensions,
- self._lib.sk_X509_EXTENSION_free,
+ sk_extension = self._lib.sk_X509_EXTENSION_new_null()
+ self.openssl_assert(sk_extension != self._ffi.NULL)
+ sk_extension = self._ffi.gc(
+ sk_extension, self._lib.sk_X509_EXTENSION_free
)
- for extension in builder._extensions:
- try:
- encode = _EXTENSION_ENCODE_HANDLERS[extension.oid]
- except KeyError:
- raise NotImplementedError('Extension not yet supported.')
-
- pp, r = encode(self, extension.value)
- obj = _txt2obj_gc(self, extension.oid.dotted_string)
- extension = self._lib.X509_EXTENSION_create_by_OBJ(
- self._ffi.NULL,
- obj,
- 1 if extension.critical else 0,
- _encode_asn1_str_gc(self, pp[0], r),
- )
- self.openssl_assert(extension != self._ffi.NULL)
- res = self._lib.sk_X509_EXTENSION_push(extensions, extension)
- self.openssl_assert(res >= 1)
- res = self._lib.X509_REQ_add_extensions(x509_req, extensions)
+ # gc is not necessary for CSRs, as sk_X509_EXTENSION_free
+ # will release all the X509_EXTENSIONs.
+ self._create_x509_extensions(
+ extensions=builder._extensions,
+ handlers=_EXTENSION_ENCODE_HANDLERS,
+ x509_obj=sk_extension,
+ add_func=self._lib.sk_X509_EXTENSION_insert,
+ gc=False
+ )
+ res = self._lib.X509_REQ_add_extensions(x509_req, sk_extension)
self.openssl_assert(res == 1)
# Sign the request using the requester's private key.
@@ -1416,24 +1419,13 @@ class Backend(object):
self.openssl_assert(res != self._ffi.NULL)
# Add extensions.
- for i, extension in enumerate(builder._extensions):
- try:
- encode = _EXTENSION_ENCODE_HANDLERS[extension.oid]
- except KeyError:
- raise NotImplementedError('Extension not yet supported.')
-
- pp, r = encode(self, extension.value)
- obj = _txt2obj_gc(self, extension.oid.dotted_string)
- extension = self._lib.X509_EXTENSION_create_by_OBJ(
- self._ffi.NULL,
- obj,
- 1 if extension.critical else 0,
- _encode_asn1_str_gc(self, pp[0], r)
- )
- self.openssl_assert(extension != self._ffi.NULL)
- extension = self._ffi.gc(extension, self._lib.X509_EXTENSION_free)
- res = self._lib.X509_add_ext(x509_cert, extension, i)
- self.openssl_assert(res == 1)
+ self._create_x509_extensions(
+ extensions=builder._extensions,
+ handlers=_EXTENSION_ENCODE_HANDLERS,
+ x509_obj=x509_cert,
+ add_func=self._lib.X509_add_ext,
+ gc=True
+ )
# Set the issuer name.
res = self._lib.X509_set_issuer_name(
@@ -1455,6 +1447,147 @@ class Backend(object):
return _Certificate(self, x509_cert)
+ def create_x509_crl(self, builder, private_key, algorithm):
+ if not isinstance(builder, x509.CertificateRevocationListBuilder):
+ raise TypeError('Builder type mismatch.')
+ if not isinstance(algorithm, hashes.HashAlgorithm):
+ raise TypeError('Algorithm must be a registered hash algorithm.')
+
+ if self._lib.OPENSSL_VERSION_NUMBER <= 0x10001000:
+ if isinstance(private_key, _DSAPrivateKey):
+ raise NotImplementedError(
+ "CRL signatures aren't implemented for DSA"
+ " keys on OpenSSL versions less than 1.0.1."
+ )
+ if isinstance(private_key, _EllipticCurvePrivateKey):
+ raise NotImplementedError(
+ "CRL signatures aren't implemented for EC"
+ " keys on OpenSSL versions less than 1.0.1."
+ )
+
+ evp_md = self._lib.EVP_get_digestbyname(
+ algorithm.name.encode('ascii')
+ )
+ self.openssl_assert(evp_md != self._ffi.NULL)
+
+ # Create an empty CRL.
+ x509_crl = self._lib.X509_CRL_new()
+ x509_crl = self._ffi.gc(x509_crl, backend._lib.X509_CRL_free)
+
+ # Set the x509 CRL version. We only support v2 (integer value 1).
+ res = self._lib.X509_CRL_set_version(x509_crl, 1)
+ self.openssl_assert(res == 1)
+
+ # Set the issuer name.
+ res = self._lib.X509_CRL_set_issuer_name(
+ x509_crl, _encode_name_gc(self, list(builder._issuer_name))
+ )
+ self.openssl_assert(res == 1)
+
+ # Set the last update time.
+ last_update = self._lib.ASN1_TIME_set(
+ self._ffi.NULL, calendar.timegm(builder._last_update.timetuple())
+ )
+ self.openssl_assert(last_update != self._ffi.NULL)
+ last_update = self._ffi.gc(last_update, self._lib.ASN1_TIME_free)
+ res = self._lib.X509_CRL_set_lastUpdate(x509_crl, last_update)
+ self.openssl_assert(res == 1)
+
+ # Set the next update time.
+ next_update = self._lib.ASN1_TIME_set(
+ self._ffi.NULL, calendar.timegm(builder._next_update.timetuple())
+ )
+ self.openssl_assert(next_update != self._ffi.NULL)
+ next_update = self._ffi.gc(next_update, self._lib.ASN1_TIME_free)
+ res = self._lib.X509_CRL_set_nextUpdate(x509_crl, next_update)
+ self.openssl_assert(res == 1)
+
+ # Add extensions.
+ self._create_x509_extensions(
+ extensions=builder._extensions,
+ handlers=_CRL_EXTENSION_ENCODE_HANDLERS,
+ x509_obj=x509_crl,
+ add_func=self._lib.X509_CRL_add_ext,
+ gc=True
+ )
+
+ # add revoked certificates
+ for revoked_cert in builder._revoked_certificates:
+ # Duplicating because the X509_CRL takes ownership and will free
+ # this memory when X509_CRL_free is called.
+ revoked = self._lib.Cryptography_X509_REVOKED_dup(
+ revoked_cert._x509_revoked
+ )
+ self.openssl_assert(revoked != self._ffi.NULL)
+ res = self._lib.X509_CRL_add0_revoked(x509_crl, revoked)
+ self.openssl_assert(res == 1)
+
+ res = self._lib.X509_CRL_sign(
+ x509_crl, private_key._evp_pkey, evp_md
+ )
+ if res == 0:
+ errors = self._consume_errors()
+ self.openssl_assert(errors[0][1] == self._lib.ERR_LIB_RSA)
+ self.openssl_assert(
+ errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ )
+ raise ValueError("Digest too big for RSA key")
+
+ return _CertificateRevocationList(self, x509_crl)
+
+ def _create_x509_extensions(self, extensions, handlers, x509_obj,
+ add_func, gc):
+ for i, extension in enumerate(extensions):
+ try:
+ encode = handlers[extension.oid]
+ except KeyError:
+ raise NotImplementedError(
+ 'Extension not supported: {0}'.format(extension.oid)
+ )
+
+ pp, r = encode(self, extension.value)
+ obj = _txt2obj_gc(self, extension.oid.dotted_string)
+ x509_extension = self._lib.X509_EXTENSION_create_by_OBJ(
+ self._ffi.NULL,
+ obj,
+ 1 if extension.critical else 0,
+ _encode_asn1_str_gc(self, pp[0], r)
+ )
+ self.openssl_assert(x509_extension != self._ffi.NULL)
+ if gc:
+ x509_extension = self._ffi.gc(
+ x509_extension, self._lib.X509_EXTENSION_free
+ )
+ res = add_func(x509_obj, x509_extension, i)
+ self.openssl_assert(res >= 1)
+
+ def create_x509_revoked_certificate(self, builder):
+ if not isinstance(builder, x509.RevokedCertificateBuilder):
+ raise TypeError('Builder type mismatch.')
+
+ x509_revoked = self._lib.X509_REVOKED_new()
+ self.openssl_assert(x509_revoked != self._ffi.NULL)
+ x509_revoked = self._ffi.gc(x509_revoked, self._lib.X509_REVOKED_free)
+ serial_number = _encode_asn1_int_gc(self, builder._serial_number)
+ res = self._lib.X509_REVOKED_set_serialNumber(
+ x509_revoked, serial_number
+ )
+ self.openssl_assert(res == 1)
+ res = self._lib.ASN1_TIME_set(
+ x509_revoked.revocationDate,
+ calendar.timegm(builder._revocation_date.timetuple())
+ )
+ self.openssl_assert(res != self._ffi.NULL)
+ # add CRL entry extensions
+ self._create_x509_extensions(
+ extensions=builder._extensions,
+ handlers=_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS,
+ x509_obj=x509_revoked,
+ add_func=self._lib.X509_REVOKED_add_ext,
+ gc=True
+ )
+ return _RevokedCertificate(self, None, x509_revoked)
+
def load_pem_private_key(self, data, password):
return self._load_key(
self._lib.PEM_read_bio_PrivateKey,
@@ -2136,6 +2269,9 @@ class Backend(object):
generalized_time = self._ffi.gc(
generalized_time, self._lib.ASN1_GENERALIZEDTIME_free
)
+ return self._parse_asn1_generalized_time(generalized_time)
+
+ def _parse_asn1_generalized_time(self, generalized_time):
time = self._asn1_string_to_ascii(
self._ffi.cast("ASN1_STRING *", generalized_time)
)
diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py
index 664f6d35..033cd3b1 100644
--- a/src/cryptography/hazmat/backends/openssl/rsa.py
+++ b/src/cryptography/hazmat/backends/openssl/rsa.py
@@ -138,6 +138,7 @@ def _handle_rsa_enc_dec_error(backend, key):
decoding_errors = [
backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_01,
backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_02,
+ backend._lib.RSA_R_OAEP_DECODING_ERROR,
]
if backend._lib.Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR:
decoding_errors.append(backend._lib.RSA_R_PKCS_DECODING_ERROR)
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 7e89ac67..b8614e0b 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -4,8 +4,8 @@
from __future__ import absolute_import, division, print_function
-import datetime
import ipaddress
+import operator
from email.utils import parseaddr
@@ -19,7 +19,7 @@ from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.x509.oid import (
- CRLExtensionOID, CertificatePoliciesOID, ExtensionOID
+ CRLEntryExtensionOID, CertificatePoliciesOID, ExtensionOID
)
@@ -213,6 +213,15 @@ class _X509ExtensionParser(object):
"Critical extension {0} is not currently supported"
.format(oid), oid
)
+ else:
+ # Dump the DER payload into an UnrecognizedExtension object
+ data = backend._lib.X509_EXTENSION_get_data(ext)
+ backend.openssl_assert(data != backend._ffi.NULL)
+ der = backend._ffi.buffer(data.data, data.length)[:]
+ unrecognized = x509.UnrecognizedExtension(oid, der)
+ extensions.append(
+ x509.Extension(oid, critical, unrecognized)
+ )
else:
# For extensions which are not supported by OpenSSL we pass the
# extension object directly to the parsing routine so it can
@@ -679,7 +688,19 @@ def _decode_inhibit_any_policy(backend, asn1_int):
return x509.InhibitAnyPolicy(skip_certs)
-_CRL_REASON_CODE_TO_ENUM = {
+# CRLReason ::= ENUMERATED {
+# unspecified (0),
+# keyCompromise (1),
+# cACompromise (2),
+# affiliationChanged (3),
+# superseded (4),
+# cessationOfOperation (5),
+# certificateHold (6),
+# -- value 7 is not used
+# removeFromCRL (8),
+# privilegeWithdrawn (9),
+# aACompromise (10) }
+_CRL_ENTRY_REASON_CODE_TO_ENUM = {
0: x509.ReasonFlags.unspecified,
1: x509.ReasonFlags.key_compromise,
2: x509.ReasonFlags.ca_compromise,
@@ -693,13 +714,27 @@ _CRL_REASON_CODE_TO_ENUM = {
}
+_CRL_ENTRY_REASON_ENUM_TO_CODE = {
+ x509.ReasonFlags.unspecified: 0,
+ x509.ReasonFlags.key_compromise: 1,
+ x509.ReasonFlags.ca_compromise: 2,
+ x509.ReasonFlags.affiliation_changed: 3,
+ x509.ReasonFlags.superseded: 4,
+ x509.ReasonFlags.cessation_of_operation: 5,
+ x509.ReasonFlags.certificate_hold: 6,
+ x509.ReasonFlags.remove_from_crl: 8,
+ x509.ReasonFlags.privilege_withdrawn: 9,
+ x509.ReasonFlags.aa_compromise: 10
+}
+
+
def _decode_crl_reason(backend, enum):
enum = backend._ffi.cast("ASN1_ENUMERATED *", enum)
enum = backend._ffi.gc(enum, backend._lib.ASN1_ENUMERATED_free)
code = backend._lib.ASN1_ENUMERATED_get(enum)
try:
- return _CRL_REASON_CODE_TO_ENUM[code]
+ return x509.CRLReason(_CRL_ENTRY_REASON_CODE_TO_ENUM[code])
except KeyError:
raise ValueError("Unsupported reason code: {0}".format(code))
@@ -711,12 +746,9 @@ def _decode_invalidity_date(backend, inv_date):
generalized_time = backend._ffi.gc(
generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free
)
- time = backend._ffi.string(
- backend._lib.ASN1_STRING_data(
- backend._ffi.cast("ASN1_STRING *", generalized_time)
- )
- ).decode("ascii")
- return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ")
+ return x509.InvalidityDate(
+ backend._parse_asn1_generalized_time(generalized_time)
+ )
def _decode_cert_issuer(backend, ext):
@@ -739,16 +771,24 @@ def _decode_cert_issuer(backend, ext):
backend._consume_errors()
raise ValueError(
"The {0} extension is corrupted and can't be parsed".format(
- CRLExtensionOID.CERTIFICATE_ISSUER))
+ CRLEntryExtensionOID.CERTIFICATE_ISSUER))
gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free)
- return x509.GeneralNames(_decode_general_names(backend, gns))
+ return x509.CertificateIssuer(_decode_general_names(backend, gns))
@utils.register_interface(x509.RevokedCertificate)
class _RevokedCertificate(object):
- def __init__(self, backend, x509_revoked):
+ def __init__(self, backend, crl, x509_revoked):
self._backend = backend
+ # The X509_REVOKED_value is a X509_REVOKED * that has
+ # no reference counting. This means when X509_CRL_free is
+ # called then the CRL and all X509_REVOKED * are freed. Since
+ # you can retain a reference to a single revoked certificate
+ # and let the CRL fall out of scope we need to retain a
+ # private reference to the CRL inside the RevokedCertificate
+ # object to prevent the gc from being called inappropriately.
+ self._crl = crl
self._x509_revoked = x509_revoked
@property
@@ -853,26 +893,34 @@ class _CertificateRevocationList(object):
self._backend.openssl_assert(res == 1)
return self._backend._read_mem_bio(bio)
- def _revoked_certificates(self):
+ def _revoked_cert(self, idx):
revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl)
- revoked_list = []
- if revoked != self._backend._ffi.NULL:
- num = self._backend._lib.sk_X509_REVOKED_num(revoked)
- for i in range(num):
- r = self._backend._lib.sk_X509_REVOKED_value(revoked, i)
- self._backend.openssl_assert(r != self._backend._ffi.NULL)
- revoked_list.append(_RevokedCertificate(self._backend, r))
-
- return revoked_list
+ r = self._backend._lib.sk_X509_REVOKED_value(revoked, idx)
+ self._backend.openssl_assert(r != self._backend._ffi.NULL)
+ return _RevokedCertificate(self._backend, self, r)
def __iter__(self):
- return iter(self._revoked_certificates())
+ for i in range(len(self)):
+ yield self._revoked_cert(i)
def __getitem__(self, idx):
- return self._revoked_certificates()[idx]
+ if isinstance(idx, slice):
+ start, stop, step = idx.indices(len(self))
+ return [self._revoked_cert(i) for i in range(start, stop, step)]
+ else:
+ idx = operator.index(idx)
+ if idx < 0:
+ idx += len(self)
+ if not 0 <= idx < len(self):
+ raise IndexError
+ return self._revoked_cert(idx)
def __len__(self):
- return len(self._revoked_certificates())
+ revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl)
+ if revoked == self._backend._ffi.NULL:
+ return 0
+ else:
+ return self._backend._lib.sk_X509_REVOKED_num(revoked)
@property
def extensions(self):
@@ -975,13 +1023,13 @@ _EXTENSION_HANDLERS = {
}
_REVOKED_EXTENSION_HANDLERS = {
- CRLExtensionOID.CRL_REASON: _decode_crl_reason,
- CRLExtensionOID.INVALIDITY_DATE: _decode_invalidity_date,
- CRLExtensionOID.CERTIFICATE_ISSUER: _decode_cert_issuer,
+ CRLEntryExtensionOID.CRL_REASON: _decode_crl_reason,
+ CRLEntryExtensionOID.INVALIDITY_DATE: _decode_invalidity_date,
+ CRLEntryExtensionOID.CERTIFICATE_ISSUER: _decode_cert_issuer,
}
_REVOKED_UNSUPPORTED_EXTENSIONS = set([
- CRLExtensionOID.CERTIFICATE_ISSUER,
+ CRLEntryExtensionOID.CERTIFICATE_ISSUER,
])
_CRL_EXTENSION_HANDLERS = {
diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py
index 07b6b9ac..8e419439 100644
--- a/src/cryptography/hazmat/bindings/openssl/binding.py
+++ b/src/cryptography/hazmat/bindings/openssl/binding.py
@@ -8,6 +8,7 @@ import collections
import os
import threading
import types
+import warnings
from cryptography.exceptions import InternalError
from cryptography.hazmat.bindings._openssl import ffi, lib
@@ -180,3 +181,11 @@ class Binding(object):
# condition registering the OpenSSL locks. On Python 3.4+ the import lock
# is per module so this approach will not work.
Binding.init_static_locks()
+
+if Binding.lib.SSLeay() < 0x10001000:
+ warnings.warn(
+ "OpenSSL versions less than 1.0.1 are no longer supported by the "
+ "OpenSSL project, please upgrade. A future version of cryptography "
+ "will drop support for these versions.",
+ DeprecationWarning
+ )
diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py
index 4449e85a..b85d50d3 100644
--- a/src/cryptography/utils.py
+++ b/src/cryptography/utils.py
@@ -12,7 +12,10 @@ import sys
import warnings
+# the functions deprecated in 1.0 are on an arbitrarily extended deprecation
+# cycle and should not be removed until we agree on when that cycle ends.
DeprecatedIn10 = DeprecationWarning
+DeprecatedIn12 = PendingDeprecationWarning
def read_only_property(name):
diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py
index c4434fd1..a1deb7f4 100644
--- a/src/cryptography/x509/__init__.py
+++ b/src/cryptography/x509/__init__.py
@@ -6,20 +6,22 @@ from __future__ import absolute_import, division, print_function
from cryptography.x509.base import (
Certificate, CertificateBuilder, CertificateRevocationList,
+ CertificateRevocationListBuilder,
CertificateSigningRequest, CertificateSigningRequestBuilder,
- InvalidVersion, RevokedCertificate,
+ InvalidVersion, RevokedCertificate, RevokedCertificateBuilder,
Version, load_der_x509_certificate, load_der_x509_crl, load_der_x509_csr,
load_pem_x509_certificate, load_pem_x509_crl, load_pem_x509_csr,
)
from cryptography.x509.extensions import (
AccessDescription, AuthorityInformationAccess,
AuthorityKeyIdentifier, BasicConstraints, CRLDistributionPoints,
- CRLNumber, CertificatePolicies, DistributionPoint, DuplicateExtension,
- ExtendedKeyUsage, Extension, ExtensionNotFound, ExtensionType, Extensions,
- GeneralNames, InhibitAnyPolicy, IssuerAlternativeName, KeyUsage,
+ CRLNumber, CRLReason, CertificateIssuer, CertificatePolicies,
+ DistributionPoint, DuplicateExtension, ExtendedKeyUsage, Extension,
+ ExtensionNotFound, ExtensionType, Extensions, GeneralNames,
+ InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, KeyUsage,
NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation,
ReasonFlags, SubjectAlternativeName, SubjectKeyIdentifier,
- UnsupportedExtension, UserNotice
+ UnrecognizedExtension, UnsupportedExtension, UserNotice
)
from cryptography.x509.general_name import (
DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name,
@@ -28,9 +30,9 @@ from cryptography.x509.general_name import (
)
from cryptography.x509.name import Name, NameAttribute
from cryptography.x509.oid import (
- AuthorityInformationAccessOID, CRLExtensionOID, CertificatePoliciesOID,
- ExtendedKeyUsageOID, ExtensionOID, NameOID, ObjectIdentifier,
- SignatureAlgorithmOID, _SIG_OIDS_TO_HASH
+ AuthorityInformationAccessOID, CRLEntryExtensionOID, CRLExtensionOID,
+ CertificatePoliciesOID, ExtendedKeyUsageOID, ExtensionOID, NameOID,
+ ObjectIdentifier, SignatureAlgorithmOID, _SIG_OIDS_TO_HASH
)
@@ -95,9 +97,9 @@ OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY
OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER
OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE
-OID_CERTIFICATE_ISSUER = CRLExtensionOID.CERTIFICATE_ISSUER
-OID_CRL_REASON = CRLExtensionOID.CRL_REASON
-OID_INVALIDITY_DATE = CRLExtensionOID.INVALIDITY_DATE
+OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER
+OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON
+OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE
OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS
OID_OCSP = AuthorityInformationAccessOID.OCSP
@@ -152,8 +154,10 @@ __all__ = [
"OtherName",
"Certificate",
"CertificateRevocationList",
+ "CertificateRevocationListBuilder",
"CertificateSigningRequest",
"RevokedCertificate",
+ "RevokedCertificateBuilder",
"CertificateSigningRequestBuilder",
"CertificateBuilder",
"Version",
@@ -161,4 +165,9 @@ __all__ = [
"OID_CA_ISSUERS",
"OID_OCSP",
"_GENERAL_NAMES",
+ "CRLExtensionOID",
+ "CertificateIssuer",
+ "CRLReason",
+ "InvalidityDate",
+ "UnrecognizedExtension",
]
diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py
index 057d0e9b..55e965f7 100644
--- a/src/cryptography/x509/base.py
+++ b/src/cryptography/x509/base.py
@@ -518,3 +518,159 @@ class CertificateBuilder(object):
raise ValueError("A certificate must have a public key")
return backend.create_x509_certificate(self, private_key, algorithm)
+
+
+class CertificateRevocationListBuilder(object):
+ def __init__(self, issuer_name=None, last_update=None, next_update=None,
+ extensions=[], revoked_certificates=[]):
+ self._issuer_name = issuer_name
+ self._last_update = last_update
+ self._next_update = next_update
+ self._extensions = extensions
+ self._revoked_certificates = revoked_certificates
+
+ def issuer_name(self, issuer_name):
+ if not isinstance(issuer_name, Name):
+ raise TypeError('Expecting x509.Name object.')
+ if self._issuer_name is not None:
+ raise ValueError('The issuer name may only be set once.')
+ return CertificateRevocationListBuilder(
+ issuer_name, self._last_update, self._next_update,
+ self._extensions, self._revoked_certificates
+ )
+
+ def last_update(self, last_update):
+ if not isinstance(last_update, datetime.datetime):
+ raise TypeError('Expecting datetime object.')
+ if self._last_update is not None:
+ raise ValueError('Last update may only be set once.')
+ if last_update <= _UNIX_EPOCH:
+ raise ValueError('The last update date must be after the unix'
+ ' epoch (1970 January 1).')
+ if self._next_update is not None and last_update > self._next_update:
+ raise ValueError(
+ 'The last update date must be before the next update date.'
+ )
+ return CertificateRevocationListBuilder(
+ self._issuer_name, last_update, self._next_update,
+ self._extensions, self._revoked_certificates
+ )
+
+ def next_update(self, next_update):
+ if not isinstance(next_update, datetime.datetime):
+ raise TypeError('Expecting datetime object.')
+ if self._next_update is not None:
+ raise ValueError('Last update may only be set once.')
+ if next_update <= _UNIX_EPOCH:
+ raise ValueError('The last update date must be after the unix'
+ ' epoch (1970 January 1).')
+ if self._last_update is not None and next_update < self._last_update:
+ raise ValueError(
+ 'The next update date must be after the last update date.'
+ )
+ return CertificateRevocationListBuilder(
+ self._issuer_name, self._last_update, next_update,
+ self._extensions, self._revoked_certificates
+ )
+
+ def add_extension(self, extension, critical):
+ """
+ Adds an X.509 extension to the certificate revocation list.
+ """
+ if not isinstance(extension, ExtensionType):
+ raise TypeError("extension must be an ExtensionType")
+
+ extension = Extension(extension.oid, critical, extension)
+
+ # TODO: This is quadratic in the number of extensions
+ for e in self._extensions:
+ if e.oid == extension.oid:
+ raise ValueError('This extension has already been set.')
+ return CertificateRevocationListBuilder(
+ self._issuer_name, self._last_update, self._next_update,
+ self._extensions + [extension], self._revoked_certificates
+ )
+
+ def add_revoked_certificate(self, revoked_certificate):
+ """
+ Adds a revoked certificate to the CRL.
+ """
+ if not isinstance(revoked_certificate, RevokedCertificate):
+ raise TypeError("Must be an instance of RevokedCertificate")
+
+ return CertificateRevocationListBuilder(
+ self._issuer_name, self._last_update,
+ self._next_update, self._extensions,
+ self._revoked_certificates + [revoked_certificate]
+ )
+
+ def sign(self, private_key, algorithm, backend):
+ if self._issuer_name is None:
+ raise ValueError("A CRL must have an issuer name")
+
+ if self._last_update is None:
+ raise ValueError("A CRL must have a last update time")
+
+ if self._next_update is None:
+ raise ValueError("A CRL must have a next update time")
+
+ return backend.create_x509_crl(self, private_key, algorithm)
+
+
+class RevokedCertificateBuilder(object):
+ def __init__(self, serial_number=None, revocation_date=None,
+ extensions=[]):
+ self._serial_number = serial_number
+ self._revocation_date = revocation_date
+ self._extensions = extensions
+
+ def serial_number(self, number):
+ if not isinstance(number, six.integer_types):
+ raise TypeError('Serial number must be of integral type.')
+ if self._serial_number is not None:
+ raise ValueError('The serial number may only be set once.')
+ if number < 0:
+ raise ValueError('The serial number should be non-negative.')
+ if utils.bit_length(number) > 160: # As defined in RFC 5280
+ raise ValueError('The serial number should not be more than 160 '
+ 'bits.')
+ return RevokedCertificateBuilder(
+ number, self._revocation_date, self._extensions
+ )
+
+ def revocation_date(self, time):
+ if not isinstance(time, datetime.datetime):
+ raise TypeError('Expecting datetime object.')
+ if self._revocation_date is not None:
+ raise ValueError('The revocation date may only be set once.')
+ if time <= _UNIX_EPOCH:
+ raise ValueError('The revocation date must be after the unix'
+ ' epoch (1970 January 1).')
+ return RevokedCertificateBuilder(
+ self._serial_number, time, self._extensions
+ )
+
+ def add_extension(self, extension, critical):
+ if not isinstance(extension, ExtensionType):
+ raise TypeError("extension must be an ExtensionType")
+
+ extension = Extension(extension.oid, critical, extension)
+
+ # TODO: This is quadratic in the number of extensions
+ for e in self._extensions:
+ if e.oid == extension.oid:
+ raise ValueError('This extension has already been set.')
+ return RevokedCertificateBuilder(
+ self._serial_number, self._revocation_date,
+ self._extensions + [extension]
+ )
+
+ def build(self, backend):
+ if self._serial_number is None:
+ raise ValueError("A revoked certificate must have a serial number")
+ if self._revocation_date is None:
+ raise ValueError(
+ "A revoked certificate must have a revocation date"
+ )
+
+ return backend.create_x509_revoked_certificate(self)
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index 15feb717..f7b5d7f5 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -5,6 +5,7 @@
from __future__ import absolute_import, division, print_function
import abc
+import datetime
import hashlib
import ipaddress
from enum import Enum
@@ -18,7 +19,9 @@ from cryptography import utils
from cryptography.hazmat.primitives import constant_time, serialization
from cryptography.x509.general_name import GeneralName, IPAddress, OtherName
from cryptography.x509.name import Name
-from cryptography.x509.oid import ExtensionOID, ObjectIdentifier
+from cryptography.x509.oid import (
+ CRLEntryExtensionOID, ExtensionOID, ObjectIdentifier
+)
class _SubjectPublicKeyInfo(univ.Sequence):
@@ -88,6 +91,13 @@ class Extensions(object):
raise ExtensionNotFound("No {0} extension was found".format(oid), oid)
def get_extension_for_class(self, extclass):
+ if extclass is UnrecognizedExtension:
+ raise TypeError(
+ "UnrecognizedExtension can't be used with "
+ "get_extension_for_class because more than one instance of the"
+ " class may be present."
+ )
+
for ext in self:
if isinstance(ext.value, extclass):
return ext
@@ -102,6 +112,9 @@ class Extensions(object):
def __len__(self):
return len(self._extensions)
+ def __getitem__(self, idx):
+ return self._extensions[idx]
+
def __repr__(self):
return (
"<Extensions({0})>".format(self._extensions)
@@ -127,6 +140,9 @@ class CRLNumber(object):
def __ne__(self, other):
return not self == other
+ def __hash__(self):
+ return hash(self.crl_number)
+
def __repr__(self):
return "<CRLNumber({0})>".format(self.crl_number)
@@ -226,6 +242,9 @@ class SubjectKeyIdentifier(object):
def __ne__(self, other):
return not self == other
+ def __hash__(self):
+ return hash(self.digest)
+
@utils.register_interface(ExtensionType)
class AuthorityInformationAccess(object):
@@ -258,6 +277,9 @@ class AuthorityInformationAccess(object):
def __ne__(self, other):
return not self == other
+ def __getitem__(self, idx):
+ return self._descriptions[idx]
+
class AccessDescription(object):
def __init__(self, access_method, access_location):
@@ -330,6 +352,9 @@ class BasicConstraints(object):
def __ne__(self, other):
return not self == other
+ def __hash__(self):
+ return hash((self.ca, self.path_length))
+
@utils.register_interface(ExtensionType)
class CRLDistributionPoints(object):
@@ -364,6 +389,9 @@ class CRLDistributionPoints(object):
def __ne__(self, other):
return not self == other
+ def __getitem__(self, idx):
+ return self._distribution_points[idx]
+
class DistributionPoint(object):
def __init__(self, full_name, relative_name, reasons, crl_issuer):
@@ -486,6 +514,9 @@ class CertificatePolicies(object):
def __ne__(self, other):
return not self == other
+ def __getitem__(self, idx):
+ return self._policies[idx]
+
class PolicyInformation(object):
def __init__(self, policy_identifier, policy_qualifiers):
@@ -885,6 +916,9 @@ class GeneralNames(object):
def __ne__(self, other):
return not self == other
+ def __getitem__(self, idx):
+ return self._general_names[idx]
+
@utils.register_interface(ExtensionType)
class SubjectAlternativeName(object):
@@ -911,6 +945,9 @@ class SubjectAlternativeName(object):
return self._general_names == other._general_names
+ def __getitem__(self, idx):
+ return self._general_names[idx]
+
def __ne__(self, other):
return not self == other
@@ -942,3 +979,127 @@ class IssuerAlternativeName(object):
def __ne__(self, other):
return not self == other
+
+ def __getitem__(self, idx):
+ return self._general_names[idx]
+
+
+@utils.register_interface(ExtensionType)
+class CertificateIssuer(object):
+ oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER
+
+ def __init__(self, general_names):
+ self._general_names = GeneralNames(general_names)
+
+ def __iter__(self):
+ return iter(self._general_names)
+
+ def __len__(self):
+ return len(self._general_names)
+
+ def get_values_for_type(self, type):
+ return self._general_names.get_values_for_type(type)
+
+ def __repr__(self):
+ return "<CertificateIssuer({0})>".format(self._general_names)
+
+ def __eq__(self, other):
+ if not isinstance(other, CertificateIssuer):
+ return NotImplemented
+
+ return self._general_names == other._general_names
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __getitem__(self, idx):
+ return self._general_names[idx]
+
+
+@utils.register_interface(ExtensionType)
+class CRLReason(object):
+ oid = CRLEntryExtensionOID.CRL_REASON
+
+ def __init__(self, reason):
+ if not isinstance(reason, ReasonFlags):
+ raise TypeError("reason must be an element from ReasonFlags")
+
+ self._reason = reason
+
+ def __repr__(self):
+ return "<CRLReason(reason={0})>".format(self._reason)
+
+ def __eq__(self, other):
+ if not isinstance(other, CRLReason):
+ return NotImplemented
+
+ return self.reason == other.reason
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash(self.reason)
+
+ reason = utils.read_only_property("_reason")
+
+
+@utils.register_interface(ExtensionType)
+class InvalidityDate(object):
+ oid = CRLEntryExtensionOID.INVALIDITY_DATE
+
+ def __init__(self, invalidity_date):
+ if not isinstance(invalidity_date, datetime.datetime):
+ raise TypeError("invalidity_date must be a datetime.datetime")
+
+ self._invalidity_date = invalidity_date
+
+ def __repr__(self):
+ return "<InvalidityDate(invalidity_date={0})>".format(
+ self._invalidity_date
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, InvalidityDate):
+ return NotImplemented
+
+ return self.invalidity_date == other.invalidity_date
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash(self.invalidity_date)
+
+ invalidity_date = utils.read_only_property("_invalidity_date")
+
+
+@utils.register_interface(ExtensionType)
+class UnrecognizedExtension(object):
+ def __init__(self, oid, value):
+ if not isinstance(oid, ObjectIdentifier):
+ raise TypeError("oid must be an ObjectIdentifier")
+ self._oid = oid
+ self._value = value
+
+ oid = utils.read_only_property("_oid")
+ value = utils.read_only_property("_value")
+
+ def __repr__(self):
+ return (
+ "<UnrecognizedExtension(oid={0.oid}, value={0.value!r})>".format(
+ self
+ )
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, UnrecognizedExtension):
+ return NotImplemented
+
+ return self.oid == other.oid and self.value == other.value
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash((self.oid, self.value))
diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py
index 94295d66..ced15103 100644
--- a/src/cryptography/x509/oid.py
+++ b/src/cryptography/x509/oid.py
@@ -88,12 +88,20 @@ class ExtensionOID(object):
CRL_NUMBER = ObjectIdentifier("2.5.29.20")
-class CRLExtensionOID(object):
+class CRLEntryExtensionOID(object):
CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29")
CRL_REASON = ObjectIdentifier("2.5.29.21")
INVALIDITY_DATE = ObjectIdentifier("2.5.29.24")
+CRLExtensionOID = utils.deprecated(
+ CRLEntryExtensionOID,
+ __name__,
+ "CRLExtensionOID has been renamed to CRLEntryExtensionOID",
+ utils.DeprecatedIn12
+)
+
+
class NameOID(object):
COMMON_NAME = ObjectIdentifier("2.5.4.3")
COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
@@ -220,9 +228,9 @@ _OID_NAMES = {
ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName",
ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName",
ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints",
- CRLExtensionOID.CRL_REASON: "cRLReason",
- CRLExtensionOID.INVALIDITY_DATE: "invalidityDate",
- CRLExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer",
+ CRLEntryExtensionOID.CRL_REASON: "cRLReason",
+ CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate",
+ CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer",
ExtensionOID.NAME_CONSTRAINTS: "nameConstraints",
ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints",
ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies",