From d3e84164d9932782beebfb997615bca6f6d30a8b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 28 Jun 2015 10:14:55 -0400 Subject: Initial code to encode SANs --- src/_cffi_src/openssl/asn1.py | 5 ++- src/_cffi_src/openssl/x509v3.py | 6 +++ .../hazmat/backends/openssl/backend.py | 47 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py index c18708c5..5210c7c9 100644 --- a/src/_cffi_src/openssl/asn1.py +++ b/src/_cffi_src/openssl/asn1.py @@ -42,7 +42,7 @@ typedef struct asn1_string_st ASN1_OCTET_STRING; typedef struct asn1_string_st ASN1_IA5STRING; typedef ... ASN1_BIT_STRING; typedef ... ASN1_OBJECT; -typedef ... ASN1_STRING; +typedef struct asn1_string_st ASN1_STRING; typedef ... ASN1_TYPE; typedef ... ASN1_GENERALIZEDTIME; typedef ... ASN1_ENUMERATED; @@ -87,6 +87,9 @@ ASN1_OCTET_STRING *ASN1_OCTET_STRING_new(void); void ASN1_OCTET_STRING_free(ASN1_OCTET_STRING *); int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *, const unsigned char *, int); +/* ASN1 IA5STRING */ +ASN1_IA5STRING *ASN1_IA5STRING_new(void); + /* ASN1 INTEGER */ ASN1_INTEGER *ASN1_INTEGER_new(void); void ASN1_INTEGER_free(ASN1_INTEGER *); diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 166fa59d..36d90911 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -172,7 +172,9 @@ FUNCTIONS = """ int X509V3_EXT_add_alias(int, int); void X509V3_set_ctx(X509V3_CTX *, X509 *, X509 *, X509_REQ *, X509_CRL *, int); X509_EXTENSION *X509V3_EXT_nconf(CONF *, X509V3_CTX *, char *, char *); +GENERAL_NAME *GENERAL_NAME_new(void); int GENERAL_NAME_print(BIO *, GENERAL_NAME *); +GENERAL_NAMES *GENERAL_NAMES_new(void); void GENERAL_NAMES_free(GENERAL_NAMES *); void *X509V3_EXT_d2i(X509_EXTENSION *); """ @@ -183,6 +185,7 @@ MACROS = """ int i2d_BASIC_CONSTRAINTS(BASIC_CONSTRAINTS *, unsigned char **); BASIC_CONSTRAINTS *BASIC_CONSTRAINTS_new(void); 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 *); @@ -191,6 +194,9 @@ NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void); void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *); void *X509V3_set_ctx_nodb(X509V3_CTX *); + +int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **); + 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); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 18faecb3..c2a3dc2d 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -146,6 +146,47 @@ def _encode_basic_constraints(backend, basic_constraints, critical): return extension +def _encode_subject_alt_name(backend, san, critical): + obj = _txt2obj(backend, x509.OID_SUBJECT_ALTERNATIVE_NAME.dotted_string) + assert obj is not None + + general_names = backend._lib.GENERAL_NAMES_new() + assert general_names != backend._ffi.NULL + # TODO: GC + + for alt_name in san: + assert isinstance(alt_name, x509.DNSName) + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + gn.type = backend._lib.GEN_DNS + ia5 = backend._lib.ASN1_IA5STRING_new() + assert ia5 != backend._ffi.NULL + gn.d.dNSName = ia5 + # TODO: idna + value = alt_name.value.encode("ascii") + res = backend._lib.ASN1_STRING_set(gn.d.dNSName, value, len(value)) + assert res == 1 + + res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) + assert res == 1 + + pp = backend._ffi.new("unsigned char **") + r = backend._lib.i2d_GENERAL_NAMES(general_names, pp) + assert r > 0 + pp = backend._ffi.gc( + pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ) + + extension = backend._lib.X509_EXTENSION_create_by_OBJ( + backend._ffi.NULL, + obj, + 1 if critical else 0, + _encode_asn1_str(backend, pp[0], r) + ) + assert extension != backend._ffi.NULL + return extension + + @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DERSerializationBackend) @@ -857,6 +898,12 @@ class Backend(object): extension.value, extension.critical ) + elif isinstance(extension.value, x509.SubjectAlternativeName): + extension = _encode_subject_alt_name( + self, + extension.value, + extension.critical, + ) else: raise NotImplementedError('Extension not yet supported.') res = self._lib.sk_X509_EXTENSION_push(extensions, extension) -- cgit v1.2.3 From 6eeb877becb90f1a8209a8ac4db96e9b440265a6 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 28 Jun 2015 11:03:37 -0400 Subject: Clean up code and fix. --- .../hazmat/backends/openssl/backend.py | 54 ++++++++-------------- src/cryptography/hazmat/backends/openssl/x509.py | 2 + 2 files changed, 20 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index c2a3dc2d..22179607 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -115,9 +115,7 @@ def _txt2obj(backend, name): return obj -def _encode_basic_constraints(backend, basic_constraints, critical): - obj = _txt2obj(backend, x509.OID_BASIC_CONSTRAINTS.dotted_string) - assert obj is not None +def _encode_basic_constraints(backend, basic_constraints): constraints = backend._lib.BASIC_CONSTRAINTS_new() constraints.ca = 255 if basic_constraints.ca else 0 if basic_constraints.ca: @@ -132,24 +130,10 @@ def _encode_basic_constraints(backend, basic_constraints, critical): pp = backend._ffi.gc( pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) ) - - # Wrap that in an X509 extension object. - extension = backend._lib.X509_EXTENSION_create_by_OBJ( - backend._ffi.NULL, - obj, - 1 if critical else 0, - _encode_asn1_str(backend, pp[0], r), - ) - assert extension != backend._ffi.NULL - - # Return the wrapped extension. - return extension + return pp, r -def _encode_subject_alt_name(backend, san, critical): - obj = _txt2obj(backend, x509.OID_SUBJECT_ALTERNATIVE_NAME.dotted_string) - assert obj is not None - +def _encode_subject_alt_name(backend, san): general_names = backend._lib.GENERAL_NAMES_new() assert general_names != backend._ffi.NULL # TODO: GC @@ -176,15 +160,7 @@ def _encode_subject_alt_name(backend, san, critical): pp = backend._ffi.gc( pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) ) - - extension = backend._lib.X509_EXTENSION_create_by_OBJ( - backend._ffi.NULL, - obj, - 1 if critical else 0, - _encode_asn1_str(backend, pp[0], r) - ) - assert extension != backend._ffi.NULL - return extension + return pp, r @utils.register_interface(CipherBackend) @@ -893,19 +869,25 @@ class Backend(object): ) for extension in builder._extensions: if isinstance(extension.value, x509.BasicConstraints): - extension = _encode_basic_constraints( - self, - extension.value, - extension.critical + pp, r = _encode_basic_constraints( + self, extension.value, ) elif isinstance(extension.value, x509.SubjectAlternativeName): - extension = _encode_subject_alt_name( - self, - extension.value, - extension.critical, + pp, r = _encode_subject_alt_name( + self, extension.value, ) else: raise NotImplementedError('Extension not yet supported.') + + obj = _txt2obj(self, extension.oid.dotted_string) + extension = backend._lib.X509_EXTENSION_create_by_OBJ( + backend._ffi.NULL, + obj, + 1 if extension.critical else 0, + _encode_asn1_str(backend, pp[0], r) + ) + assert extension != backend._ffi.NULL + res = self._lib.sk_X509_EXTENSION_push(extensions, extension) assert res == 1 res = self._lib.X509_REQ_add_extensions(x509_req, extensions) diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 8e361fa2..b387b9ee 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -720,6 +720,8 @@ class _CertificateSigningRequest(object): ) elif oid == x509.OID_BASIC_CONSTRAINTS: value = _decode_basic_constraints(self._backend, ext) + elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME: + value = _decode_subject_alt_name(self._backend, ext) elif critical: raise x509.UnsupportedExtension( "{0} is not currently supported".format(oid), oid -- cgit v1.2.3 From ac7f70a1dc284339238fcfbdfba4f76476ab3d29 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 28 Jun 2015 11:07:52 -0400 Subject: fix the not implemeneted test --- src/cryptography/x509.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 668bc2ef..a091cd78 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1472,6 +1472,10 @@ class CertificateSigningRequestBuilder(object): extension = Extension( OID_SUBJECT_ALTERNATIVE_NAME, critical, extension ) + elif isinstance(extension, KeyUsage): + extension = Extension( + OID_KEY_USAGE, critical, extension + ) else: raise NotImplementedError('Unsupported X.509 extension.') # TODO: This is quadratic in the number of extensions -- cgit v1.2.3 From d74e336fdad10333718d189da535ce47e147a967 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 3 Jul 2015 10:10:27 -0400 Subject: reduce diff with master --- src/_cffi_src/openssl/x509v3.py | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 36d90911..52287459 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -185,7 +185,6 @@ MACROS = """ int i2d_BASIC_CONSTRAINTS(BASIC_CONSTRAINTS *, unsigned char **); BASIC_CONSTRAINTS *BASIC_CONSTRAINTS_new(void); 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 *); -- cgit v1.2.3 From d5f718c19c09f529ff34b319a1e2e0e7f1862a9a Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 5 Jul 2015 11:19:38 -0400 Subject: Organize code a bit better --- .../hazmat/backends/openssl/backend.py | 25 +++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f05b0515..753cb50d 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -139,20 +139,25 @@ def _encode_basic_constraints(backend, basic_constraints): def _encode_subject_alt_name(backend, san): general_names = backend._lib.GENERAL_NAMES_new() assert general_names != backend._ffi.NULL - # TODO: GC + general_names = backend._ffi.gc( + general_names, backend._lib.GENERAL_NAMES_free + ) for alt_name in san: - assert isinstance(alt_name, x509.DNSName) gn = backend._lib.GENERAL_NAME_new() assert gn != backend._ffi.NULL - gn.type = backend._lib.GEN_DNS - ia5 = backend._lib.ASN1_IA5STRING_new() - assert ia5 != backend._ffi.NULL - gn.d.dNSName = ia5 - # TODO: idna - value = alt_name.value.encode("ascii") - res = backend._lib.ASN1_STRING_set(gn.d.dNSName, value, len(value)) - assert res == 1 + # TODO: GC? + if isinstance(alt_name, x509.DNSName): + gn.type = backend._lib.GEN_DNS + ia5 = backend._lib.ASN1_IA5STRING_new() + assert ia5 != backend._ffi.NULL + # TODO: idna + value = alt_name.value.encode("ascii") + res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) + assert res == 1 + gn.d.dNSName = ia5 + else: + raise NotImplementedError("Only DNSNames are supported right now") res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) assert res == 1 -- cgit v1.2.3 From ba2abb102152a4b57f7661481c8bc53d473cbf09 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 5 Jul 2015 11:21:05 -0400 Subject: idna here --- src/cryptography/hazmat/backends/openssl/backend.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 753cb50d..52b5abcf 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -8,6 +8,8 @@ import collections import itertools from contextlib import contextmanager +import idna + import six from cryptography import utils, x509 @@ -151,8 +153,7 @@ def _encode_subject_alt_name(backend, san): gn.type = backend._lib.GEN_DNS ia5 = backend._lib.ASN1_IA5STRING_new() assert ia5 != backend._ffi.NULL - # TODO: idna - value = alt_name.value.encode("ascii") + value = idna.encode(alt_name.value) res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) assert res == 1 gn.d.dNSName = ia5 -- cgit v1.2.3 From 4de08a2e1e85b7e16b78074c20cee840d315d637 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 5 Jul 2015 11:22:14 -0400 Subject: this needs to be freed as well --- src/cryptography/hazmat/backends/openssl/backend.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 52b5abcf..767605c0 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -153,6 +153,7 @@ def _encode_subject_alt_name(backend, san): gn.type = backend._lib.GEN_DNS ia5 = backend._lib.ASN1_IA5STRING_new() assert ia5 != backend._ffi.NULL + # TODO: GC? value = idna.encode(alt_name.value) res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) assert res == 1 -- cgit v1.2.3 From 46501ce22c662600f45b3fadca54387a4520c119 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 5 Jul 2015 11:30:41 -0400 Subject: Paul says openssl just does the right thing here. --- src/cryptography/hazmat/backends/openssl/backend.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 767605c0..6f64613b 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -148,12 +148,10 @@ def _encode_subject_alt_name(backend, san): for alt_name in san: gn = backend._lib.GENERAL_NAME_new() assert gn != backend._ffi.NULL - # TODO: GC? if isinstance(alt_name, x509.DNSName): gn.type = backend._lib.GEN_DNS ia5 = backend._lib.ASN1_IA5STRING_new() assert ia5 != backend._ffi.NULL - # TODO: GC? value = idna.encode(alt_name.value) res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) assert res == 1 -- cgit v1.2.3 From 6431d50831b8e4a4927f5e6619603eeac78ff489 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 5 Jul 2015 12:28:46 -0400 Subject: Wildcards. Also fixed a bug with multiple GNs --- src/cryptography/hazmat/backends/openssl/backend.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 6f64613b..ec692926 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -152,7 +152,12 @@ def _encode_subject_alt_name(backend, san): gn.type = backend._lib.GEN_DNS ia5 = backend._lib.ASN1_IA5STRING_new() assert ia5 != backend._ffi.NULL - value = idna.encode(alt_name.value) + + if alt_name.value.startswith(u"*."): + value = b"*." + idna.encode(alt_name.value[2:]) + else: + value = idna.encode(alt_name.value) + res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) assert res == 1 gn.d.dNSName = ia5 @@ -160,7 +165,7 @@ def _encode_subject_alt_name(backend, san): raise NotImplementedError("Only DNSNames are supported right now") res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) - assert res == 1 + assert res >= 0 pp = backend._ffi.new("unsigned char **") r = backend._lib.i2d_GENERAL_NAMES(general_names, pp) -- cgit v1.2.3 From 8df780814d0f779dd80bf093ec8fcefc2071b248 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 5 Jul 2015 12:30:20 -0400 Subject: Make the error check match how the openssl codebase does it --- src/cryptography/hazmat/backends/openssl/backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index ec692926..06801bc9 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -165,7 +165,7 @@ def _encode_subject_alt_name(backend, san): raise NotImplementedError("Only DNSNames are supported right now") res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) - assert res >= 0 + assert res != 0 pp = backend._ffi.new("unsigned char **") r = backend._lib.i2d_GENERAL_NAMES(general_names, pp) -- cgit v1.2.3 From cc781e31f37d53c7d8ba0654db4e47e04c88f662 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 5 Jul 2015 12:43:20 -0400 Subject: fixed a leak in the event of non-DNS GN --- src/cryptography/hazmat/backends/openssl/backend.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 06801bc9..bbec6185 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -146,10 +146,11 @@ def _encode_subject_alt_name(backend, san): ) for alt_name in san: - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL if isinstance(alt_name, x509.DNSName): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL gn.type = backend._lib.GEN_DNS + ia5 = backend._lib.ASN1_IA5STRING_new() assert ia5 != backend._ffi.NULL -- cgit v1.2.3