diff options
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r-- | cryptography/hazmat/bindings/openssl/backend.py | 56 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/ciphers/base.py | 63 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/ciphers/modes.py | 11 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/interfaces.py | 24 |
4 files changed, 145 insertions, 9 deletions
diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py index 9f8ea939..1b19ddaa 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, InvalidTag from cryptography.hazmat.bindings.interfaces import ( CipherBackend, HashBackend, HMACBackend ) @@ -28,7 +28,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import ( AES, Blowfish, Camellia, CAST5, TripleDES, ARC4, ) from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, CTR, ECB, OFB, CFB + CBC, CTR, ECB, OFB, CFB, GCM, ) @@ -186,6 +186,11 @@ class Backend(object): type(None), GetCipherByName("rc4") ) + self.register_cipher_adapter( + AES, + GCM, + GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") + ) def create_symmetric_encryption_ctx(self, cipher, mode): return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) @@ -193,8 +198,10 @@ class Backend(object): def create_symmetric_decryption_ctx(self, cipher, mode): return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) - def _handle_error(self): + def _handle_error(self, mode): code = self.lib.ERR_get_error() + if not code and isinstance(mode, GCM): + raise InvalidTag assert code != 0 lib = self.lib.ERR_GET_LIB(code) func = self.lib.ERR_GET_FUNC(code) @@ -231,6 +238,8 @@ class GetCipherByName(object): @utils.register_interface(interfaces.CipherContext) +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.AEADEncryptionContext) class _CipherContext(object): _ENCRYPT = 1 _DECRYPT = 0 @@ -238,6 +247,9 @@ class _CipherContext(object): def __init__(self, backend, cipher, mode, operation): self._backend = backend self._cipher = cipher + self._mode = mode + self._operation = operation + self._tag = None ctx = self._backend.lib.EVP_CIPHER_CTX_new() ctx = self._backend.ffi.gc(ctx, self._backend.lib.EVP_CIPHER_CTX_free) @@ -270,6 +282,20 @@ class _CipherContext(object): ctx, len(cipher.key) ) assert res != 0 + if isinstance(mode, GCM): + res = self._backend.lib.EVP_CIPHER_CTX_ctrl( + ctx, self._backend.lib.Cryptography_EVP_CTRL_GCM_SET_IVLEN, + len(iv_nonce), self._backend.ffi.NULL + ) + assert res != 0 + if operation == self._DECRYPT: + assert mode.tag is not None + res = self._backend.lib.EVP_CIPHER_CTX_ctrl( + ctx, self._backend.lib.Cryptography_EVP_CTRL_GCM_SET_TAG, + len(mode.tag), mode.tag + ) + assert res != 0 + # pass key/iv res = self._backend.lib.EVP_CipherInit_ex(ctx, self._backend.ffi.NULL, self._backend.ffi.NULL, @@ -296,12 +322,34 @@ 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._backend._handle_error(self._mode) + + if (isinstance(self._mode, GCM) and + self._operation == self._ENCRYPT): + block_byte_size = self._cipher.block_size // 8 + tag_buf = self._backend.ffi.new("unsigned char[]", block_byte_size) + res = self._backend.lib.EVP_CIPHER_CTX_ctrl( + self._ctx, self._backend.lib.Cryptography_EVP_CTRL_GCM_GET_TAG, + block_byte_size, tag_buf + ) + assert res != 0 + self._tag = self._backend.ffi.buffer(tag_buf)[:] res = self._backend.lib.EVP_CIPHER_CTX_cleanup(self._ctx) assert res == 1 return self._backend.ffi.buffer(buf)[:outlen[0]] + def authenticate_additional_data(self, data): + outlen = self._backend.ffi.new("int *") + res = self._backend.lib.EVP_CipherUpdate( + self._ctx, self._backend.ffi.NULL, outlen, data, len(data) + ) + assert res != 0 + + @property + def tag(self): + return self._tag + @utils.register_interface(interfaces.HashContext) class _HashContext(object): diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py index 48e6da6f..b8615cb9 100644 --- a/cryptography/hazmat/primitives/ciphers/base.py +++ b/cryptography/hazmat/primitives/ciphers/base.py @@ -14,7 +14,9 @@ from __future__ import absolute_import, division, print_function from cryptography import utils -from cryptography.exceptions import AlreadyFinalized +from cryptography.exceptions import ( + AlreadyFinalized, NotYetFinalized, AlreadyUpdated, +) from cryptography.hazmat.primitives import interfaces @@ -28,14 +30,25 @@ class Cipher(object): self._backend = backend def encryptor(self): - return _CipherContext(self._backend.create_symmetric_encryption_ctx( + ctx = self._backend.create_symmetric_encryption_ctx( self.algorithm, self.mode - )) + ) + return self._wrap_ctx(ctx, True) def decryptor(self): - return _CipherContext(self._backend.create_symmetric_decryption_ctx( + ctx = self._backend.create_symmetric_decryption_ctx( self.algorithm, self.mode - )) + ) + return self._wrap_ctx(ctx, False) + + def _wrap_ctx(self, ctx, encrypt): + if isinstance(self.mode, interfaces.ModeWithAuthenticationTag): + if encrypt: + return _AEADEncryptionContext(ctx) + else: + return _AEADCipherContext(ctx) + else: + return _CipherContext(ctx) @utils.register_interface(interfaces.CipherContext) @@ -54,3 +67,43 @@ class _CipherContext(object): data = self._ctx.finalize() self._ctx = None return data + + +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.CipherContext) +class _AEADCipherContext(object): + def __init__(self, ctx): + self._ctx = ctx + self._tag = None + self._updated = False + + def update(self, data): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + self._updated = True + return self._ctx.update(data) + + def finalize(self): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + data = self._ctx.finalize() + self._tag = self._ctx.tag + self._ctx = None + return data + + def authenticate_additional_data(self, data): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + if self._updated: + raise AlreadyUpdated("Update has been called on this context") + self._ctx.authenticate_additional_data(data) + + +@utils.register_interface(interfaces.AEADEncryptionContext) +class _AEADEncryptionContext(_AEADCipherContext): + @property + def tag(self): + if self._ctx is not None: + raise NotYetFinalized("You must finalize encryption before " + "getting the tag") + return self._tag diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py index 1d0de689..e1c70185 100644 --- a/cryptography/hazmat/primitives/ciphers/modes.py +++ b/cryptography/hazmat/primitives/ciphers/modes.py @@ -56,3 +56,14 @@ class CTR(object): def __init__(self, nonce): self.nonce = nonce + + +@utils.register_interface(interfaces.Mode) +@utils.register_interface(interfaces.ModeWithInitializationVector) +@utils.register_interface(interfaces.ModeWithAuthenticationTag) +class GCM(object): + name = "GCM" + + def __init__(self, initialization_vector, tag=None): + self.initialization_vector = initialization_vector + self.tag = tag diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py index 8cc9d42c..e3f4f586 100644 --- a/cryptography/hazmat/primitives/interfaces.py +++ b/cryptography/hazmat/primitives/interfaces.py @@ -56,6 +56,14 @@ class ModeWithNonce(six.with_metaclass(abc.ABCMeta)): """ +class ModeWithAuthenticationTag(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def tag(self): + """ + The value of the tag supplied to the constructor of this mode. + """ + + class CipherContext(six.with_metaclass(abc.ABCMeta)): @abc.abstractmethod def update(self, data): @@ -70,6 +78,22 @@ class CipherContext(six.with_metaclass(abc.ABCMeta)): """ +class AEADCipherContext(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def authenticate_additional_data(self, data): + """ + authenticate_additional_data takes bytes and returns nothing. + """ + + +class AEADEncryptionContext(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def tag(self): + """ + Returns tag bytes after finalizing encryption. + """ + + class PaddingContext(six.with_metaclass(abc.ABCMeta)): @abc.abstractmethod def update(self, data): |