diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2017-05-20 14:53:33 -0700 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2017-05-20 14:53:33 -0700 |
commit | 453d2ac482bf4c84207cb2ac7974f6136f27ce5c (patch) | |
tree | 29be901862371f31f27df8b66770a9af65e17b17 | |
parent | c7dd9de42ff93d94757a339fe3bf39dc42cd86f9 (diff) | |
download | cryptography-453d2ac482bf4c84207cb2ac7974f6136f27ce5c.tar.gz cryptography-453d2ac482bf4c84207cb2ac7974f6136f27ce5c.tar.bz2 cryptography-453d2ac482bf4c84207cb2ac7974f6136f27ce5c.zip |
Fixed #3533 -- made GCM mode object immutable (#3553)
* Fixed #3533 -- made GCM mode object immutable
* flake8
* Fix for older openssl
* fix
* fix
* sigh, fix
* fixed
* dropped negation
* computers are bad
* A test
* This implements an interface
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/ciphers.py | 55 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/ciphers/__init__.py | 5 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/ciphers/base.py | 17 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/ciphers/modes.py | 3 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_ciphers.py | 24 |
5 files changed, 64 insertions, 40 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 739ae19a..cfd6c1b5 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -13,6 +13,7 @@ from cryptography.hazmat.primitives.ciphers import modes @utils.register_interface(ciphers.CipherContext) @utils.register_interface(ciphers.AEADCipherContext) @utils.register_interface(ciphers.AEADEncryptionContext) +@utils.register_interface(ciphers.AEADDecryptionContext) class _CipherContext(object): _ENCRYPT = 1 _DECRYPT = 0 @@ -78,21 +79,22 @@ class _CipherContext(object): len(iv_nonce), self._backend._ffi.NULL ) self._backend.openssl_assert(res != 0) - if ( - self._operation == self._DECRYPT and - self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and - not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - ): - if mode.tag is None: - raise NotImplementedError( - "delayed passing of GCM tag requires OpenSSL >= 1.0.2." - " To use this feature please update OpenSSL" - ) + if mode.tag is not None: res = self._backend._lib.EVP_CIPHER_CTX_ctrl( ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG, len(mode.tag), mode.tag ) self._backend.openssl_assert(res != 0) + self._tag = mode.tag + elif ( + self._operation == self._DECRYPT and + self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and + not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL + ): + raise NotImplementedError( + "delayed passing of GCM tag requires OpenSSL >= 1.0.2." + " To use this feature please update OpenSSL" + ) # pass key/iv res = self._backend._lib.EVP_CipherInit_ex( @@ -146,21 +148,11 @@ class _CipherContext(object): if ( self._operation == self._DECRYPT and isinstance(self._mode, modes.ModeWithAuthenticationTag) and - ( - not self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 or - self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - ) + self.tag is None ): - tag = self._mode.tag - if tag is None: - raise ValueError( - "Authentication tag must be provided when decrypting." - ) - res = self._backend._lib.EVP_CIPHER_CTX_ctrl( - self._ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG, - len(tag), tag + raise ValueError( + "Authentication tag must be provided when decrypting." ) - self._backend.openssl_assert(res != 0) buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes) outlen = self._backend._ffi.new("int *") @@ -203,6 +195,23 @@ class _CipherContext(object): self._backend.openssl_assert(res == 1) return self._backend._ffi.buffer(buf)[:outlen[0]] + def finalize_with_tag(self, tag): + if ( + self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and + not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL + ): + raise NotImplementedError( + "finalize_with_tag requires OpenSSL >= 1.0.2. To use this " + "method please update OpenSSL" + ) + res = self._backend._lib.EVP_CIPHER_CTX_ctrl( + self._ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG, + len(tag), tag + ) + self._backend.openssl_assert(res != 0) + self._tag = tag + return self.finalize() + def authenticate_additional_data(self, data): outlen = self._backend._ffi.new("int *") res = self._backend._lib.EVP_CipherUpdate( diff --git a/src/cryptography/hazmat/primitives/ciphers/__init__.py b/src/cryptography/hazmat/primitives/ciphers/__init__.py index b5dd0ed7..171b1c69 100644 --- a/src/cryptography/hazmat/primitives/ciphers/__init__.py +++ b/src/cryptography/hazmat/primitives/ciphers/__init__.py @@ -5,8 +5,8 @@ from __future__ import absolute_import, division, print_function from cryptography.hazmat.primitives.ciphers.base import ( - AEADCipherContext, AEADEncryptionContext, BlockCipherAlgorithm, Cipher, - CipherAlgorithm, CipherContext + AEADCipherContext, AEADDecryptionContext, AEADEncryptionContext, + BlockCipherAlgorithm, Cipher, CipherAlgorithm, CipherContext ) @@ -16,5 +16,6 @@ __all__ = [ "BlockCipherAlgorithm", "CipherContext", "AEADCipherContext", + "AEADDecryptionContext", "AEADEncryptionContext", ] diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py index dd024fb9..826e23ef 100644 --- a/src/cryptography/hazmat/primitives/ciphers/base.py +++ b/src/cryptography/hazmat/primitives/ciphers/base.py @@ -221,17 +221,12 @@ class _AEADCipherContext(object): return data def finalize_with_tag(self, tag): - if ( - self._ctx._backend.name == "openssl" and - self._ctx._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and - not self._ctx._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - ): - raise NotImplementedError( - "finalize_with_tag requires OpenSSL >= 1.0.2. To use this " - "method please update OpenSSL" - ) - self._ctx._mode._set_tag(tag) - return self.finalize() + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + data = self._ctx.finalize_with_tag(tag) + self._tag = self._ctx.tag + self._ctx = None + return data def authenticate_additional_data(self, data): if self._ctx is None: diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py index 5b28157a..54670b7f 100644 --- a/src/cryptography/hazmat/primitives/ciphers/modes.py +++ b/src/cryptography/hazmat/primitives/ciphers/modes.py @@ -164,9 +164,6 @@ class GCM(object): if not isinstance(initialization_vector, bytes): raise TypeError("initialization_vector must be bytes") self._initialization_vector = initialization_vector - self._set_tag(tag, min_tag_length) - - def _set_tag(self, tag, min_tag_length=16): if tag is not None: if not isinstance(tag, bytes): raise TypeError("tag must be bytes or None") diff --git a/tests/hazmat/primitives/test_ciphers.py b/tests/hazmat/primitives/test_ciphers.py index 7f51576a..60faa0c5 100644 --- a/tests/hazmat/primitives/test_ciphers.py +++ b/tests/hazmat/primitives/test_ciphers.py @@ -11,7 +11,7 @@ import cffi import pytest -from cryptography.exceptions import _Reasons +from cryptography.exceptions import AlreadyFinalized, _Reasons from cryptography.hazmat.backends.interfaces import CipherBackend from cryptography.hazmat.primitives import ciphers from cryptography.hazmat.primitives.ciphers import modes @@ -196,6 +196,28 @@ class TestCipherUpdateInto(object): assert res == len(pt) assert bytes(buf)[:res] == pt + @pytest.mark.supported( + only_if=lambda backend: backend.cipher_supported( + AES(b"\x00" * 16), modes.GCM(b"0" * 12) + ), + skip_message="Does not support AES GCM", + ) + def test_finalize_with_tag_already_finalized(self, backend): + key = binascii.unhexlify(b"e98b72a9881a84ca6b76e0f43e68647a") + iv = binascii.unhexlify(b"8b23299fde174053f3d652ba") + encryptor = ciphers.Cipher( + AES(key), modes.GCM(iv), backend + ).encryptor() + ciphertext = encryptor.update(b"abc") + encryptor.finalize() + + decryptor = ciphers.Cipher( + AES(key), modes.GCM(iv, tag=encryptor.tag), backend + ).decryptor() + decryptor.update(ciphertext) + decryptor.finalize() + with pytest.raises(AlreadyFinalized): + decryptor.finalize_with_tag(encryptor.tag) + @pytest.mark.parametrize( "params", load_vectors_from_file( |