aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2017-05-20 14:53:33 -0700
committerPaul Kehrer <paul.l.kehrer@gmail.com>2017-05-20 14:53:33 -0700
commit453d2ac482bf4c84207cb2ac7974f6136f27ce5c (patch)
tree29be901862371f31f27df8b66770a9af65e17b17
parentc7dd9de42ff93d94757a339fe3bf39dc42cd86f9 (diff)
downloadcryptography-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.py55
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/__init__.py5
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/base.py17
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/modes.py3
-rw-r--r--tests/hazmat/primitives/test_ciphers.py24
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(