aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/exceptions.py14
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py79
-rw-r--r--src/cryptography/hazmat/bindings/openssl/asn1.py1
-rw-r--r--src/cryptography/hazmat/bindings/openssl/ssl.py2
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/utils.py7
-rw-r--r--src/cryptography/hazmat/primitives/twofactor/__init__.py4
-rw-r--r--src/cryptography/hazmat/primitives/twofactor/hotp.py3
-rw-r--r--src/cryptography/hazmat/primitives/twofactor/totp.py3
-rw-r--r--src/cryptography/utils.py1
-rw-r--r--src/cryptography/x509.py236
10 files changed, 334 insertions, 16 deletions
diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py
index 102165c7..a4292eb8 100644
--- a/src/cryptography/exceptions.py
+++ b/src/cryptography/exceptions.py
@@ -6,6 +6,9 @@ from __future__ import absolute_import, division, print_function
from enum import Enum
+from cryptography import utils
+from cryptography.hazmat.primitives import twofactor
+
class _Reasons(Enum):
BACKEND_MISSING_INTERFACE = 0
@@ -53,5 +56,12 @@ class InvalidKey(Exception):
pass
-class InvalidToken(Exception):
- pass
+InvalidToken = utils.deprecated(
+ twofactor.InvalidToken,
+ __name__,
+ (
+ "The InvalidToken exception has moved to the "
+ "cryptography.hazmat.primitives.twofactor module"
+ ),
+ utils.DeprecatedIn09
+)
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 5d47c5ea..c21aeeb1 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -14,7 +14,8 @@
from __future__ import absolute_import, division, print_function
import datetime
-import warnings
+
+import idna
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
@@ -58,6 +59,23 @@ def _build_x509_name(backend, x509_name):
return x509.Name(attributes)
+def _build_general_name(backend, gn):
+ if gn.type == backend._lib.GEN_DNS:
+ data = backend._ffi.buffer(gn.d.dNSName.data, gn.d.dNSName.length)[:]
+ return x509.DNSName(idna.decode(data))
+ elif gn.type == backend._lib.GEN_RID:
+ oid = _obj2txt(backend, gn.d.registeredID)
+ return x509.RegisteredID(x509.ObjectIdentifier(oid))
+ else:
+ # otherName, x400Address or ediPartyName
+ raise x509.UnsupportedGeneralNameType(
+ "{0} is not a supported type".format(
+ x509._GENERAL_NAMES.get(gn.type, gn.type)
+ ),
+ gn.type
+ )
+
+
@utils.register_interface(x509.Certificate)
class _Certificate(object):
def __init__(self, backend, x509):
@@ -172,14 +190,10 @@ class _Certificate(object):
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(
- "Extension support is not fully implemented. A key usage "
- "extension with the critical flag was seen and IGNORED."
- )
- seen_oids.add(oid)
- continue
+ elif oid == x509.OID_KEY_USAGE:
+ value = self._build_key_usage(ext)
+ elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME:
+ value = self._build_subject_alt_name(ext)
elif critical:
raise x509.UnsupportedExtension(
"{0} is not currently supported".format(oid), oid
@@ -232,6 +246,53 @@ class _Certificate(object):
self._backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
)
+ def _build_key_usage(self, ext):
+ bit_string = self._backend._lib.X509V3_EXT_d2i(ext)
+ assert bit_string != self._backend._ffi.NULL
+ bit_string = self._backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
+ bit_string = self._backend._ffi.gc(
+ bit_string, self._backend._lib.ASN1_BIT_STRING_free
+ )
+ get_bit = self._backend._lib.ASN1_BIT_STRING_get_bit
+ digital_signature = get_bit(bit_string, 0) == 1
+ content_commitment = get_bit(bit_string, 1) == 1
+ key_encipherment = get_bit(bit_string, 2) == 1
+ data_encipherment = get_bit(bit_string, 3) == 1
+ key_agreement = get_bit(bit_string, 4) == 1
+ key_cert_sign = get_bit(bit_string, 5) == 1
+ crl_sign = get_bit(bit_string, 6) == 1
+ encipher_only = get_bit(bit_string, 7) == 1
+ decipher_only = get_bit(bit_string, 8) == 1
+ return x509.KeyUsage(
+ digital_signature,
+ content_commitment,
+ key_encipherment,
+ data_encipherment,
+ key_agreement,
+ key_cert_sign,
+ crl_sign,
+ encipher_only,
+ decipher_only
+ )
+
+ def _build_subject_alt_name(self, ext):
+ gns = self._backend._ffi.cast(
+ "GENERAL_NAMES *", self._backend._lib.X509V3_EXT_d2i(ext)
+ )
+ assert gns != self._backend._ffi.NULL
+ gns = self._backend._ffi.gc(gns, self._backend._lib.GENERAL_NAMES_free)
+ num = self._backend._lib.sk_GENERAL_NAME_num(gns)
+ general_names = []
+
+ for i in range(num):
+ gn = self._backend._lib.sk_GENERAL_NAME_value(gns, i)
+ assert gn != self._backend._ffi.NULL
+ value = _build_general_name(self._backend, gn)
+
+ general_names.append(value)
+
+ return x509.SubjectAlternativeName(general_names)
+
@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 45dfe758..475bd052 100644
--- a/src/cryptography/hazmat/bindings/openssl/asn1.py
+++ b/src/cryptography/hazmat/bindings/openssl/asn1.py
@@ -120,6 +120,7 @@ int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *, int, int);
"""
MACROS = """
+void ASN1_BIT_STRING_free(ASN1_BIT_STRING *);
/* 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 *);
diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py
index d680c3a5..6161a9d1 100644
--- a/src/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/src/cryptography/hazmat/bindings/openssl/ssl.py
@@ -526,7 +526,7 @@ static const long Cryptography_HAS_NEXTPROTONEG = 1;
#endif
/* ALPN was added in OpenSSL 1.0.2. */
-#if OPENSSL_VERSION_NUMBER < 0x10002001L
+#if OPENSSL_VERSION_NUMBER < 0x10002001L && !defined(LIBRESSL_VERSION_NUMBER)
int (*SSL_CTX_set_alpn_protos)(SSL_CTX *,
const unsigned char *,
unsigned) = NULL;
diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py
index 71f4ff8e..29390e40 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/utils.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py
@@ -4,6 +4,7 @@
from __future__ import absolute_import, division, print_function
+from pyasn1.codec.ber import eoo
from pyasn1.codec.der import decoder, encoder
from pyasn1.error import PyAsn1Error
from pyasn1.type import namedtype, univ
@@ -28,6 +29,12 @@ def decode_rfc6979_signature(signature):
raise ValueError(
"The signature contains bytes after the end of the ASN.1 sequence."
)
+ # pyasn1 can erroneously return this from top-level DER decoding.
+ # It's intended as a sentinel in recursive BER decoding, so it's
+ # returned even though an asn1Spec is provided.
+ if eoo.endOfOctets.isSameTypeWith(data) and data == eoo.endOfOctets:
+ raise ValueError("Invalid signature data. Unable to decode ASN.1")
+
r = int(data.getComponentByName('r'))
s = int(data.getComponentByName('s'))
return (r, s)
diff --git a/src/cryptography/hazmat/primitives/twofactor/__init__.py b/src/cryptography/hazmat/primitives/twofactor/__init__.py
index 4b540884..e71f9e67 100644
--- a/src/cryptography/hazmat/primitives/twofactor/__init__.py
+++ b/src/cryptography/hazmat/primitives/twofactor/__init__.py
@@ -3,3 +3,7 @@
# for complete details.
from __future__ import absolute_import, division, print_function
+
+
+class InvalidToken(Exception):
+ pass
diff --git a/src/cryptography/hazmat/primitives/twofactor/hotp.py b/src/cryptography/hazmat/primitives/twofactor/hotp.py
index 1dac920f..ba228b40 100644
--- a/src/cryptography/hazmat/primitives/twofactor/hotp.py
+++ b/src/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -9,11 +9,12 @@ import struct
import six
from cryptography.exceptions import (
- InvalidToken, UnsupportedAlgorithm, _Reasons
+ UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, hmac
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
+from cryptography.hazmat.primitives.twofactor import InvalidToken
class HOTP(object):
diff --git a/src/cryptography/hazmat/primitives/twofactor/totp.py b/src/cryptography/hazmat/primitives/twofactor/totp.py
index 0b04a131..03df9292 100644
--- a/src/cryptography/hazmat/primitives/twofactor/totp.py
+++ b/src/cryptography/hazmat/primitives/twofactor/totp.py
@@ -5,10 +5,11 @@
from __future__ import absolute_import, division, print_function
from cryptography.exceptions import (
- InvalidToken, UnsupportedAlgorithm, _Reasons
+ UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time
+from cryptography.hazmat.primitives.twofactor import InvalidToken
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py
index 0f8cbb27..445554ec 100644
--- a/src/cryptography/utils.py
+++ b/src/cryptography/utils.py
@@ -11,6 +11,7 @@ import warnings
DeprecatedIn08 = DeprecationWarning
+DeprecatedIn09 = PendingDeprecationWarning
def read_only_property(name):
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index a9b6f8bd..dd6ea926 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -5,6 +5,7 @@
from __future__ import absolute_import, division, print_function
import abc
+import ipaddress
from enum import Enum
import six
@@ -69,6 +70,19 @@ _OID_NAMES = {
}
+_GENERAL_NAMES = {
+ 0: "otherName",
+ 1: "rfc822Name",
+ 2: "dNSName",
+ 3: "x400Address",
+ 4: "directoryName",
+ 5: "ediPartyName",
+ 6: "uniformResourceIdentifier",
+ 7: "iPAddress",
+ 8: "registeredID",
+}
+
+
class Version(Enum):
v1 = 0
v3 = 2
@@ -114,6 +128,12 @@ class ExtensionNotFound(Exception):
self.oid = oid
+class UnsupportedGeneralNameType(Exception):
+ def __init__(self, msg, type):
+ super(UnsupportedGeneralNameType, self).__init__(msg)
+ self.type = type
+
+
class NameAttribute(object):
def __init__(self, oid, value):
if not isinstance(oid, ObjectIdentifier):
@@ -353,8 +373,8 @@ class KeyUsage(object):
encipher_only = self.encipher_only
decipher_only = self.decipher_only
except ValueError:
- encipher_only = "N/A"
- decipher_only = "N/A"
+ encipher_only = None
+ decipher_only = None
return ("<KeyUsage(digital_signature={0.digital_signature}, "
"content_commitment={0.content_commitment}, "
@@ -387,6 +407,218 @@ class SubjectKeyIdentifier(object):
return not self == other
+@six.add_metaclass(abc.ABCMeta)
+class GeneralName(object):
+ @abc.abstractproperty
+ def value(self):
+ """
+ Return the value of the object
+ """
+
+
+@utils.register_interface(GeneralName)
+class RFC822Name(object):
+ def __init__(self, value):
+ if not isinstance(value, six.text_type):
+ raise TypeError("value must be a unicode string")
+
+ self._value = value
+
+ value = utils.read_only_property("_value")
+
+ def __repr__(self):
+ return "<RFC822Name(value={0})>".format(self.value)
+
+ def __eq__(self, other):
+ if not isinstance(other, RFC822Name):
+ return NotImplemented
+
+ return self.value == other.value
+
+ def __ne__(self, other):
+ return not self == other
+
+
+@utils.register_interface(GeneralName)
+class DNSName(object):
+ def __init__(self, value):
+ if not isinstance(value, six.text_type):
+ raise TypeError("value must be a unicode string")
+
+ self._value = value
+
+ value = utils.read_only_property("_value")
+
+ def __repr__(self):
+ return "<DNSName(value={0})>".format(self.value)
+
+ def __eq__(self, other):
+ if not isinstance(other, DNSName):
+ return NotImplemented
+
+ return self.value == other.value
+
+ def __ne__(self, other):
+ return not self == other
+
+
+@utils.register_interface(GeneralName)
+class UniformResourceIdentifier(object):
+ def __init__(self, value):
+ if not isinstance(value, six.text_type):
+ raise TypeError("value must be a unicode string")
+
+ self._value = value
+
+ value = utils.read_only_property("_value")
+
+ def __repr__(self):
+ return "<UniformResourceIdentifier(value={0})>".format(self.value)
+
+ def __eq__(self, other):
+ if not isinstance(other, UniformResourceIdentifier):
+ return NotImplemented
+
+ return self.value == other.value
+
+ def __ne__(self, other):
+ return not self == other
+
+
+@utils.register_interface(GeneralName)
+class DirectoryName(object):
+ def __init__(self, value):
+ if not isinstance(value, Name):
+ raise TypeError("value must be a Name")
+
+ self._value = value
+
+ value = utils.read_only_property("_value")
+
+ def __repr__(self):
+ return "<DirectoryName(value={0})>".format(self.value)
+
+ def __eq__(self, other):
+ if not isinstance(other, DirectoryName):
+ return NotImplemented
+
+ return self.value == other.value
+
+ def __ne__(self, other):
+ return not self == other
+
+
+@utils.register_interface(GeneralName)
+class RegisteredID(object):
+ def __init__(self, value):
+ if not isinstance(value, ObjectIdentifier):
+ raise TypeError("value must be an ObjectIdentifier")
+
+ self._value = value
+
+ value = utils.read_only_property("_value")
+
+ def __repr__(self):
+ return "<RegisteredID(value={0})>".format(self.value)
+
+ def __eq__(self, other):
+ if not isinstance(other, RegisteredID):
+ return NotImplemented
+
+ return self.value == other.value
+
+ def __ne__(self, other):
+ return not self == other
+
+
+@utils.register_interface(GeneralName)
+class IPAddress(object):
+ def __init__(self, value):
+ if not isinstance(
+ value, (ipaddress.IPv4Address, ipaddress.IPv6Address)
+ ):
+ raise TypeError(
+ "value must be an instance of ipaddress.IPv4Address or "
+ "ipaddress.IPv6Address"
+ )
+
+ self._value = value
+
+ value = utils.read_only_property("_value")
+
+ def __repr__(self):
+ return "<IPAddress(value={0})>".format(self.value)
+
+ def __eq__(self, other):
+ if not isinstance(other, IPAddress):
+ return NotImplemented
+
+ return self.value == other.value
+
+ def __ne__(self, other):
+ return not self == other
+
+
+class SubjectAlternativeName(object):
+ def __init__(self, general_names):
+ if not all(isinstance(x, GeneralName) for x in general_names):
+ raise TypeError(
+ "Every item in the general_names list must be an "
+ "object conforming to the GeneralName interface"
+ )
+
+ self._general_names = 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 [i.value for i in self if isinstance(i, type)]
+
+ def __repr__(self):
+ return "<SubjectAlternativeName({0})>".format(self._general_names)
+
+
+class AuthorityKeyIdentifier(object):
+ def __init__(self, key_identifier, authority_cert_issuer,
+ authority_cert_serial_number):
+ if authority_cert_issuer or authority_cert_serial_number:
+ if not authority_cert_issuer or not authority_cert_serial_number:
+ raise ValueError(
+ "authority_cert_issuer and authority_cert_serial_number "
+ "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 isinstance(authority_cert_serial_number, six.integer_types):
+ raise TypeError(
+ "authority_cert_serial_number must be an integer"
+ )
+
+ self._key_identifier = key_identifier
+ self._authority_cert_issuer = authority_cert_issuer
+ self._authority_cert_serial_number = authority_cert_serial_number
+
+ def __repr__(self):
+ return (
+ "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, "
+ "authority_cert_issuer={0.authority_cert_issuer}, "
+ "authority_cert_serial_number={0.authority_cert_serial_number}"
+ ")>".format(self)
+ )
+
+ key_identifier = utils.read_only_property("_key_identifier")
+ authority_cert_issuer = utils.read_only_property("_authority_cert_issuer")
+ authority_cert_serial_number = utils.read_only_property(
+ "_authority_cert_serial_number"
+ )
+
+
OID_COMMON_NAME = ObjectIdentifier("2.5.4.3")
OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7")