diff options
Diffstat (limited to 'src')
4 files changed, 64 insertions, 23 deletions
diff --git a/src/cryptography/hazmat/backends/commoncrypto/ciphers.py b/src/cryptography/hazmat/backends/commoncrypto/ciphers.py index b59381cb..85ec9e76 100644 --- a/src/cryptography/hazmat/backends/commoncrypto/ciphers.py +++ b/src/cryptography/hazmat/backends/commoncrypto/ciphers.py @@ -213,11 +213,15 @@ class _GCMCipherContext(object): self._backend._check_cipher_response(res) self._backend._release_cipher_ctx(self._ctx) self._tag = self._backend._ffi.buffer(tag_buf)[:] - if (self._operation == self._backend._lib.kCCDecrypt and - not constant_time.bytes_eq( - self._tag[:len(self._mode.tag)], self._mode.tag - )): - raise InvalidTag + if self._operation == self._backend._lib.kCCDecrypt: + if self._mode.tag is None: + raise ValueError( + "Authentication tag must be provided when decrypting." + ) + if not constant_time.bytes_eq( + self._tag[:len(self._mode.tag)], self._mode.tag + ): + raise InvalidTag return b"" def authenticate_additional_data(self, data): diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 0e0918af..b6058150 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -78,7 +78,13 @@ class _CipherContext(object): len(iv_nonce), self._backend._ffi.NULL ) self._backend.openssl_assert(res != 0) - if operation == self._DECRYPT: + if operation == self._DECRYPT and \ + self._backend.openssl_version_number() < 0x10002000: + if mode.tag is None: + raise NotImplementedError( + "delayed passing of GCM tag requires OpenSSL >= 1.0.2." + " To use this feature please update OpenSSL" + ) res = self._backend._lib.EVP_CIPHER_CTX_ctrl( ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG, len(mode.tag), mode.tag @@ -134,6 +140,20 @@ class _CipherContext(object): if isinstance(self._mode, modes.GCM): self.update(b"") + if self._operation == self._DECRYPT and \ + isinstance(self._mode, modes.ModeWithAuthenticationTag) and \ + self._backend.openssl_version_number() >= 0x10002000: + 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 + ) + self._backend.openssl_assert(res != 0) + buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes) outlen = self._backend._ffi.new("int *") res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen) diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py index e9d55a10..9e0d0051 100644 --- a/src/cryptography/hazmat/primitives/ciphers/base.py +++ b/src/cryptography/hazmat/primitives/ciphers/base.py @@ -76,6 +76,16 @@ class AEADCipherContext(object): @six.add_metaclass(abc.ABCMeta) +class AEADDecryptionContext(object): + @abc.abstractmethod + def finalize_with_tag(self, tag): + """ + Returns the results of processing the final block as bytes and allows + delayed passing of the authentication tag. + """ + + +@six.add_metaclass(abc.ABCMeta) class AEADEncryptionContext(object): @abc.abstractproperty def tag(self): @@ -115,11 +125,6 @@ class Cipher(object): return self._wrap_ctx(ctx, encrypt=True) def decryptor(self): - if isinstance(self.mode, modes.ModeWithAuthenticationTag): - if self.mode.tag is None: - raise ValueError( - "Authentication tag must be provided when decrypting." - ) ctx = self._backend.create_symmetric_decryption_ctx( self.algorithm, self.mode ) @@ -169,6 +174,7 @@ class _CipherContext(object): @utils.register_interface(AEADCipherContext) @utils.register_interface(CipherContext) +@utils.register_interface(AEADDecryptionContext) class _AEADCipherContext(object): def __init__(self, ctx): self._ctx = ctx @@ -214,6 +220,16 @@ class _AEADCipherContext(object): self._ctx = None return data + def finalize_with_tag(self, tag): + if self._ctx._backend.name == "openssl" and \ + self._ctx._backend.openssl_version_number() < 0x10002000: + 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() + def authenticate_additional_data(self, data): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py index 802e544a..5b28157a 100644 --- a/src/cryptography/hazmat/primitives/ciphers/modes.py +++ b/src/cryptography/hazmat/primitives/ciphers/modes.py @@ -161,21 +161,22 @@ class GCM(object): # len(initialization_vector) must in [1, 2 ** 64), but it's impossible # to actually construct a bytes object that large, so we don't check # for it - if min_tag_length < 4: - raise ValueError("min_tag_length must be >= 4") - if tag is not None and len(tag) < min_tag_length: - raise ValueError( - "Authentication tag must be {0} bytes or longer.".format( - min_tag_length) - ) - if not isinstance(initialization_vector, bytes): raise TypeError("initialization_vector must be bytes") - - if tag is not None and not isinstance(tag, bytes): - raise TypeError("tag must be bytes or None") - 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") + if min_tag_length < 4: + raise ValueError("min_tag_length must be >= 4") + if len(tag) < min_tag_length: + raise ValueError( + "Authentication tag must be {0} bytes or longer.".format( + min_tag_length) + ) self._tag = tag tag = utils.read_only_property("_tag") |