fix libressl error/refactor some error handling (#3609)
* add libre so I can see the error
* add the libre error needed and refactor error handling a bit
We were historically matching on lib + func + reason, but func is
somewhat unstable so now we match on lib + reason only. Of course, in
this case libressl changed both lib and reason so it wouldn't
have mattered. All error handling from the error queue in
openssl is an illusion
* fix a typo, probably an unneeded branch
* review feedback
* refactor tests to support libressl
insert additional rant about libre here, although admittedly these tests
were assuming stability where openssl itself guarantees none
* better assert, fix flake8
diff --git a/Jenkinsfile b/Jenkinsfile
index 6cdf162..a45d0b4 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -55,6 +55,11 @@
],
[
label: 'docker',
+ imageName: 'pyca/cryptography-runner-jessie-libressl:2.5.4',
+ toxenvs: ['py27'],
+ ],
+ [
+ label: 'docker',
imageName: 'pyca/cryptography-runner-ubuntu-xenial',
toxenvs: ['py27', 'py35'],
],
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index a259d66..9900d05 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -710,10 +710,13 @@
)
if res == 0:
errors = self._consume_errors()
- self.openssl_assert(errors[0][1] == self._lib.ERR_LIB_RSA)
self.openssl_assert(
- errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_RSA,
+ self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ )
)
+
raise ValueError("Digest too big for RSA key")
return _CertificateSigningRequest(self, x509_req)
@@ -792,9 +795,11 @@
)
if res == 0:
errors = self._consume_errors()
- self.openssl_assert(errors[0][1] == self._lib.ERR_LIB_RSA)
self.openssl_assert(
- errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_RSA,
+ self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ )
)
raise ValueError("Digest too big for RSA key")
@@ -802,9 +807,11 @@
def _raise_time_set_error(self):
errors = self._consume_errors()
- self.openssl_assert(errors[0][1] == self._lib.ERR_LIB_ASN1)
self.openssl_assert(
- errors[0][3] == self._lib.ASN1_R_ERROR_GETTING_TIME
+ errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_ASN1,
+ self._lib.ASN1_R_ERROR_GETTING_TIME
+ )
)
raise ValueError(
"Invalid time. This error can occur if you set a time too far in "
@@ -879,9 +886,11 @@
)
if res == 0:
errors = self._consume_errors()
- self.openssl_assert(errors[0][1] == self._lib.ERR_LIB_RSA)
self.openssl_assert(
- errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_RSA,
+ self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ )
)
raise ValueError("Digest too big for RSA key")
@@ -1173,31 +1182,21 @@
if not errors:
raise ValueError("Could not deserialize key data.")
- elif errors[0][1:] in (
- (
- self._lib.ERR_LIB_EVP,
- self._lib.EVP_F_EVP_DECRYPTFINAL_EX,
- self._lib.EVP_R_BAD_DECRYPT
- ),
- (
+ elif (
+ errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
+ ) or errors[0]._lib_reason_match(
self._lib.ERR_LIB_PKCS12,
- self._lib.PKCS12_F_PKCS12_PBE_CRYPT,
- self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
+ self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR
)
):
raise ValueError("Bad decrypt. Incorrect password?")
- elif errors[0][1:] in (
- (
- self._lib.ERR_LIB_PEM,
- self._lib.PEM_F_PEM_GET_EVP_CIPHER_INFO,
- self._lib.PEM_R_UNSUPPORTED_ENCRYPTION
- ),
-
- (
- self._lib.ERR_LIB_EVP,
- self._lib.EVP_F_EVP_PBE_CIPHERINIT,
- self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM
+ elif (
+ errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM
+ ) or errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION
)
):
raise UnsupportedAlgorithm(
@@ -1206,9 +1205,8 @@
)
elif any(
- error[1:] == (
+ error._lib_reason_match(
self._lib.ERR_LIB_EVP,
- self._lib.EVP_F_EVP_PKCS82PKEY,
self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM
)
for error in errors
@@ -1216,7 +1214,7 @@
raise ValueError("Unsupported public key algorithm.")
else:
- assert errors[0][1] in (
+ assert errors[0].lib in (
self._lib.ERR_LIB_EVP,
self._lib.ERR_LIB_PEM,
self._lib.ERR_LIB_ASN1,
@@ -1235,9 +1233,8 @@
errors = self._consume_errors()
self.openssl_assert(
curve_nid == self._lib.NID_undef or
- errors[0][1:] == (
+ errors[0]._lib_reason_match(
self._lib.ERR_LIB_EC,
- self._lib.EC_F_EC_GROUP_NEW_BY_CURVE_NAME,
self._lib.EC_R_UNKNOWN_GROUP
)
)
diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py
index 13c9fa5..4ca2fee 100644
--- a/src/cryptography/hazmat/backends/openssl/ciphers.py
+++ b/src/cryptography/hazmat/backends/openssl/ciphers.py
@@ -160,13 +160,11 @@
raise InvalidTag
self._backend.openssl_assert(
- errors[0][1:] == (
+ errors[0]._lib_reason_match(
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:] == (
+ ) or errors[0]._lib_reason_match(
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
)
)
diff --git a/src/cryptography/hazmat/backends/openssl/dh.py b/src/cryptography/hazmat/backends/openssl/dh.py
index 88c876f..456e9be 100644
--- a/src/cryptography/hazmat/backends/openssl/dh.py
+++ b/src/cryptography/hazmat/backends/openssl/dh.py
@@ -63,11 +63,11 @@
def _handle_dh_compute_key_error(errors, backend):
lib = backend._lib
- backend.openssl_assert(errors[0][1:] == (
- lib.ERR_LIB_DH,
- lib.DH_F_COMPUTE_KEY,
- lib.DH_R_INVALID_PUBKEY
- ))
+ backend.openssl_assert(
+ errors[0]._lib_reason_match(
+ lib.ERR_LIB_DH, lib.DH_R_INVALID_PUBKEY
+ )
+ )
raise ValueError("Public key value is invalid for this exchange.")
diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py
index 6b3d50c..d00fc79 100644
--- a/src/cryptography/hazmat/bindings/openssl/binding.py
+++ b/src/cryptography/hazmat/bindings/openssl/binding.py
@@ -8,17 +8,32 @@
import threading
import types
+from cryptography import utils
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"])
_OpenSSLErrorWithText = collections.namedtuple(
"_OpenSSLErrorWithText", ["code", "lib", "func", "reason", "reason_text"]
)
+class _OpenSSLError(object):
+ def __init__(self, code, lib, func, reason):
+ self._code = code
+ self._lib = lib
+ self._func = func
+ self._reason = reason
+
+ def _lib_reason_match(self, lib, reason):
+ return lib == self.lib and reason == self.reason
+
+ code = utils.read_only_property("_code")
+ lib = utils.read_only_property("_lib")
+ func = utils.read_only_property("_func")
+ reason = utils.read_only_property("_reason")
+
+
def _consume_errors(lib):
errors = []
while True:
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 20c073a..e857ff6 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -127,10 +127,7 @@
def test_error_strings_loaded(self):
# returns a value in a static buffer
err = backend._lib.ERR_error_string(101183626, backend._ffi.NULL)
- assert backend._ffi.string(err) == (
- b"error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:"
- b"data not multiple of block length"
- )
+ assert b"data not multiple of block length" in backend._ffi.string(err)
def test_unknown_error_in_cipher_finalize(self):
cipher = Cipher(AES(b"\0" * 16), CBC(b"\0" * 16), backend=backend)
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index 9b0da67..488f64e 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -8,7 +8,7 @@
from cryptography.exceptions import InternalError
from cryptography.hazmat.bindings.openssl.binding import (
- Binding, _OpenSSLErrorWithText, _consume_errors, _openssl_assert
+ Binding, _consume_errors, _openssl_assert
)
@@ -94,16 +94,12 @@
with pytest.raises(InternalError) as exc_info:
_openssl_assert(b.lib, False)
- assert exc_info.value.err_code == [_OpenSSLErrorWithText(
- code=101183626,
- lib=b.lib.ERR_LIB_EVP,
- func=b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
- reason=b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
- reason_text=(
- b'error:0607F08A:digital envelope routines:EVP_EncryptFinal_'
- b'ex:data not multiple of block length'
- )
- )]
+ error = exc_info.value.err_code[0]
+ assert error.code == 101183626
+ assert error.lib == b.lib.ERR_LIB_EVP
+ assert error.func == b.lib.EVP_F_EVP_ENCRYPTFINAL_EX
+ assert error.reason == b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
+ assert b"data not multiple of block length" in error.reason_text
def test_check_startup_errors_are_allowed(self):
b = Binding()