aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py82
-rw-r--r--docs/development/test-vectors.rst11
-rw-r--r--docs/spelling_wordlist.txt1
-rw-r--r--tests/hazmat/backends/test_openssl.py48
-rw-r--r--tests/hazmat/primitives/vectors/KDF/scrypt.txt38
5 files changed, 107 insertions, 73 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 0dc6803e..f05ee3d6 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -13,6 +13,7 @@
from __future__ import absolute_import, division, print_function
+import collections
import itertools
from cryptography import utils
@@ -34,6 +35,10 @@ from cryptography.hazmat.primitives.ciphers.modes import (
)
+_OpenSSLError = collections.namedtuple("_OpenSSLError",
+ ["code", "lib", "func", "reason"])
+
+
@utils.register_interface(CipherBackend)
@utils.register_interface(HashBackend)
@utils.register_interface(HMACBackend)
@@ -228,43 +233,25 @@ class Backend(object):
self._lib.ERR_error_string_n(code, err_buf, 256)
return self._ffi.string(err_buf, 256)[:]
- def _handle_error(self, mode):
- code = self._lib.ERR_get_error()
- if not code and isinstance(mode, GCM):
- raise InvalidTag
- assert code != 0
-
- # consume any remaining errors on the stack
- ignored_code = None
- while ignored_code != 0:
- ignored_code = self._lib.ERR_get_error()
-
- # raise the first error we found
- return self._handle_error_code(code)
-
- def _handle_error_code(self, code):
- lib = self._lib.ERR_GET_LIB(code)
- func = self._lib.ERR_GET_FUNC(code)
- reason = self._lib.ERR_GET_REASON(code)
-
- if lib == self._lib.ERR_LIB_EVP:
- if func == self._lib.EVP_F_EVP_ENCRYPTFINAL_EX:
- if reason == self._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"
- )
- elif func == self._lib.EVP_F_EVP_DECRYPTFINAL_EX:
- if reason == self._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"
- )
-
- raise InternalError(
+ 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(
- code, self._err_string(code)
+ error.code, self._err_string(error.code)
)
)
@@ -464,7 +451,28 @@ class _CipherContext(object):
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
if res == 0:
- self._backend._handle_error(self._mode)
+ errors = self._backend._consume_errors()
+
+ if not errors and isinstance(self._mode, 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."
+ )
+ else:
+ raise self._backend._unknown_error(errors[0])
if (isinstance(self._mode, GCM) and
self._operation == self._ENCRYPT):
@@ -776,6 +784,7 @@ class _RSAVerificationContext(object):
# occurs.
assert res >= 0
if res == 0:
+ assert self._backend._consume_errors()
raise InvalidSignature
def _verify_pkcs1(self, rsa_cdata, evp_pkey, evp_md):
@@ -792,6 +801,7 @@ class _RSAVerificationContext(object):
# occurs.
assert res >= 0
if res == 0:
+ assert self._backend._consume_errors()
raise InvalidSignature
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index c96b6d89..8b27e9d9 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -15,10 +15,12 @@ Asymmetric Ciphers
* RSA PKCS #1 from the RSA FTP site (ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/
and ftp://ftp.rsa.com/pub/rsalabs/tmp/).
-* OpenSSL PEM serialization vectors from the `OpenSSL test suite`_ and `GnuTLS test suite`_.
+* OpenSSL PEM serialization vectors from the `OpenSSL test suite`_ and `GnuTLS
+ test suite`_.
* PKCS #8 PEM serialization vectors from
- * GnuTLS: `encpkcs8.pem`_, `enc2pkcs8.pem`_, `unencpkcs8.pem`_, `pkcs12_s2k_pem.c`_.
+ * GnuTLS: `encpkcs8.pem`_, `enc2pkcs8.pem`_, `unencpkcs8.pem`_,
+ `pkcs12_s2k_pem.c`_.
* `Botan's ECC private keys`_.
Hashes
@@ -43,6 +45,7 @@ Key Derivation Functions
* HKDF (SHA1, SHA256) from :rfc:`5869`.
* PBKDF2 (HMAC-SHA1) from :rfc:`6070`.
+* scrypt from the `draft RFC`_.
Recipes
~~~~~~~
@@ -67,7 +70,8 @@ Two Factor Authentication
~~~~~~~~~~~~~~~~~~~~~~~~~
* HOTP from :rfc:`4226`
-* TOTP from :rfc:`6238` (Note that an `errata`_ for the test vectors in RFC 6238 exists)
+* TOTP from :rfc:`6238` (Note that an `errata`_ for the test vectors in RFC
+ 6238 exists)
Creating Test Vectors
@@ -103,6 +107,7 @@ header format (substituting the correct information):
.. _`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
.. _`OpenSSL test suite`: http://git.openssl.org/gitweb/?p=openssl.git;a=blob;f=test/testrsa.pem;h=aad21067a8f7cb93a52a511eb9162fd83be39135;hb=66e8211c0b1347970096e04b18aa52567c325200
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 7200855d..bf5ae05e 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -28,6 +28,7 @@ pickleable
plaintext
pseudorandom
Schneier
+scrypt
testability
unencrypted
unpadded
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index b24808df..42c1b395 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -71,46 +71,17 @@ class TestOpenSSL(object):
with pytest.raises(UnsupportedAlgorithm):
cipher.encryptor()
- def test_handle_unknown_error(self):
- with pytest.raises(InternalError):
- backend._handle_error_code(0)
-
- backend._lib.ERR_put_error(backend._lib.ERR_LIB_EVP, 0, 0,
- b"test_openssl.py", -1)
- with pytest.raises(InternalError):
- backend._handle_error(None)
-
- backend._lib.ERR_put_error(
- backend._lib.ERR_LIB_EVP,
- backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX,
- 0,
- b"test_openssl.py",
- -1
- )
- with pytest.raises(InternalError):
- backend._handle_error(None)
-
- backend._lib.ERR_put_error(
- backend._lib.ERR_LIB_EVP,
- backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
- 0,
- b"test_openssl.py",
- -1
- )
- with pytest.raises(InternalError):
- backend._handle_error(None)
-
- def test_handle_multiple_errors(self):
+ def test_consume_errors(self):
for i in range(10):
backend._lib.ERR_put_error(backend._lib.ERR_LIB_EVP, 0, 0,
b"test_openssl.py", -1)
assert backend._lib.ERR_peek_error() != 0
- with pytest.raises(InternalError):
- backend._handle_error(None)
+ errors = backend._consume_errors()
assert backend._lib.ERR_peek_error() == 0
+ assert len(errors) == 10
def test_openssl_error_string(self):
backend._lib.ERR_put_error(
@@ -121,8 +92,8 @@ class TestOpenSSL(object):
-1
)
- with pytest.raises(InternalError) as exc:
- backend._handle_error(None)
+ errors = backend._consume_errors()
+ exc = backend._unknown_error(errors[0])
assert (
"digital envelope routines:"
@@ -147,6 +118,15 @@ class TestOpenSSL(object):
b"data not multiple of block length"
)
+ def test_unknown_error_in_cipher_finalize(self):
+ cipher = Cipher(AES(b"\0" * 16), CBC(b"\0" * 16), backend=backend)
+ enc = cipher.encryptor()
+ enc.update(b"\0")
+ backend._lib.ERR_put_error(0, 0, 1,
+ b"test_openssl.py", -1)
+ with pytest.raises(InternalError):
+ enc.finalize()
+
def test_derive_pbkdf2_raises_unsupported_on_old_openssl(self):
if backend.pbkdf2_hmac_supported(hashes.SHA256()):
pytest.skip("Requires an older OpenSSL")
diff --git a/tests/hazmat/primitives/vectors/KDF/scrypt.txt b/tests/hazmat/primitives/vectors/KDF/scrypt.txt
new file mode 100644
index 00000000..ad0dfc83
--- /dev/null
+++ b/tests/hazmat/primitives/vectors/KDF/scrypt.txt
@@ -0,0 +1,38 @@
+COUNT = 0
+PASSWORD =
+SALT =
+N = 16
+r = 1
+p = 1
+LENGTH = 64
+DERIVED_KEY = 77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906
+
+
+COUNT = 1
+PASSWORD = password
+SALT = NaCl
+N = 1024
+r = 8
+p = 16
+LENGTH = 64
+DERIVED_KEY = fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640
+
+
+COUNT = 2
+PASSWORD = pleaseletmein
+SALT = SodiumChloride
+N = 16384
+r = 8
+p = 1
+LENGTH = 64
+DERIVED_KEY = 7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887
+
+
+COUNT = 3
+PASSWORD = pleaseletmein
+SALT = SodiumChloride
+N = 1048576
+r = 8
+p = 1
+LENGTH = 64
+DERIVED_KEY = 2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4