diff options
-rw-r--r-- | CHANGELOG.rst | 11 | ||||
-rw-r--r-- | docs/development/test-vectors.rst | 6 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/rsa.rst | 2 | ||||
-rw-r--r-- | docs/hazmat/primitives/mac/index.rst | 2 | ||||
-rw-r--r-- | docs/installation.rst | 2 | ||||
-rw-r--r-- | src/cryptography/exceptions.py | 4 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/commoncrypto/backend.py | 3 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 53 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/ciphers.py | 30 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/dsa.py | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/hashes.py | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/hmac.py | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/commoncrypto/binding.py | 3 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/binding.py | 46 | ||||
-rw-r--r-- | src/cryptography/utils.py | 14 | ||||
-rw-r--r-- | tests/hazmat/backends/test_openssl.py | 21 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_x963_vectors.py (renamed from tests/hazmat/primitives/test_X963_vectors.py) | 0 | ||||
-rw-r--r-- | tests/test_x509.py | 24 |
18 files changed, 120 insertions, 107 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 705c09cd..fdea8c35 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,17 @@ Changelog * Added :class:`~cryptography.hazmat.primitives.kdf.x963kdf.X963KDF`. +1.0.2 - 2015-09-27 +~~~~~~~~~~~~~~~~~~ +* **SECURITY ISSUE**: The OpenSSL backend prior to 1.0.2 made extensive use + of assertions to check response codes where our tests could not trigger a + failure. However, when Python is run with ``-O`` these asserts are optimized + away. If a user ran Python with this flag and got an invalid response code + this could result in undefined behavior or worse. Accordingly, all response + checks from the OpenSSL backend have been converted from ``assert`` + to a true function call. Credit **Emilia Käsper (Google Security Team)** + for the report. + 1.0.1 - 2015-09-05 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 0495cc77..25871d5c 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -396,14 +396,14 @@ header format (substituting the correct information): .. _`IETF`: https://www.ietf.org/ .. _`NIST CAVP`: http://csrc.nist.gov/groups/STM/cavp/ .. _`Bruce Schneier's vectors`: https://www.schneier.com/code/vectors.txt -.. _`Camellia page`: http://info.isl.ntt.co.jp/crypt/eng/camellia/ -.. _`CRYPTREC`: http://www.cryptrec.go.jp +.. _`Camellia page`: https://info.isl.ntt.co.jp/crypt/eng/camellia/ +.. _`CRYPTREC`: https://www.cryptrec.go.jp .. _`OpenSSL's test vectors`: https://github.com/openssl/openssl/blob/97cf1f6c2854a3a955fd7dd3a1f113deba00c9ef/crypto/evp/evptests.txt#L232 .. _`RIPEMD website`: http://homes.esat.kuleuven.be/~bosselae/ripemd160.html .. _`Whirlpool website`: http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html .. _`draft RFC`: https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01 .. _`Specification repository`: https://github.com/fernet/spec -.. _`errata`: http://www.rfc-editor.org/errata_search.php?rfc=6238 +.. _`errata`: https://www.rfc-editor.org/errata_search.php?rfc=6238 .. _`OpenSSL example key`: https://github.com/openssl/openssl/blob/d02b48c63a58ea4367a0e905979f140b7d090f86/test/testrsa.pem .. _`GnuTLS key parsing tests`: https://gitlab.com/gnutls/gnutls/commit/f16ef39ef0303b02d7fa590a37820440c466ce8d .. _`enc-rsa-pkcs8.pem`: https://gitlab.com/gnutls/gnutls/blob/f8d943b38bf74eaaa11d396112daf43cb8aa82ae/tests/pkcs8-decode/encpkcs8.pem diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index a67ea47f..f88750cf 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -652,4 +652,4 @@ Key interfaces .. _`Chinese Remainder Theorem`: https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29#Using_the_Chinese_remainder_algorithm .. _`security proof`: http://eprint.iacr.org/2001/062.pdf .. _`recommended padding algorithm`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html -.. _`proven secure`: http://cseweb.ucsd.edu/~mihir/papers/oae.pdf +.. _`proven secure`: https://cseweb.ucsd.edu/~mihir/papers/oae.pdf diff --git a/docs/hazmat/primitives/mac/index.rst b/docs/hazmat/primitives/mac/index.rst index bc54bae4..05db708c 100644 --- a/docs/hazmat/primitives/mac/index.rst +++ b/docs/hazmat/primitives/mac/index.rst @@ -9,7 +9,7 @@ recommend that HMAC should be used unless you have a good reason otherwise. For more information on why HMAC is preferred, see `Use cases for CMAC vs. HMAC?`_ -.. _`Use cases for CMAC vs. HMAC?`: http://crypto.stackexchange.com/questions/15721/use-cases-for-cmac-vs-hmac +.. _`Use cases for CMAC vs. HMAC?`: https://crypto.stackexchange.com/questions/15721/use-cases-for-cmac-vs-hmac .. toctree:: :maxdepth: 1 diff --git a/docs/installation.rst b/docs/installation.rst index 2f978852..928ed4e9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -202,7 +202,7 @@ information, consult `Greg Wilson's blog post`_ on the subject. .. _`Homebrew`: http://brew.sh -.. _`MacPorts`: http://www.macports.org +.. _`MacPorts`: https://www.macports.org .. _`32-bit`: https://jenkins.cryptography.io/job/openssl-win32-release/ .. _`64-bit`: https://jenkins.cryptography.io/job/openssl-win64-release/ .. _`bug in conda`: https://github.com/conda/conda-recipes/issues/110 diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py index a4292eb8..29be22be 100644 --- a/src/cryptography/exceptions.py +++ b/src/cryptography/exceptions.py @@ -49,7 +49,9 @@ class InvalidSignature(Exception): class InternalError(Exception): - pass + def __init__(self, msg, err_code): + super(InternalError, self).__init__(msg) + self.err_code = err_code class InvalidKey(Exception): diff --git a/src/cryptography/hazmat/backends/commoncrypto/backend.py b/src/cryptography/hazmat/backends/commoncrypto/backend.py index 091fbb7c..315d67d8 100644 --- a/src/cryptography/hazmat/backends/commoncrypto/backend.py +++ b/src/cryptography/hazmat/backends/commoncrypto/backend.py @@ -227,7 +227,8 @@ class Backend(object): else: raise InternalError( "The backend returned an unknown error, consider filing a bug." - " Code: {0}.".format(response) + " Code: {0}.".format(response), + response ) def _release_cipher_ctx(self, ctx): diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index a476b1e9..ac025e95 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -15,9 +15,7 @@ import idna import six from cryptography import utils, x509 -from cryptography.exceptions import ( - InternalError, UnsupportedAlgorithm, _Reasons -) +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( CMACBackend, CipherBackend, DERSerializationBackend, DSABackend, EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, @@ -42,7 +40,7 @@ from cryptography.hazmat.backends.openssl.x509 import ( _Certificate, _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME, _DISTPOINT_TYPE_RELATIVENAME ) -from cryptography.hazmat.bindings.openssl.binding import Binding +from cryptography.hazmat.bindings.openssl import binding from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa from cryptography.hazmat.primitives.asymmetric.padding import ( @@ -58,14 +56,6 @@ from cryptography.x509.oid import ExtensionOID _MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) -_OpenSSLError = collections.namedtuple("_OpenSSLError", - ["code", "lib", "func", "reason"]) - - -class UnhandledOpenSSLError(Exception): - def __init__(self, msg, errors): - super(UnhandledOpenSSLError, self).__init__(msg) - self.errors = errors def _encode_asn1_int(backend, x): @@ -245,7 +235,7 @@ def _encode_basic_constraints(backend, basic_constraints): constraints, backend._lib.BASIC_CONSTRAINTS_free ) constraints.ca = 255 if basic_constraints.ca else 0 - if basic_constraints.ca: + if basic_constraints.ca and basic_constraints.path_length is not None: constraints.pathlen = _encode_asn1_int( backend, basic_constraints.path_length ) @@ -524,7 +514,7 @@ class Backend(object): name = "openssl" def __init__(self): - self._binding = Binding() + self._binding = binding.Binding() self._ffi = self._binding.ffi self._lib = self._binding.lib @@ -541,14 +531,7 @@ class Backend(object): self.activate_osrandom_engine() def openssl_assert(self, ok): - if not ok: - errors = self._consume_errors() - raise UnhandledOpenSSLError( - "Unknown OpenSSL error. Please file an issue at https://github" - ".com/pyca/cryptography/issues with information on how to " - "reproduce this.", - errors - ) + return binding._openssl_assert(self._lib, ok) def activate_builtin_random(self): # Obtain a new structural reference. @@ -753,32 +736,8 @@ class Backend(object): return self._ffi.buffer(buf)[:] - def _err_string(self, code): - err_buf = self._ffi.new("char[]", 256) - self._lib.ERR_error_string_n(code, err_buf, 256) - return self._ffi.string(err_buf, 256)[:] - def _consume_errors(self): - errors = [] - while True: - code = self._lib.ERR_get_error() - if code == 0: - break - - lib = self._lib.ERR_GET_LIB(code) - func = self._lib.ERR_GET_FUNC(code) - reason = self._lib.ERR_GET_REASON(code) - - errors.append(_OpenSSLError(code, lib, func, reason)) - return errors - - def _unknown_error(self, error): - return InternalError( - "Unknown error code {0} from OpenSSL, " - "you should probably file a bug. {1}.".format( - error.code, self._err_string(error.code) - ) - ) + return binding._consume_errors(self._lib) def _bn_to_int(self, bn): assert bn != self._ffi.NULL diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 4c1c7bc9..a80708aa 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -136,23 +136,21 @@ class _CipherContext(object): if not errors and isinstance(self._mode, modes.GCM): raise InvalidTag - assert errors - - if errors[0][1:] == ( - self._backend._lib.ERR_LIB_EVP, - self._backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX, - self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH - ) or errors[0][1:] == ( - self._backend._lib.ERR_LIB_EVP, - self._backend._lib.EVP_F_EVP_DECRYPTFINAL_EX, - self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH - ): - raise ValueError( - "The length of the provided data is not a multiple of " - "the block length." + self._backend.openssl_assert( + errors[0][1:] == ( + self._backend._lib.ERR_LIB_EVP, + self._backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX, + self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH + ) or errors[0][1:] == ( + self._backend._lib.ERR_LIB_EVP, + self._backend._lib.EVP_F_EVP_DECRYPTFINAL_EX, + self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH ) - else: - raise self._backend._unknown_error(errors[0]) + ) + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length." + ) if (isinstance(self._mode, modes.GCM) and self._operation == self._ENCRYPT): diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py index a442bfb7..9b4c1aff 100644 --- a/src/cryptography/hazmat/backends/openssl/dsa.py +++ b/src/cryptography/hazmat/backends/openssl/dsa.py @@ -83,7 +83,7 @@ class _DSASignatureContext(object): 0, data_to_sign, len(data_to_sign), sig_buf, buflen, self._private_key._dsa_cdata) self._backend.openssl_assert(res == 1) - assert buflen[0] + self._backend.openssl_assert(buflen[0]) return self._backend._ffi.buffer(sig_buf)[:buflen[0]] diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py index a6b65f22..02ce5f0d 100644 --- a/src/cryptography/hazmat/backends/openssl/hashes.py +++ b/src/cryptography/hazmat/backends/openssl/hashes.py @@ -56,7 +56,7 @@ class _HashContext(object): outlen = self._backend._ffi.new("unsigned int *") res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) self._backend.openssl_assert(res != 0) - assert outlen[0] == self.algorithm.digest_size + self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size) res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx) self._backend.openssl_assert(res == 1) return self._backend._ffi.buffer(buf)[:outlen[0]] diff --git a/src/cryptography/hazmat/backends/openssl/hmac.py b/src/cryptography/hazmat/backends/openssl/hmac.py index 52c691a5..dcf2fbaf 100644 --- a/src/cryptography/hazmat/backends/openssl/hmac.py +++ b/src/cryptography/hazmat/backends/openssl/hmac.py @@ -71,7 +71,7 @@ class _HMACContext(object): self._ctx, buf, outlen ) self._backend.openssl_assert(res != 0) - assert outlen[0] == self.algorithm.digest_size + self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size) self._backend._lib.HMAC_CTX_cleanup(self._ctx) return self._backend._ffi.buffer(buf)[:outlen[0]] diff --git a/src/cryptography/hazmat/bindings/commoncrypto/binding.py b/src/cryptography/hazmat/bindings/commoncrypto/binding.py index 1695c041..dfe046b5 100644 --- a/src/cryptography/hazmat/bindings/commoncrypto/binding.py +++ b/src/cryptography/hazmat/bindings/commoncrypto/binding.py @@ -13,6 +13,3 @@ class Binding(object): """ lib = lib ffi = ffi - - def __init__(self): - pass diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 50d7f6d5..47b1d6e2 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -4,14 +4,46 @@ from __future__ import absolute_import, division, print_function +import collections import os import threading import types +from cryptography.exceptions import InternalError from cryptography.hazmat.bindings._openssl import ffi, lib from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES +_OpenSSLError = collections.namedtuple("_OpenSSLError", + ["code", "lib", "func", "reason"]) + + +def _consume_errors(lib): + errors = [] + while True: + code = lib.ERR_get_error() + if code == 0: + break + + err_lib = lib.ERR_GET_LIB(code) + err_func = lib.ERR_GET_FUNC(code) + err_reason = lib.ERR_GET_REASON(code) + + errors.append(_OpenSSLError(code, err_lib, err_func, err_reason)) + return errors + + +def _openssl_assert(lib, ok): + if not ok: + errors = _consume_errors(lib) + raise InternalError( + "Unknown OpenSSL error. Please file an issue at https://github.com" + "/pyca/cryptography/issues with information on how to reproduce " + "this.", + errors + ) + + @ffi.callback("int (*)(unsigned char *, int)", error=-1) def _osrandom_rand_bytes(buf, size): signed = ffi.cast("char *", buf) @@ -64,7 +96,7 @@ class Binding(object): @classmethod def _register_osrandom_engine(cls): - assert cls.lib.ERR_peek_error() == 0 + _openssl_assert(cls.lib, cls.lib.ERR_peek_error() == 0) looked_up_engine = cls.lib.ENGINE_by_id(cls._osrandom_engine_id) if looked_up_engine != ffi.NULL: raise RuntimeError("osrandom engine already registered") @@ -72,19 +104,19 @@ class Binding(object): cls.lib.ERR_clear_error() engine = cls.lib.ENGINE_new() - assert engine != cls.ffi.NULL + _openssl_assert(cls.lib, engine != cls.ffi.NULL) try: result = cls.lib.ENGINE_set_id(engine, cls._osrandom_engine_id) - assert result == 1 + _openssl_assert(cls.lib, result == 1) result = cls.lib.ENGINE_set_name(engine, cls._osrandom_engine_name) - assert result == 1 + _openssl_assert(cls.lib, result == 1) result = cls.lib.ENGINE_set_RAND(engine, cls._osrandom_method) - assert result == 1 + _openssl_assert(cls.lib, result == 1) result = cls.lib.ENGINE_add(engine) - assert result == 1 + _openssl_assert(cls.lib, result == 1) finally: result = cls.lib.ENGINE_free(engine) - assert result == 1 + _openssl_assert(cls.lib, result == 1) @classmethod def _ensure_ffi_initialized(cls): diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index 237d5968..dac4046d 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -58,6 +58,12 @@ class InterfaceNotImplemented(Exception): pass +if hasattr(inspect, "signature"): + signature = inspect.signature +else: + signature = inspect.getargspec + + def verify_interface(iface, klass): for method in iface.__abstractmethods__: if not hasattr(klass, method): @@ -67,13 +73,13 @@ def verify_interface(iface, klass): if isinstance(getattr(iface, method), abc.abstractproperty): # Can't properly verify these yet. continue - spec = inspect.getargspec(getattr(iface, method)) - actual = inspect.getargspec(getattr(klass, method)) - if spec != actual: + sig = signature(getattr(iface, method)) + actual = signature(getattr(klass, method)) + if sig != actual: raise InterfaceNotImplemented( "{0}.{1}'s signature differs from the expected. Expected: " "{2!r}. Received: {3!r}".format( - klass, method, spec, actual + klass, method, sig, actual ) ) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 5264ba55..8fd0d711 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -17,7 +17,7 @@ from cryptography import utils from cryptography.exceptions import InternalError, _Reasons from cryptography.hazmat.backends.interfaces import RSABackend from cryptography.hazmat.backends.openssl.backend import ( - Backend, UnhandledOpenSSLError, backend + Backend, backend ) from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve from cryptography.hazmat.primitives import hashes, serialization @@ -124,7 +124,7 @@ class TestOpenSSL(object): def test_openssl_assert(self): backend.openssl_assert(True) - with pytest.raises(UnhandledOpenSSLError): + with pytest.raises(InternalError): backend.openssl_assert(False) def test_consume_errors(self): @@ -139,23 +139,6 @@ class TestOpenSSL(object): assert backend._lib.ERR_peek_error() == 0 assert len(errors) == 10 - def test_openssl_error_string(self): - backend._lib.ERR_put_error( - backend._lib.ERR_LIB_EVP, - backend._lib.EVP_F_EVP_DECRYPTFINAL_EX, - 0, - b"test_openssl.py", - -1 - ) - - errors = backend._consume_errors() - exc = backend._unknown_error(errors[0]) - - assert ( - "digital envelope routines:" - "EVP_DecryptFinal_ex:digital envelope routines" in str(exc) - ) - def test_ssl_ciphers_registered(self): meth = backend._lib.TLSv1_method() ctx = backend._lib.SSL_CTX_new(meth) diff --git a/tests/hazmat/primitives/test_X963_vectors.py b/tests/hazmat/primitives/test_x963_vectors.py index 0332e601..0332e601 100644 --- a/tests/hazmat/primitives/test_X963_vectors.py +++ b/tests/hazmat/primitives/test_x963_vectors.py diff --git a/tests/test_x509.py b/tests/test_x509.py index 220e71a5..0c022df1 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -1601,6 +1601,30 @@ class TestCertificateBuilder(object): decipher_only=False ) + @pytest.mark.requires_backend_interface(interface=RSABackend) + @pytest.mark.requires_backend_interface(interface=X509Backend) + def test_build_ca_request_with_path_length_none(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + + request = x509.CertificateSigningRequestBuilder().subject_name( + x509.Name([ + x509.NameAttribute(NameOID.ORGANIZATION_NAME, + u'PyCA'), + ]) + ).add_extension( + x509.BasicConstraints(ca=True, path_length=None), critical=True + ).sign(private_key, hashes.SHA1(), backend) + + loaded_request = x509.load_pem_x509_csr( + request.public_bytes(encoding=serialization.Encoding.PEM), backend + ) + subject = loaded_request.subject + assert isinstance(subject, x509.Name) + basic_constraints = request.extensions.get_extension_for_oid( + ExtensionOID.BASIC_CONSTRAINTS + ) + assert basic_constraints.value.path_length is None + @pytest.mark.requires_backend_interface(interface=X509Backend) class TestCertificateSigningRequestBuilder(object): |