aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst2
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py61
-rw-r--r--cryptography/hazmat/bindings/openssl/aes.py6
-rw-r--r--tests/hazmat/backends/test_openssl.py7
4 files changed, 73 insertions, 3 deletions
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
~~~~~~~~~~~~~~~~
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 8853646a..bd5808ed 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._evp_cipher_supported(cipher, mode):
+ return True
+ elif isinstance(mode, CTR) and isinstance(cipher, AES):
+ return True
+ else:
+ return False
+
+ def _evp_cipher_supported(self, cipher, mode):
try:
adapter = self._cipher_registry[type(cipher), type(mode)]
except KeyError:
@@ -198,10 +206,24 @@ 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._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).
+ 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._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).
+ 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 +856,41 @@ class _CipherContext(object):
return self._tag
+@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
+
+ 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[16]", 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 = """
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)