From 0ab3e44b0512a6cbab845a65759a21787bf15d27 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 12:52:18 -0800 Subject: Raise a correct error when content isn't padded correctly --- cryptography/exceptions.py | 4 ++++ cryptography/hazmat/bindings/openssl/backend.py | 22 ++++++++++++++++++++-- cryptography/hazmat/bindings/openssl/err.py | 7 +++++++ tests/hazmat/primitives/test_block.py | 20 +++++++++++++++++++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py index c2e71493..a39674f9 100644 --- a/cryptography/exceptions.py +++ b/cryptography/exceptions.py @@ -18,3 +18,7 @@ class UnsupportedAlgorithm(Exception): class AlreadyFinalized(Exception): pass + + +class IncorrectPadding(Exception): + pass diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index db4d18e7..0071da5d 100644 --- a/cryptography/hazmat/bindings/openssl/backend.py +++ b/cryptography/hazmat/bindings/openssl/backend.py @@ -19,7 +19,7 @@ import sys import cffi from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm +from cryptography.exceptions import UnsupportedAlgorithm, IncorrectPadding from cryptography.hazmat.bindings.interfaces import ( CipherBackend, HashBackend, HMACBackend ) @@ -193,6 +193,22 @@ class Backend(object): def create_symmetric_decryption_ctx(self, cipher, mode): return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + def _handle_error(self): + code = self.lib.ERR_get_error() + assert code != 0 + 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 IncorrectPadding + elif func == self.lib.EVP_F_EVP_DECRYPTFINAL_EX: + if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: + raise IncorrectPadding + assert False + class GetCipherByName(object): def __init__(self, fmt): @@ -268,7 +284,9 @@ class _CipherContext(object): buf = self._backend.ffi.new("unsigned char[]", self._cipher.block_size) outlen = self._backend.ffi.new("int *") res = self._backend.lib.EVP_CipherFinal_ex(self._ctx, buf, outlen) - assert res != 0 + if res == 0: + self._backend._handle_error() + res = self._backend.lib.EVP_CIPHER_CTX_cleanup(self._ctx) assert res == 1 return self._backend.ffi.buffer(buf)[:outlen[0]] diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py index 6a36dee0..3dac6948 100644 --- a/cryptography/hazmat/bindings/openssl/err.py +++ b/cryptography/hazmat/bindings/openssl/err.py @@ -21,6 +21,13 @@ struct ERR_string_data_st { const char *string; }; typedef struct ERR_string_data_st ERR_STRING_DATA; + +static const int ERR_LIB_EVP; + +static const int EVP_F_EVP_ENCRYPTFINAL_EX; +static const int EVP_F_EVP_DECRYPTFINAL_EX; + +static const int EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH; """ FUNCTIONS = """ diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py index 9460c53d..4c756203 100644 --- a/tests/hazmat/primitives/test_block.py +++ b/tests/hazmat/primitives/test_block.py @@ -18,7 +18,9 @@ import binascii import pytest from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, AlreadyFinalized +from cryptography.exceptions import ( + UnsupportedAlgorithm, AlreadyFinalized, IncorrectPadding +) from cryptography.hazmat.primitives import interfaces from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes @@ -108,3 +110,19 @@ class TestCipherContext(object): with pytest.raises(UnsupportedAlgorithm): cipher.decryptor() + + def test_incorrectly_padded(self, backend): + cipher = Cipher( + algorithms.AES(b"\x00" * 16), + modes.CBC(b"\x00" * 16), + backend + ) + encryptor = cipher.encryptor() + encryptor.update(b"1") + with pytest.raises(IncorrectPadding): + encryptor.finalize() + + decryptor = cipher.decryptor() + decryptor.update(b"1") + with pytest.raises(IncorrectPadding): + decryptor.finalize() -- cgit v1.2.3 From 797dd83d81915d5bab8791e513fcb26051870eb7 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 13:08:58 -0800 Subject: Documentation! --- docs/hazmat/primitives/symmetric-encryption.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index eef359d6..35b0d9a8 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -75,6 +75,15 @@ an "encrypt-then-MAC" formulation as `described by Colin Percival`_. everything into the context. Once that is done call ``finalize()`` to finish the operation and obtain the remainder of the data. + Block ciphers require that plaintext or ciphertext always be a multiple of + their block size, because of that **padding** is often required to make a + message the correct size. ``CipherContext`` will not automatically apply + any padding; you'll need to add your own. For block ciphers the reccomended + padding is :class:`cryptography.hazmat.primitives.padding.PKCS7`. If you + are using a stream cipher mode (such as + :class:`cryptography.hazmat.primitives.modes.CTR`) you don't have to worry + about this. + .. method:: update(data) :param bytes data: The data you wish to pass into the context. @@ -90,6 +99,13 @@ an "encrypt-then-MAC" formulation as `described by Colin Percival`_. .. method:: finalize() :return bytes: Returns the remainder of the data. + :raises cryptography.exceptions.IncorrectPadding: This is raised when + the data provided + isn't correctly + padded to be a + multiple of the + algorithm's block + size. Once ``finalize`` is called this object can no longer be used and :meth:`update` and :meth:`finalize` will raise -- cgit v1.2.3 From f1569b6abbba6920ca343c62721098e0ce8c7f9c Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 13:09:38 -0800 Subject: One more documentation --- docs/exceptions.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/exceptions.rst b/docs/exceptions.rst index ab1b28fe..b0435b0c 100644 --- a/docs/exceptions.rst +++ b/docs/exceptions.rst @@ -13,3 +13,7 @@ Exceptions This is raised when a backend doesn't support the requested algorithm (or combination of algorithms). + +.. class:: IncorrectPadding + + This is raised when a block cipher's content isn't correctly padded. -- cgit v1.2.3 From d203710bba3f2eb15a2e2db38a20305540af2eea Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 13:12:02 -0800 Subject: Better, but still not covered --- cryptography/hazmat/bindings/openssl/backend.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index 0071da5d..99f11f45 100644 --- a/cryptography/hazmat/bindings/openssl/backend.py +++ b/cryptography/hazmat/bindings/openssl/backend.py @@ -207,7 +207,9 @@ class Backend(object): elif func == self.lib.EVP_F_EVP_DECRYPTFINAL_EX: if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: raise IncorrectPadding - assert False + raise SystemError( + "Unknown error code from OpenSSL, you should probably file a bug" + ) class GetCipherByName(object): -- cgit v1.2.3 From 3edffe25ab91702842a7553b028d50086c58eef1 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 14:31:52 -0800 Subject: include the error message --- cryptography/hazmat/bindings/openssl/backend.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index 99f11f45..ae951717 100644 --- a/cryptography/hazmat/bindings/openssl/backend.py +++ b/cryptography/hazmat/bindings/openssl/backend.py @@ -207,8 +207,11 @@ class Backend(object): elif func == self.lib.EVP_F_EVP_DECRYPTFINAL_EX: if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: raise IncorrectPadding + + message = self.ffi.string(self.lib.ERR_reason_error_string(code)) raise SystemError( - "Unknown error code from OpenSSL, you should probably file a bug" + "Unknown error code from OpenSSL, you should probably file a bug. " + "Cause: %s" % message ) -- cgit v1.2.3 From e247d0be584c52e25694918ad2345dadde3fefa9 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 16:24:06 -0800 Subject: Coverage --- cryptography/hazmat/bindings/openssl/backend.py | 6 +++--- tests/hazmat/bindings/test_openssl.py | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index ae951717..77ecf277 100644 --- a/cryptography/hazmat/bindings/openssl/backend.py +++ b/cryptography/hazmat/bindings/openssl/backend.py @@ -199,7 +199,9 @@ class Backend(object): lib = self.lib.ERR_GET_LIB(code) func = self.lib.ERR_GET_FUNC(code) reason = self.lib.ERR_GET_REASON(code) + return self._handle_error_code(lib, func, reason) + def _handle_error_code(self, lib, func, reason): 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: @@ -208,10 +210,8 @@ class Backend(object): if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: raise IncorrectPadding - message = self.ffi.string(self.lib.ERR_reason_error_string(code)) raise SystemError( - "Unknown error code from OpenSSL, you should probably file a bug. " - "Cause: %s" % message + "Unknown error code from OpenSSL, you should probably file a bug." ) diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index 9f27aab7..7ad1ea75 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -70,3 +70,7 @@ class TestOpenSSL(object): ) with pytest.raises(UnsupportedAlgorithm): cipher.encryptor() + + def test_handle_unknown_error(self): + with pytest.raises(SystemError): + backend._handle_error_code(0, 0, 0) -- cgit v1.2.3 From bae899ad36bcb99dbec94aaf026ef1650f2b1242 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 16:54:55 -0800 Subject: Change teh exception --- cryptography/exceptions.py | 4 ---- cryptography/hazmat/bindings/openssl/backend.py | 6 +++--- docs/exceptions.rst | 5 ----- docs/hazmat/primitives/padding.rst | 8 +++++++- docs/hazmat/primitives/symmetric-encryption.rst | 10 +++------- tests/hazmat/primitives/test_block.py | 8 +++----- 6 files changed, 16 insertions(+), 25 deletions(-) diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py index a39674f9..c2e71493 100644 --- a/cryptography/exceptions.py +++ b/cryptography/exceptions.py @@ -18,7 +18,3 @@ class UnsupportedAlgorithm(Exception): class AlreadyFinalized(Exception): pass - - -class IncorrectPadding(Exception): - pass diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index 77ecf277..b2599d22 100644 --- a/cryptography/hazmat/bindings/openssl/backend.py +++ b/cryptography/hazmat/bindings/openssl/backend.py @@ -19,7 +19,7 @@ import sys import cffi from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, IncorrectPadding +from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.bindings.interfaces import ( CipherBackend, HashBackend, HMACBackend ) @@ -205,10 +205,10 @@ class Backend(object): 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 IncorrectPadding + raise ValueError elif func == self.lib.EVP_F_EVP_DECRYPTFINAL_EX: if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: - raise IncorrectPadding + raise ValueError raise SystemError( "Unknown error code from OpenSSL, you should probably file a bug." diff --git a/docs/exceptions.rst b/docs/exceptions.rst index b0435b0c..c6f5a7cc 100644 --- a/docs/exceptions.rst +++ b/docs/exceptions.rst @@ -12,8 +12,3 @@ Exceptions This is raised when a backend doesn't support the requested algorithm (or combination of algorithms). - - -.. class:: IncorrectPadding - - This is raised when a block cipher's content isn't correctly padded. diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst index aebb4d4d..4d79ac8f 100644 --- a/docs/hazmat/primitives/padding.rst +++ b/docs/hazmat/primitives/padding.rst @@ -25,8 +25,14 @@ multiple of the block size. >>> padder = padding.PKCS7(128).padder() >>> padder.update(b"1111111111") '' - >>> padder.finalize() + >>> padded_data = padder.finalize() + >>> padded_data '1111111111\x06\x06\x06\x06\x06\x06' + >>> unpadder = padding.PKCS7(128).unpadder() + >>> unpadder.update(padded_data) + '' + >>> unpadder.finalize() + '1111111111' :param block_size: The size of the block in bits that the data is being padded to. diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 35b0d9a8..732af33c 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -99,13 +99,9 @@ an "encrypt-then-MAC" formulation as `described by Colin Percival`_. .. method:: finalize() :return bytes: Returns the remainder of the data. - :raises cryptography.exceptions.IncorrectPadding: This is raised when - the data provided - isn't correctly - padded to be a - multiple of the - algorithm's block - size. + :raises ValueError: This is raised when the data provided isn't + correctly padded to be a multiple of the + algorithm's block size. Once ``finalize`` is called this object can no longer be used and :meth:`update` and :meth:`finalize` will raise diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py index 4c756203..ea40127e 100644 --- a/tests/hazmat/primitives/test_block.py +++ b/tests/hazmat/primitives/test_block.py @@ -18,9 +18,7 @@ import binascii import pytest from cryptography import utils -from cryptography.exceptions import ( - UnsupportedAlgorithm, AlreadyFinalized, IncorrectPadding -) +from cryptography.exceptions import UnsupportedAlgorithm, AlreadyFinalized from cryptography.hazmat.primitives import interfaces from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes @@ -119,10 +117,10 @@ class TestCipherContext(object): ) encryptor = cipher.encryptor() encryptor.update(b"1") - with pytest.raises(IncorrectPadding): + with pytest.raises(ValueError): encryptor.finalize() decryptor = cipher.decryptor() decryptor.update(b"1") - with pytest.raises(IncorrectPadding): + with pytest.raises(ValueError): decryptor.finalize() -- cgit v1.2.3 From b7e8990aabb46ac6c0511530d7a67f69e08f1788 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Nov 2013 16:58:34 -0800 Subject: Useful error message --- cryptography/hazmat/bindings/openssl/backend.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index b2599d22..9f8ea939 100644 --- a/cryptography/hazmat/bindings/openssl/backend.py +++ b/cryptography/hazmat/bindings/openssl/backend.py @@ -205,10 +205,16 @@ class Backend(object): 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 + 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 + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length" + ) raise SystemError( "Unknown error code from OpenSSL, you should probably file a bug." -- cgit v1.2.3