aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2017-10-02 10:03:20 +0800
committerAlex Gaynor <alex.gaynor@gmail.com>2017-10-01 22:03:20 -0400
commita397d75a1e091299d012035655bdc30376378b4c (patch)
tree6cc453b672db069abe64838ec3d4d990777f20fc /src
parentdd567cbf732d310e8a79aa05d7001c8639e9e6f3 (diff)
downloadcryptography-a397d75a1e091299d012035655bdc30376378b4c.tar.gz
cryptography-a397d75a1e091299d012035655bdc30376378b4c.tar.bz2
cryptography-a397d75a1e091299d012035655bdc30376378b4c.zip
Add support for AES XTS (#3900)
* Add support for AES XTS We drop the non-byte aligned test vectors because according to NIST http://csrc.nist.gov/groups/STM/cavp/documents/aes/XTSVS.pdf "An implementation may support a data unit length that is not a multiple of 8 bits." OpenSSL does not support this, so we can't use those test vectors. * fix docs and pep8 * docs fix * the spellchecker is so frustrating * add note about AES 192 for XTS (it's not supported) * docs work * enforce key length on ECB mode in AES as well (thanks XTS) * a few more words about why we exclude some test vectors for XTS
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py8
-rw-r--r--src/cryptography/hazmat/backends/openssl/ciphers.py2
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/algorithms.py3
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/modes.py59
4 files changed, 63 insertions, 9 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 2cbfca2c..6abf4ecc 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -61,7 +61,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import (
AES, ARC4, Blowfish, CAST5, Camellia, ChaCha20, IDEA, SEED, TripleDES
)
from cryptography.hazmat.primitives.ciphers.modes import (
- CBC, CFB, CFB8, CTR, ECB, GCM, OFB
+ CBC, CFB, CFB8, CTR, ECB, GCM, OFB, XTS
)
from cryptography.hazmat.primitives.kdf import scrypt
@@ -263,6 +263,7 @@ class Backend(object):
type(None),
GetCipherByName("chacha20")
)
+ self.register_cipher_adapter(AES, XTS, _get_xts_cipher)
def create_symmetric_encryption_ctx(self, cipher, mode):
return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
@@ -1961,4 +1962,9 @@ class GetCipherByName(object):
return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
+def _get_xts_cipher(backend, cipher, mode):
+ cipher_name = "aes-{0}-xts".format(cipher.key_size // 2)
+ return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
+
+
backend = Backend()
diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py
index dfb33a07..8e55e28b 100644
--- a/src/cryptography/hazmat/backends/openssl/ciphers.py
+++ b/src/cryptography/hazmat/backends/openssl/ciphers.py
@@ -57,6 +57,8 @@ class _CipherContext(object):
if isinstance(mode, modes.ModeWithInitializationVector):
iv_nonce = mode.initialization_vector
+ elif isinstance(mode, modes.ModeWithTweak):
+ iv_nonce = mode.tweak
elif isinstance(mode, modes.ModeWithNonce):
iv_nonce = mode.nonce
elif isinstance(cipher, modes.ModeWithNonce):
diff --git a/src/cryptography/hazmat/primitives/ciphers/algorithms.py b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
index 6e5eb313..99a837e4 100644
--- a/src/cryptography/hazmat/primitives/ciphers/algorithms.py
+++ b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
@@ -25,7 +25,8 @@ def _verify_key_size(algorithm, key):
class AES(object):
name = "AES"
block_size = 128
- key_sizes = frozenset([128, 192, 256])
+ # 512 added to support AES-256-XTS, which uses 512-bit keys
+ key_sizes = frozenset([128, 192, 256, 512])
def __init__(self, key):
self.key = _verify_key_size(self, key)
diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py
index 54670b7f..598dfaa4 100644
--- a/src/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/src/cryptography/hazmat/primitives/ciphers/modes.py
@@ -37,6 +37,15 @@ class ModeWithInitializationVector(object):
@six.add_metaclass(abc.ABCMeta)
+class ModeWithTweak(object):
+ @abc.abstractproperty
+ def tweak(self):
+ """
+ The value of the tweak for this mode as bytes.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
class ModeWithNonce(object):
@abc.abstractproperty
def nonce(self):
@@ -54,6 +63,13 @@ class ModeWithAuthenticationTag(object):
"""
+def _check_aes_key_length(self, algorithm):
+ if algorithm.key_size > 256 and algorithm.name == "AES":
+ raise ValueError(
+ "Only 128, 192, and 256 bit keys are allowed for this AES mode"
+ )
+
+
def _check_iv_length(self, algorithm):
if len(self.initialization_vector) * 8 != algorithm.block_size:
raise ValueError("Invalid IV size ({0}) for {1}.".format(
@@ -61,6 +77,11 @@ def _check_iv_length(self, algorithm):
))
+def _check_iv_and_key_length(self, algorithm):
+ _check_aes_key_length(self, algorithm)
+ _check_iv_length(self, algorithm)
+
+
@utils.register_interface(Mode)
@utils.register_interface(ModeWithInitializationVector)
class CBC(object):
@@ -73,15 +94,38 @@ class CBC(object):
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
- validate_for_algorithm = _check_iv_length
+ validate_for_algorithm = _check_iv_and_key_length
+
+
+@utils.register_interface(Mode)
+@utils.register_interface(ModeWithTweak)
+class XTS(object):
+ name = "XTS"
+
+ def __init__(self, tweak):
+ if not isinstance(tweak, bytes):
+ raise TypeError("tweak must be bytes")
+
+ if len(tweak) != 16:
+ raise ValueError("tweak must be 128-bits (16 bytes)")
+
+ self._tweak = tweak
+
+ tweak = utils.read_only_property("_tweak")
+
+ def validate_for_algorithm(self, algorithm):
+ if algorithm.key_size not in (256, 512):
+ raise ValueError(
+ "The XTS specification requires a 256-bit key for AES-128-XTS"
+ " and 512-bit key for AES-256-XTS"
+ )
@utils.register_interface(Mode)
class ECB(object):
name = "ECB"
- def validate_for_algorithm(self, algorithm):
- pass
+ validate_for_algorithm = _check_aes_key_length
@utils.register_interface(Mode)
@@ -96,7 +140,7 @@ class OFB(object):
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
- validate_for_algorithm = _check_iv_length
+ validate_for_algorithm = _check_iv_and_key_length
@utils.register_interface(Mode)
@@ -111,7 +155,7 @@ class CFB(object):
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
- validate_for_algorithm = _check_iv_length
+ validate_for_algorithm = _check_iv_and_key_length
@utils.register_interface(Mode)
@@ -126,7 +170,7 @@ class CFB8(object):
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
- validate_for_algorithm = _check_iv_length
+ validate_for_algorithm = _check_iv_and_key_length
@utils.register_interface(Mode)
@@ -143,6 +187,7 @@ class CTR(object):
nonce = utils.read_only_property("_nonce")
def validate_for_algorithm(self, algorithm):
+ _check_aes_key_length(self, algorithm)
if len(self.nonce) * 8 != algorithm.block_size:
raise ValueError("Invalid nonce size ({0}) for {1}.".format(
len(self.nonce), self.name
@@ -180,4 +225,4 @@ class GCM(object):
initialization_vector = utils.read_only_property("_initialization_vector")
def validate_for_algorithm(self, algorithm):
- pass
+ _check_aes_key_length(self, algorithm)