From 336423ea730ae65a639c33b8b9cd7df75969ccce Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 20 May 2014 23:10:08 -0500 Subject: add AES CTR support to OpenSSL 0.9.8 --- cryptography/hazmat/backends/openssl/backend.py | 51 ++++++++++++++++++++++++- cryptography/hazmat/bindings/openssl/aes.py | 6 +++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 8853646a..d7110f81 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -132,6 +132,14 @@ class Backend(object): return _HashContext(self, algorithm) def cipher_supported(self, cipher, mode): + if self._cipher_supported(cipher, mode): + return True + elif isinstance(mode, CTR) and isinstance(cipher, AES): + return True + else: + return False + + def _cipher_supported(self, cipher, mode): try: adapter = self._cipher_registry[type(cipher), type(mode)] except KeyError: @@ -198,10 +206,18 @@ class Backend(object): ) def create_symmetric_encryption_ctx(self, cipher, mode): - return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) + if (isinstance(mode, CTR) and isinstance(cipher, AES) + and not self._cipher_supported(cipher, mode)): + return _AESCTRCipherContext(self, cipher, mode) + else: + return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) def create_symmetric_decryption_ctx(self, cipher, mode): - return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + if (isinstance(mode, CTR) and isinstance(cipher, AES) + and not self._cipher_supported(cipher, mode)): + return _AESCTRCipherContext(self, cipher, mode) + else: + return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) def pbkdf2_hmac_supported(self, algorithm): if self._lib.Cryptography_HAS_PBKDF2_HMAC: @@ -834,6 +850,37 @@ class _CipherContext(object): return self._tag +@utils.register_interface(interfaces.CipherContext) +class _AESCTRCipherContext(object): + def __init__(self, backend, cipher, mode): + self._backend = backend + + self._key = self._backend._ffi.new("AES_KEY *") + assert self._key != self._backend._ffi.NULL + res = self._backend._lib.AES_set_encrypt_key( + cipher.key, len(cipher.key) * 8, self._key + ) + assert res == 0 + self._ecount = self._backend._ffi.new("char[]", 16) + self._nonce = self._backend._ffi.new("char[]", mode.nonce) + self._num = self._backend._ffi.new("unsigned int *", 0) + + def update(self, data): + buf = self._backend._ffi.new("unsigned char[]", len(data)) + self._backend._lib.AES_ctr128_encrypt( + data, buf, len(data), self._key, self._nonce, + self._ecount, self._num + ) + return self._backend._ffi.buffer(buf)[:] + + def finalize(self): + self._key = None + self._ecount = None + self._nonce = None + self._num = None + return b"" + + @utils.register_interface(interfaces.HashContext) class _HashContext(object): def __init__(self, backend, algorithm, ctx=None): diff --git a/cryptography/hazmat/bindings/openssl/aes.py b/cryptography/hazmat/bindings/openssl/aes.py index 17c154cf..b0e00721 100644 --- a/cryptography/hazmat/bindings/openssl/aes.py +++ b/cryptography/hazmat/bindings/openssl/aes.py @@ -29,6 +29,12 @@ typedef struct aes_key_st AES_KEY; FUNCTIONS = """ int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *); int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *); +/* The ctr128_encrypt function is only useful in 0.9.8. You should use EVP for + this in 1.0.0+. */ +void AES_ctr128_encrypt(const unsigned char *, unsigned char *, + const unsigned long, const AES_KEY *, + unsigned char[], unsigned char[], unsigned int *); + """ MACROS = """ -- cgit v1.2.3 From 055f9601e84cae2c8c5998fbbbf4cbabfb646272 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 20 May 2014 23:21:26 -0500 Subject: add changelog entry --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1801f33f..f5702812 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,8 @@ Changelog for :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` and :class:`~cryptography.hazmat.primitives.ciphers.algorithms.TripleDES` on :doc:`/hazmat/backends/commoncrypto` and :doc:`/hazmat/backends/openssl`. +* Added ``AES`` :class:`~cryptography.hazmat.primitives.ciphers.modes.CTR` + support to the OpenSSL backend when linked against 0.9.8. 0.4 - 2014-05-03 ~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From 6e6ac23ad11bc1932b34d3013b39fc858a16a53e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 21 May 2014 08:48:09 -0500 Subject: allocate only 16 bytes cffi allocates 17 bytes (one for trailing null), but that isn't used by this function so we don't need to allocate it. --- cryptography/hazmat/backends/openssl/backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index d7110f81..7b0e50d3 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -862,7 +862,7 @@ class _AESCTRCipherContext(object): ) assert res == 0 self._ecount = self._backend._ffi.new("char[]", 16) - self._nonce = self._backend._ffi.new("char[]", mode.nonce) + self._nonce = self._backend._ffi.new("char[16]", mode.nonce) self._num = self._backend._ffi.new("unsigned int *", 0) def update(self, data): -- cgit v1.2.3 From 9e233139790599a3d779330bb351ba1da984ae60 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 21 May 2014 10:17:57 -0500 Subject: add some comments to explain the existence of AESCTRCipherContext --- cryptography/hazmat/backends/openssl/backend.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 7b0e50d3..d9165492 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -208,6 +208,9 @@ class Backend(object): def create_symmetric_encryption_ctx(self, cipher, mode): if (isinstance(mode, CTR) and isinstance(cipher, AES) and not self._cipher_supported(cipher, mode)): + # This is needed to provide support for AES CTR mode in OpenSSL + # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 + # extended life ends 2020). return _AESCTRCipherContext(self, cipher, mode) else: return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) @@ -215,6 +218,9 @@ class Backend(object): def create_symmetric_decryption_ctx(self, cipher, mode): if (isinstance(mode, CTR) and isinstance(cipher, AES) and not self._cipher_supported(cipher, mode)): + # This is needed to provide support for AES CTR mode in OpenSSL + # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 + # extended life ends 2020). return _AESCTRCipherContext(self, cipher, mode) else: return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) @@ -850,6 +856,8 @@ class _CipherContext(object): return self._tag +# This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can +# be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020). @utils.register_interface(interfaces.CipherContext) class _AESCTRCipherContext(object): def __init__(self, backend, cipher, mode): -- cgit v1.2.3 From 3211d16da6e9a90567248208164b0e7e9e4e3bcb Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 21 May 2014 10:34:49 -0500 Subject: add test to verify AES CTR is always available in 0.9.8+, comment updates --- cryptography/hazmat/backends/openssl/backend.py | 10 ++++++---- tests/hazmat/backends/test_openssl.py | 7 ++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index d9165492..c66f0452 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -132,14 +132,14 @@ class Backend(object): return _HashContext(self, algorithm) def cipher_supported(self, cipher, mode): - if self._cipher_supported(cipher, mode): + if self._evp_cipher_supported(cipher, mode): return True elif isinstance(mode, CTR) and isinstance(cipher, AES): return True else: return False - def _cipher_supported(self, cipher, mode): + def _evp_cipher_supported(self, cipher, mode): try: adapter = self._cipher_registry[type(cipher), type(mode)] except KeyError: @@ -856,10 +856,12 @@ class _CipherContext(object): return self._tag -# This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can -# be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020). @utils.register_interface(interfaces.CipherContext) class _AESCTRCipherContext(object): + """ + This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can + be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020). + """ def __init__(self, backend, cipher, mode): self._backend = backend diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 154af00f..fb2ca19f 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -22,7 +22,7 @@ from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import dsa, padding, rsa from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES -from cryptography.hazmat.primitives.ciphers.modes import CBC +from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR from cryptography.hazmat.primitives.interfaces import BlockCipherAlgorithm from ...utils import raises_unsupported_algorithm @@ -64,6 +64,11 @@ class TestOpenSSL(object): def test_supports_cipher(self): assert backend.cipher_supported(None, None) is False + def test_aes_ctr_always_available(self): + # AES CTR should always be available in both 0.9.8 and 1.0.0+ + assert backend.cipher_supported(AES(b"\x00" * 16), + CTR(b"\x00" * 16)) is True + def test_register_duplicate_cipher_adapter(self): with pytest.raises(ValueError): backend.register_cipher_adapter(AES, CBC, None) -- cgit v1.2.3 From caa11cdbef16d9a3b910153e27155d456d534753 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 21 May 2014 10:54:35 -0500 Subject: rename all the things if you want them to work --- cryptography/hazmat/backends/openssl/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index c66f0452..bd5808ed 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -207,7 +207,7 @@ class Backend(object): def create_symmetric_encryption_ctx(self, cipher, mode): if (isinstance(mode, CTR) and isinstance(cipher, AES) - and not self._cipher_supported(cipher, mode)): + and not self._evp_cipher_supported(cipher, mode)): # This is needed to provide support for AES CTR mode in OpenSSL # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 # extended life ends 2020). @@ -217,7 +217,7 @@ class Backend(object): def create_symmetric_decryption_ctx(self, cipher, mode): if (isinstance(mode, CTR) and isinstance(cipher, AES) - and not self._cipher_supported(cipher, mode)): + and not self._evp_cipher_supported(cipher, mode)): # This is needed to provide support for AES CTR mode in OpenSSL # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5 # extended life ends 2020). -- cgit v1.2.3