diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2018-10-07 10:10:09 +0800 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2018-10-06 22:10:09 -0400 |
commit | 0c07580a216d4b75bfdca22254803cf48c602079 (patch) | |
tree | e308db30d277fab192a5b647037b12cb901c2129 | |
parent | ff7e3971d8d1106a4377f6c8d436c4005c883066 (diff) | |
download | cryptography-0c07580a216d4b75bfdca22254803cf48c602079.tar.gz cryptography-0c07580a216d4b75bfdca22254803cf48c602079.tar.bz2 cryptography-0c07580a216d4b75bfdca22254803cf48c602079.zip |
support extensions in the OCSP request builder (#4481)
* support extensions in the OCSP request builder
* cover a missed branch
* refactor to use new func
* review feedback
-rw-r--r-- | docs/x509/ocsp.rst | 10 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 8 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/encode_asn1.py | 12 | ||||
-rw-r--r-- | src/cryptography/x509/ocsp.py | 23 | ||||
-rw-r--r-- | tests/hazmat/backends/test_openssl_memleak.py | 21 | ||||
-rw-r--r-- | tests/x509/test_ocsp.py | 32 |
6 files changed, 100 insertions, 6 deletions
diff --git a/docs/x509/ocsp.rst b/docs/x509/ocsp.rst index bf064134..528502a7 100644 --- a/docs/x509/ocsp.rst +++ b/docs/x509/ocsp.rst @@ -133,6 +133,16 @@ Creating Requests :class:`~cryptography.hazmat.primitives.hashes.SHA384`, and :class:`~cryptography.hazmat.primitives.hashes.SHA512` are allowed. + .. method:: add_extension(extension, critical) + + Adds an extension to the request. + + :param extension: An extension conforming to the + :class:`~cryptography.x509.ExtensionType` interface. + + :param critical: Set to ``True`` if the extension must be understood and + handled. + .. method:: build() :returns: A new :class:`~cryptography.x509.ocsp.OCSPRequest`. diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 8118cad0..5d0a4446 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -38,6 +38,7 @@ from cryptography.hazmat.backends.openssl.ec import ( from cryptography.hazmat.backends.openssl.encode_asn1 import ( _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS, _CRL_EXTENSION_ENCODE_HANDLERS, _EXTENSION_ENCODE_HANDLERS, + _OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS, _encode_asn1_int_gc, _encode_asn1_str_gc, _encode_name_gc, _txt2obj_gc, ) from cryptography.hazmat.backends.openssl.hashes import _HashContext @@ -1465,6 +1466,13 @@ class Backend(object): self.openssl_assert(certid != self._ffi.NULL) onereq = self._lib.OCSP_request_add0_id(ocsp_req, certid) self.openssl_assert(onereq != self._ffi.NULL) + self._create_x509_extensions( + extensions=builder._extensions, + handlers=_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS, + x509_obj=ocsp_req, + add_func=self._lib.OCSP_REQUEST_add_ext, + gc=True, + ) return _OCSPRequest(self, ocsp_req) def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py index 91852dff..c8b41a81 100644 --- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py @@ -15,7 +15,9 @@ from cryptography.hazmat.backends.openssl.decode_asn1 import ( _DISTPOINT_TYPE_RELATIVENAME ) from cryptography.x509.name import _ASN1Type -from cryptography.x509.oid import CRLEntryExtensionOID, ExtensionOID +from cryptography.x509.oid import ( + CRLEntryExtensionOID, ExtensionOID, OCSPExtensionOID, +) def _encode_asn1_int(backend, x): @@ -569,6 +571,10 @@ def _encode_general_subtree(backend, subtrees): return general_subtrees +def _encode_nonce(backend, nonce): + return _encode_asn1_str_gc(backend, nonce.nonce) + + _EXTENSION_ENCODE_HANDLERS = { ExtensionOID.BASIC_CONSTRAINTS: _encode_basic_constraints, ExtensionOID.SUBJECT_KEY_IDENTIFIER: _encode_subject_key_identifier, @@ -604,3 +610,7 @@ _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = { CRLEntryExtensionOID.CRL_REASON: _encode_crl_reason, CRLEntryExtensionOID.INVALIDITY_DATE: _encode_invalidity_date, } + +_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS = { + OCSPExtensionOID.NONCE: _encode_nonce, +} diff --git a/src/cryptography/x509/ocsp.py b/src/cryptography/x509/ocsp.py index fbf11336..c89f12ce 100644 --- a/src/cryptography/x509/ocsp.py +++ b/src/cryptography/x509/ocsp.py @@ -9,8 +9,9 @@ from enum import Enum import six +from cryptography import x509 from cryptography.hazmat.primitives import hashes -from cryptography.x509 import Certificate +from cryptography.x509.base import _reject_duplicate_extension _OIDS_TO_HASH = { @@ -54,8 +55,9 @@ def load_der_ocsp_response(data): class OCSPRequestBuilder(object): - def __init__(self, request=None): + def __init__(self, request=None, extensions=[]): self._request = request + self._extensions = extensions def add_certificate(self, cert, issuer, algorithm): if self._request is not None: @@ -70,12 +72,23 @@ class OCSPRequestBuilder(object): "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" ) if ( - not isinstance(cert, Certificate) or - not isinstance(issuer, Certificate) + not isinstance(cert, x509.Certificate) or + not isinstance(issuer, x509.Certificate) ): raise TypeError("cert and issuer must be a Certificate") - return OCSPRequestBuilder((cert, issuer, algorithm)) + return OCSPRequestBuilder((cert, issuer, algorithm), self._extensions) + + def add_extension(self, extension, critical): + if not isinstance(extension, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extension.oid, critical, extension) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPRequestBuilder( + self._request, self._extensions + [extension] + ) def build(self): from cryptography.hazmat.backends.openssl.backend import backend diff --git a/tests/hazmat/backends/test_openssl_memleak.py b/tests/hazmat/backends/test_openssl_memleak.py index 34ad11ba..483387af 100644 --- a/tests/hazmat/backends/test_openssl_memleak.py +++ b/tests/hazmat/backends/test_openssl_memleak.py @@ -286,3 +286,24 @@ class TestOpenSSLMemoryLeaks(object): private_key = x25519.X25519PrivateKey.generate() private_key.public_key() """)) + + def test_create_ocsp_request(self): + assert_no_memory_leaks(textwrap.dedent(""" + def func(): + from cryptography import x509 + from cryptography.hazmat.backends.openssl import backend + from cryptography.hazmat.primitives import hashes + from cryptography.x509 import ocsp + import cryptography_vectors + + path = "x509/PKITS_data/certs/ValidcRLIssuerTest28EE.crt" + with cryptography_vectors.open_vector_file(path, "rb") as f: + cert = x509.load_der_x509_certificate( + f.read(), backend + ) + builder = ocsp.OCSPRequestBuilder() + builder = builder.add_certificate( + cert, cert, hashes.SHA1() + ).add_extension(x509.OCSPNonce(b"0000"), False) + req = builder.build() + """)) diff --git a/tests/x509/test_ocsp.py b/tests/x509/test_ocsp.py index 0d98ac29..d680e07f 100644 --- a/tests/x509/test_ocsp.py +++ b/tests/x509/test_ocsp.py @@ -129,6 +129,17 @@ class TestOCSPRequestBuilder(object): with pytest.raises(ValueError): builder.add_certificate(cert, issuer, hashes.MD5()) + def test_add_extension_twice(self): + builder = ocsp.OCSPRequestBuilder() + builder = builder.add_extension(x509.OCSPNonce(b"123"), False) + with pytest.raises(ValueError): + builder.add_extension(x509.OCSPNonce(b"123"), False) + + def test_add_invalid_extension(self): + builder = ocsp.OCSPRequestBuilder() + with pytest.raises(TypeError): + builder.add_extension("notanext", False) + def test_create_ocsp_request_invalid_cert(self): cert, issuer = _cert_and_issuer() builder = ocsp.OCSPRequestBuilder() @@ -149,6 +160,27 @@ class TestOCSPRequestBuilder(object): b"/NNGCDS7zkZ/oHxb8+IIy1kCAj8g" ) + @pytest.mark.parametrize( + ("ext", "critical"), + [ + [x509.OCSPNonce(b"0000"), False], + [x509.OCSPNonce(b"\x00\x01\x02"), True], + ] + ) + def test_create_ocsp_request_with_extension(self, ext, critical): + cert, issuer = _cert_and_issuer() + builder = ocsp.OCSPRequestBuilder() + builder = builder.add_certificate( + cert, issuer, hashes.SHA1() + ).add_extension( + ext, critical + ) + req = builder.build() + assert len(req.extensions) == 1 + assert req.extensions[0].value == ext + assert req.extensions[0].oid == ext.oid + assert req.extensions[0].critical is critical + class TestOCSPResponse(object): def test_bad_response(self): |