diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2015-05-02 20:27:39 -0400 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2015-05-02 20:27:39 -0400 |
commit | 43ffcc267c8d7e35f58db4d8d7262de2bcf5db70 (patch) | |
tree | 5f476d64078130dfd525164106368c8d99fce305 | |
parent | b3c81f86f9677e77ff3c42fefeb2c1bc94dd063c (diff) | |
parent | e518faefba934a2bbf2589458170d50a69f9bdfc (diff) | |
download | cryptography-43ffcc267c8d7e35f58db4d8d7262de2bcf5db70.tar.gz cryptography-43ffcc267c8d7e35f58db4d8d7262de2bcf5db70.tar.bz2 cryptography-43ffcc267c8d7e35f58db4d8d7262de2bcf5db70.zip |
Merge pull request #1881 from reaperhulk/san-rfc822name
add support for rfc822name general names
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 22 | ||||
-rw-r--r-- | tests/test_x509_ext.py | 88 |
2 files changed, 110 insertions, 0 deletions
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/tests/test_x509_ext.py b/tests/test_x509_ext.py index d38fe573..c1d51e5e 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -856,3 +856,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) |