aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py106
-rw-r--r--src/cryptography/hazmat/bindings/openssl/ssl.py6
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509.py5
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509v3.py17
-rw-r--r--src/cryptography/hazmat/primitives/kdf/hkdf.py4
-rw-r--r--src/cryptography/x509.py96
6 files changed, 225 insertions, 9 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 7f633c76..42ca138d 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -15,6 +15,7 @@ from __future__ import absolute_import, division, print_function
import datetime
import ipaddress
+from email.utils import parseaddr
import idna
@@ -107,6 +108,27 @@ def _build_general_name(backend, gn):
return x509.DirectoryName(
_build_x509_name(backend, gn.d.directoryName)
)
+ elif gn.type == backend._lib.GEN_EMAIL:
+ data = backend._ffi.buffer(
+ gn.d.rfc822Name.data, gn.d.rfc822Name.length
+ )[:].decode("ascii")
+ name, address = parseaddr(data)
+ parts = address.split(u"@")
+ if name or len(parts) > 2 or not address:
+ # parseaddr has found a name (e.g. Name <email>) or the split
+ # has found more than 2 parts (which means more than one @ sign)
+ # or the entire value is an empty string.
+ raise ValueError("Invalid rfc822name value")
+ elif len(parts) == 1:
+ # Single label email name. This is valid for local delivery. No
+ # IDNA decoding can be done since there is no domain component.
+ return x509.RFC822Name(address)
+ else:
+ # A normal email of the form user@domain.com. Let's attempt to
+ # decode the domain component and return the entire address.
+ return x509.RFC822Name(
+ parts[0] + u"@" + idna.decode(parts[1])
+ )
else:
# otherName, x400Address or ediPartyName
raise x509.UnsupportedGeneralNameType(
@@ -245,6 +267,12 @@ class _Certificate(object):
value = self._build_key_usage(ext)
elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME:
value = self._build_subject_alt_name(ext)
+ elif oid == x509.OID_EXTENDED_KEY_USAGE:
+ value = self._build_extended_key_usage(ext)
+ elif oid == x509.OID_AUTHORITY_KEY_IDENTIFIER:
+ value = self._build_authority_key_identifier(ext)
+ elif oid == x509.OID_AUTHORITY_INFORMATION_ACCESS:
+ value = self._build_authority_information_access(ext)
elif critical:
raise x509.UnsupportedExtension(
"{0} is not currently supported".format(oid), oid
@@ -297,6 +325,66 @@ class _Certificate(object):
self._backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
)
+ def _build_authority_key_identifier(self, ext):
+ akid = self._backend._lib.X509V3_EXT_d2i(ext)
+ assert akid != self._backend._ffi.NULL
+ akid = self._backend._ffi.cast("AUTHORITY_KEYID *", akid)
+ akid = self._backend._ffi.gc(
+ akid, self._backend._lib.AUTHORITY_KEYID_free
+ )
+ key_identifier = None
+ authority_cert_issuer = None
+ authority_cert_serial_number = None
+
+ if akid.keyid != self._backend._ffi.NULL:
+ key_identifier = self._backend._ffi.buffer(
+ akid.keyid.data, akid.keyid.length
+ )[:]
+
+ if akid.issuer != self._backend._ffi.NULL:
+ authority_cert_issuer = []
+
+ num = self._backend._lib.sk_GENERAL_NAME_num(akid.issuer)
+ for i in range(num):
+ gn = self._backend._lib.sk_GENERAL_NAME_value(akid.issuer, i)
+ assert gn != self._backend._ffi.NULL
+ value = _build_general_name(self._backend, gn)
+
+ authority_cert_issuer.append(value)
+
+ if akid.serial != self._backend._ffi.NULL:
+ bn = self._backend._lib.ASN1_INTEGER_to_BN(
+ akid.serial, self._backend._ffi.NULL
+ )
+ assert bn != self._backend._ffi.NULL
+ bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free)
+ authority_cert_serial_number = self._backend._bn_to_int(bn)
+
+ return x509.AuthorityKeyIdentifier(
+ key_identifier, authority_cert_issuer, authority_cert_serial_number
+ )
+
+ def _build_authority_information_access(self, ext):
+ aia = self._backend._lib.X509V3_EXT_d2i(ext)
+ assert aia != self._backend._ffi.NULL
+ aia = self._backend._ffi.cast(
+ "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia
+ )
+ aia = self._backend._ffi.gc(
+ aia, self._backend._lib.sk_ACCESS_DESCRIPTION_free
+ )
+ num = self._backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
+ access_descriptions = []
+ for i in range(num):
+ ad = self._backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i)
+ assert ad.method != self._backend._ffi.NULL
+ oid = x509.ObjectIdentifier(_obj2txt(self._backend, ad.method))
+ assert ad.location != self._backend._ffi.NULL
+ gn = _build_general_name(self._backend, ad.location)
+ access_descriptions.append(x509.AccessDescription(oid, gn))
+
+ return x509.AuthorityInformationAccess(access_descriptions)
+
def _build_key_usage(self, ext):
bit_string = self._backend._lib.X509V3_EXT_d2i(ext)
assert bit_string != self._backend._ffi.NULL
@@ -344,6 +432,24 @@ class _Certificate(object):
return x509.SubjectAlternativeName(general_names)
+ def _build_extended_key_usage(self, ext):
+ sk = self._backend._ffi.cast(
+ "Cryptography_STACK_OF_ASN1_OBJECT *",
+ self._backend._lib.X509V3_EXT_d2i(ext)
+ )
+ assert sk != self._backend._ffi.NULL
+ sk = self._backend._ffi.gc(sk, self._backend._lib.sk_ASN1_OBJECT_free)
+ num = self._backend._lib.sk_ASN1_OBJECT_num(sk)
+ ekus = []
+
+ for i in range(num):
+ obj = self._backend._lib.sk_ASN1_OBJECT_value(sk, i)
+ assert obj != self._backend._ffi.NULL
+ oid = x509.ObjectIdentifier(_obj2txt(self._backend, obj))
+ ekus.append(oid)
+
+ return x509.ExtendedKeyUsage(ekus)
+
@utils.register_interface(x509.CertificateSigningRequest)
class _CertificateSigningRequest(object):
diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py
index b182180f..6493e867 100644
--- a/src/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/src/cryptography/hazmat/bindings/openssl/ssl.py
@@ -211,6 +211,9 @@ int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *);
int SSL_CTX_use_PrivateKey(SSL_CTX *, EVP_PKEY *);
int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);
int SSL_CTX_check_private_key(const SSL_CTX *);
+void SSL_CTX_set_cert_verify_callback(SSL_CTX *,
+ int (*)(X509_STORE_CTX *,void *),
+ void *);
void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *);
X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *);
@@ -322,6 +325,7 @@ void SSL_CTX_set_tlsext_servername_callback(
is fraught with peril thanks to OS distributions we check some constants
to determine if they are supported or not */
long SSL_set_tlsext_status_ocsp_resp(SSL *, unsigned char *, int);
+long SSL_get_tlsext_status_ocsp_resp(SSL *, const unsigned char **);
long SSL_CTX_set_tlsext_status_cb(SSL_CTX *, int(*)(SSL *, void *));
long SSL_session_reused(SSL *);
@@ -431,6 +435,7 @@ static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 1;
#else
static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 0;
long (*SSL_set_tlsext_status_ocsp_resp)(SSL *, unsigned char *, int) = NULL;
+long (*SSL_get_tlsext_status_ocsp_resp)(SSL *, const unsigned char **) = NULL;
#endif
#ifdef SSL_MODE_RELEASE_BUFFERS
@@ -617,6 +622,7 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_STATUS_REQ_OCSP_RESP": [
"SSL_set_tlsext_status_ocsp_resp",
+ "SSL_get_tlsext_status_ocsp_resp",
],
"Cryptography_HAS_RELEASE_BUFFERS": [
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
index a1fb7ffb..fa6a16b3 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -303,6 +303,11 @@ EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **);
int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *);
EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **);
int i2d_ECPrivateKey_bio(BIO *, EC_KEY *);
+
+// declared in safestack
+int sk_ASN1_OBJECT_num(Cryptography_STACK_OF_ASN1_OBJECT *);
+ASN1_OBJECT *sk_ASN1_OBJECT_value(Cryptography_STACK_OF_ASN1_OBJECT *, int);
+void sk_ASN1_OBJECT_free(Cryptography_STACK_OF_ASN1_OBJECT *);
"""
CUSTOMIZATIONS = """
diff --git a/src/cryptography/hazmat/bindings/openssl/x509v3.py b/src/cryptography/hazmat/bindings/openssl/x509v3.py
index 28dd7f32..c2b6860f 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509v3.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509v3.py
@@ -19,9 +19,12 @@ typedef LHASH_OF(CONF_VALUE) Cryptography_LHASH_OF_CONF_VALUE;
#else
typedef LHASH Cryptography_LHASH_OF_CONF_VALUE;
#endif
+typedef STACK_OF(ACCESS_DESCRIPTION) Cryptography_STACK_OF_ACCESS_DESCRIPTION;
"""
TYPES = """
+typedef ... Cryptography_STACK_OF_ACCESS_DESCRIPTION;
+
typedef struct {
X509 *issuer_cert;
X509 *subject_cert;
@@ -92,6 +95,11 @@ typedef struct {
ASN1_INTEGER *serial;
} AUTHORITY_KEYID;
+typedef struct {
+ ASN1_OBJECT *method;
+ GENERAL_NAME *location;
+} ACCESS_DESCRIPTION;
+
typedef ... Cryptography_LHASH_OF_CONF_VALUE;
"""
@@ -109,11 +117,20 @@ MACROS = """
/* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the
x509v3.h header. */
void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *);
+/* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the
+ x509v3.h header. */
+void AUTHORITY_KEYID_free(AUTHORITY_KEYID *);
void *X509V3_set_ctx_nodb(X509V3_CTX *);
int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *);
int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *);
GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int);
+int sk_ACCESS_DESCRIPTION_num(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
+ACCESS_DESCRIPTION *sk_ACCESS_DESCRIPTION_value(
+ Cryptography_STACK_OF_ACCESS_DESCRIPTION *, int
+);
+void sk_ACCESS_DESCRIPTION_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
+
X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *,
X509V3_CTX *, int, char *);
diff --git a/src/cryptography/hazmat/primitives/kdf/hkdf.py b/src/cryptography/hazmat/primitives/kdf/hkdf.py
index 65b7091a..f738bbdc 100644
--- a/src/cryptography/hazmat/primitives/kdf/hkdf.py
+++ b/src/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -26,7 +26,7 @@ class HKDF(object):
self._algorithm = algorithm
- if not isinstance(salt, bytes) and salt is not None:
+ if not (salt is None or isinstance(salt, bytes)):
raise TypeError("salt must be bytes.")
if salt is None:
@@ -77,7 +77,7 @@ class HKDFExpand(object):
self._length = length
- if not isinstance(info, bytes) and info is not None:
+ if not (info is None or isinstance(info, bytes)):
raise TypeError("info must be bytes.")
if info is None:
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index b22ac8be..0d87cd51 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -67,6 +67,8 @@ _OID_NAMES = {
"1.3.6.1.5.5.7.1.1": "authorityInfoAccess",
"1.3.6.1.5.5.7.1.11": "subjectInfoAccess",
"1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck",
+ "1.3.6.1.5.5.7.48.1": "OCSP",
+ "1.3.6.1.5.5.7.48.2": "caIssuers",
}
@@ -277,11 +279,10 @@ class Extension(object):
class ExtendedKeyUsage(object):
def __init__(self, usages):
- for oid in usages:
- if not isinstance(oid, ObjectIdentifier):
- raise TypeError(
- "Every item in the usages list must be an ObjectIdentifier"
- )
+ if not all(isinstance(x, ObjectIdentifier) for x in usages):
+ raise TypeError(
+ "Every item in the usages list must be an ObjectIdentifier"
+ )
self._usages = usages
@@ -294,6 +295,15 @@ class ExtendedKeyUsage(object):
def __repr__(self):
return "<ExtendedKeyUsage({0})>".format(self._usages)
+ def __eq__(self, other):
+ if not isinstance(other, ExtendedKeyUsage):
+ return NotImplemented
+
+ return self._usages == other._usages
+
+ def __ne__(self, other):
+ return not self == other
+
class BasicConstraints(object):
def __init__(self, ca, path_length):
@@ -386,6 +396,70 @@ class KeyUsage(object):
self, encipher_only, decipher_only)
+class AuthorityInformationAccess(object):
+ def __init__(self, descriptions):
+ if not all(isinstance(x, AccessDescription) for x in descriptions):
+ raise TypeError(
+ "Every item in the descriptions list must be an "
+ "AccessDescription"
+ )
+
+ self._descriptions = descriptions
+
+ def __iter__(self):
+ return iter(self._descriptions)
+
+ def __len__(self):
+ return len(self._descriptions)
+
+ def __repr__(self):
+ return "<AuthorityInformationAccess({0})>".format(self._descriptions)
+
+ def __eq__(self, other):
+ if not isinstance(other, AuthorityInformationAccess):
+ return NotImplemented
+
+ return self._descriptions == other._descriptions
+
+ def __ne__(self, other):
+ return not self == other
+
+
+class AccessDescription(object):
+ def __init__(self, access_method, access_location):
+ if not (access_method == OID_OCSP or access_method == OID_CA_ISSUERS):
+ raise ValueError(
+ "access_method must be OID_OCSP or OID_CA_ISSUERS"
+ )
+
+ if not isinstance(access_location, GeneralName):
+ raise TypeError("access_location must be a GeneralName")
+
+ self._access_method = access_method
+ self._access_location = access_location
+
+ def __repr__(self):
+ return (
+ "<AccessDescription(access_method={0.access_method}, access_locati"
+ "on={0.access_location})>".format(self)
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, AccessDescription):
+ return NotImplemented
+
+ return (
+ self.access_method == other.access_method and
+ self.access_location == other.access_location
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+ access_method = utils.read_only_property("_access_method")
+ access_location = utils.read_only_property("_access_location")
+
+
class SubjectKeyIdentifier(object):
def __init__(self, digest):
self._digest = digest
@@ -592,8 +666,13 @@ class AuthorityKeyIdentifier(object):
"must both be present or both None"
)
- if not isinstance(authority_cert_issuer, Name):
- raise TypeError("authority_cert_issuer must be a Name")
+ if not all(
+ isinstance(x, GeneralName) for x in authority_cert_issuer
+ ):
+ raise TypeError(
+ "authority_cert_issuer must be a list of GeneralName "
+ "objects"
+ )
if not isinstance(authority_cert_serial_number, six.integer_types):
raise TypeError(
@@ -672,6 +751,9 @@ OID_EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4")
OID_TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8")
OID_OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9")
+OID_CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2")
+OID_OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1")
+
@six.add_metaclass(abc.ABCMeta)
class Certificate(object):