diff options
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 22 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/ssl.py | 3 | ||||
-rw-r--r-- | tests/test_x509_ext.py | 88 |
4 files changed, 115 insertions, 0 deletions
diff --git a/.travis.yml b/.travis.yml index 71efd8ff..c7413ea9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -120,3 +120,5 @@ notifications: - "irc.freenode.org#cryptography-dev" use_notice: true skip_join: true + webhooks: + - https://buildtimetrend.herokuapp.com/travis diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 7f633c76..4ba66bb7 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( diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py index b182180f..4a824ae5 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 *); diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index c574aedb..05734b42 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -871,3 +871,91 @@ class TestRSASubjectAlternativeNameExtension(object): x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), ]) ] == dirname + + def test_rfc822name(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "san_rfc822_idna.pem" + ), + x509.load_pem_x509_certificate, + backend + ) + ext = cert.extensions.get_extension_for_oid( + x509.OID_SUBJECT_ALTERNATIVE_NAME + ) + assert ext is not None + assert ext.critical is False + + san = ext.value + + rfc822name = san.get_values_for_type(x509.RFC822Name) + assert [u"email@em\xe5\xefl.com"] == rfc822name + + def test_unicode_rfc822_name_dns_name_uri(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "san_idna_names.pem" + ), + x509.load_pem_x509_certificate, + backend + ) + ext = cert.extensions.get_extension_for_oid( + x509.OID_SUBJECT_ALTERNATIVE_NAME + ) + assert ext is not None + rfc822_name = ext.value.get_values_for_type(x509.RFC822Name) + dns_name = ext.value.get_values_for_type(x509.DNSName) + uri = ext.value.get_values_for_type(x509.UniformResourceIdentifier) + assert rfc822_name == [u"email@\u043f\u044b\u043a\u0430.cryptography"] + assert dns_name == [u"\u043f\u044b\u043a\u0430.cryptography"] + assert uri == [u"https://www.\u043f\u044b\u043a\u0430.cryptography"] + + def test_rfc822name_dnsname_ipaddress_directoryname_uri(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "san_email_dns_ip_dirname_uri.pem" + ), + x509.load_pem_x509_certificate, + backend + ) + ext = cert.extensions.get_extension_for_oid( + x509.OID_SUBJECT_ALTERNATIVE_NAME + ) + assert ext is not None + assert ext.critical is False + + san = ext.value + + rfc822_name = san.get_values_for_type(x509.RFC822Name) + uri = san.get_values_for_type(x509.UniformResourceIdentifier) + dns = san.get_values_for_type(x509.DNSName) + ip = san.get_values_for_type(x509.IPAddress) + dirname = san.get_values_for_type(x509.DirectoryName) + assert [u"user@cryptography.io"] == rfc822_name + assert [u"https://cryptography.io"] == uri + assert [u"cryptography.io"] == dns + assert [ + x509.Name([ + x509.NameAttribute(x509.OID_COMMON_NAME, 'dirCN'), + x509.NameAttribute( + x509.OID_ORGANIZATION_NAME, 'Cryptographic Authority' + ), + ]) + ] == dirname + assert [ + ipaddress.ip_address(u"127.0.0.1"), + ipaddress.ip_address(u"ff::") + ] == ip + + def test_invalid_rfc822name(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "san_rfc822_names.pem" + ), + x509.load_pem_x509_certificate, + backend + ) + with pytest.raises(ValueError) as exc: + cert.extensions + + assert 'Invalid rfc822name value' in str(exc.value) |