blob: 90a6b3b0a067a8d8eb7a057a5dd70d4fb219692d [file] [log] [blame]
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
import binascii
import os
import pytest
from cryptography.hazmat.backends.interfaces import CipherBackend
from cryptography.hazmat.primitives.ciphers import algorithms, base, modes
from .utils import _load_all_params, generate_aead_test, generate_encrypt_test
from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 32), modes.XTS(b"\x00" * 16)
),
skip_message="Does not support AES XTS",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeXTS(object):
@pytest.mark.parametrize(
"vector",
# This list comprehension excludes any vector that does not have a
# data unit length that is divisible by 8. The NIST vectors include
# tests for implementations that support encryption of data that is
# not divisible modulo 8, but OpenSSL is not such an implementation.
[x for x in _load_all_params(
os.path.join("ciphers", "AES", "XTS", "tweak-128hexstr"),
["XTSGenAES128.rsp", "XTSGenAES256.rsp"],
load_nist_vectors
) if int(x["dataunitlen"]) / 8.0 == int(x["dataunitlen"]) // 8]
)
def test_xts_vectors(self, vector, backend):
key = binascii.unhexlify(vector["key"])
tweak = binascii.unhexlify(vector["i"])
pt = binascii.unhexlify(vector["pt"])
ct = binascii.unhexlify(vector["ct"])
cipher = base.Cipher(algorithms.AES(key), modes.XTS(tweak), backend)
enc = cipher.encryptor()
computed_ct = enc.update(pt) + enc.finalize()
assert computed_ct == ct
dec = cipher.decryptor()
computed_pt = dec.update(ct) + dec.finalize()
assert computed_pt == pt
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 16), modes.CBC(b"\x00" * 16)
),
skip_message="Does not support AES CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCBC(object):
test_CBC = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CBC"),
[
"CBCGFSbox128.rsp",
"CBCGFSbox192.rsp",
"CBCGFSbox256.rsp",
"CBCKeySbox128.rsp",
"CBCKeySbox192.rsp",
"CBCKeySbox256.rsp",
"CBCVarKey128.rsp",
"CBCVarKey192.rsp",
"CBCVarKey256.rsp",
"CBCVarTxt128.rsp",
"CBCVarTxt192.rsp",
"CBCVarTxt256.rsp",
"CBCMMT128.rsp",
"CBCMMT192.rsp",
"CBCMMT256.rsp",
],
lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
lambda iv, **kwargs: modes.CBC(binascii.unhexlify(iv)),
)
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 16), modes.ECB()
),
skip_message="Does not support AES ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeECB(object):
test_ECB = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "ECB"),
[
"ECBGFSbox128.rsp",
"ECBGFSbox192.rsp",
"ECBGFSbox256.rsp",
"ECBKeySbox128.rsp",
"ECBKeySbox192.rsp",
"ECBKeySbox256.rsp",
"ECBVarKey128.rsp",
"ECBVarKey192.rsp",
"ECBVarKey256.rsp",
"ECBVarTxt128.rsp",
"ECBVarTxt192.rsp",
"ECBVarTxt256.rsp",
"ECBMMT128.rsp",
"ECBMMT192.rsp",
"ECBMMT256.rsp",
],
lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
lambda **kwargs: modes.ECB(),
)
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 16), modes.OFB(b"\x00" * 16)
),
skip_message="Does not support AES OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeOFB(object):
test_OFB = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "OFB"),
[
"OFBGFSbox128.rsp",
"OFBGFSbox192.rsp",
"OFBGFSbox256.rsp",
"OFBKeySbox128.rsp",
"OFBKeySbox192.rsp",
"OFBKeySbox256.rsp",
"OFBVarKey128.rsp",
"OFBVarKey192.rsp",
"OFBVarKey256.rsp",
"OFBVarTxt128.rsp",
"OFBVarTxt192.rsp",
"OFBVarTxt256.rsp",
"OFBMMT128.rsp",
"OFBMMT192.rsp",
"OFBMMT256.rsp",
],
lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
lambda iv, **kwargs: modes.OFB(binascii.unhexlify(iv)),
)
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 16), modes.CFB(b"\x00" * 16)
),
skip_message="Does not support AES CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCFB(object):
test_CFB = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CFB"),
[
"CFB128GFSbox128.rsp",
"CFB128GFSbox192.rsp",
"CFB128GFSbox256.rsp",
"CFB128KeySbox128.rsp",
"CFB128KeySbox192.rsp",
"CFB128KeySbox256.rsp",
"CFB128VarKey128.rsp",
"CFB128VarKey192.rsp",
"CFB128VarKey256.rsp",
"CFB128VarTxt128.rsp",
"CFB128VarTxt192.rsp",
"CFB128VarTxt256.rsp",
"CFB128MMT128.rsp",
"CFB128MMT192.rsp",
"CFB128MMT256.rsp",
],
lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv)),
)
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 16), modes.CFB8(b"\x00" * 16)
),
skip_message="Does not support AES CFB8",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCFB8(object):
test_CFB8 = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CFB"),
[
"CFB8GFSbox128.rsp",
"CFB8GFSbox192.rsp",
"CFB8GFSbox256.rsp",
"CFB8KeySbox128.rsp",
"CFB8KeySbox192.rsp",
"CFB8KeySbox256.rsp",
"CFB8VarKey128.rsp",
"CFB8VarKey192.rsp",
"CFB8VarKey256.rsp",
"CFB8VarTxt128.rsp",
"CFB8VarTxt192.rsp",
"CFB8VarTxt256.rsp",
"CFB8MMT128.rsp",
"CFB8MMT192.rsp",
"CFB8MMT256.rsp",
],
lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
)
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 16), modes.CTR(b"\x00" * 16)
),
skip_message="Does not support AES CTR",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCTR(object):
test_CTR = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CTR"),
["aes-128-ctr.txt", "aes-192-ctr.txt", "aes-256-ctr.txt"],
lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
lambda iv, **kwargs: modes.CTR(binascii.unhexlify(iv)),
)
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
algorithms.AES(b"\x00" * 16), modes.GCM(b"\x00" * 12)
),
skip_message="Does not support AES GCM",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeGCM(object):
test_GCM = generate_aead_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "GCM"),
[
"gcmDecrypt128.rsp",
"gcmDecrypt192.rsp",
"gcmDecrypt256.rsp",
"gcmEncryptExtIV128.rsp",
"gcmEncryptExtIV192.rsp",
"gcmEncryptExtIV256.rsp",
],
algorithms.AES,
modes.GCM,
)
def test_gcm_tag_with_only_aad(self, backend):
key = binascii.unhexlify(b"5211242698bed4774a090620a6ca56f3")
iv = binascii.unhexlify(b"b1e1349120b6e832ef976f5d")
aad = binascii.unhexlify(b"b6d729aab8e6416d7002b9faa794c410d8d2f193")
tag = binascii.unhexlify(b"0f247e7f9c2505de374006738018493b")
cipher = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
)
encryptor = cipher.encryptor()
encryptor.authenticate_additional_data(aad)
encryptor.finalize()
assert encryptor.tag == tag
def test_gcm_ciphertext_with_no_aad(self, backend):
key = binascii.unhexlify(b"e98b72a9881a84ca6b76e0f43e68647a")
iv = binascii.unhexlify(b"8b23299fde174053f3d652ba")
ct = binascii.unhexlify(b"5a3c1cf1985dbb8bed818036fdd5ab42")
tag = binascii.unhexlify(b"23c7ab0f952b7091cd324835043b5eb5")
pt = binascii.unhexlify(b"28286a321293253c3e0aa2704a278032")
cipher = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
)
encryptor = cipher.encryptor()
computed_ct = encryptor.update(pt) + encryptor.finalize()
assert computed_ct == ct
assert encryptor.tag == tag
def test_gcm_ciphertext_limit(self, backend):
encryptor = base.Cipher(
algorithms.AES(b"\x00" * 16),
modes.GCM(b"\x01" * 16),
backend=backend
).encryptor()
encryptor._bytes_processed = modes.GCM._MAX_ENCRYPTED_BYTES - 16
encryptor.update(b"0" * 16)
assert (
encryptor._bytes_processed == modes.GCM._MAX_ENCRYPTED_BYTES
)
with pytest.raises(ValueError):
encryptor.update(b"0")
def test_gcm_aad_limit(self, backend):
encryptor = base.Cipher(
algorithms.AES(b"\x00" * 16),
modes.GCM(b"\x01" * 16),
backend=backend
).encryptor()
encryptor._aad_bytes_processed = modes.GCM._MAX_AAD_BYTES - 16
encryptor.authenticate_additional_data(b"0" * 16)
assert encryptor._aad_bytes_processed == modes.GCM._MAX_AAD_BYTES
with pytest.raises(ValueError):
encryptor.authenticate_additional_data(b"0")
def test_gcm_ciphertext_increments(self, backend):
encryptor = base.Cipher(
algorithms.AES(b"\x00" * 16),
modes.GCM(b"\x01" * 16),
backend=backend
).encryptor()
encryptor.update(b"0" * 8)
assert encryptor._bytes_processed == 8
encryptor.update(b"0" * 7)
assert encryptor._bytes_processed == 15
encryptor.update(b"0" * 18)
assert encryptor._bytes_processed == 33
def test_gcm_aad_increments(self, backend):
encryptor = base.Cipher(
algorithms.AES(b"\x00" * 16),
modes.GCM(b"\x01" * 16),
backend=backend
).encryptor()
encryptor.authenticate_additional_data(b"0" * 8)
assert encryptor._aad_bytes_processed == 8
encryptor.authenticate_additional_data(b"0" * 18)
assert encryptor._aad_bytes_processed == 26
def test_gcm_tag_decrypt_none(self, backend):
key = binascii.unhexlify(b"5211242698bed4774a090620a6ca56f3")
iv = binascii.unhexlify(b"b1e1349120b6e832ef976f5d")
aad = binascii.unhexlify(b"b6d729aab8e6416d7002b9faa794c410d8d2f193")
encryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
).encryptor()
encryptor.authenticate_additional_data(aad)
encryptor.finalize()
if (
backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
):
with pytest.raises(NotImplementedError):
decryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
).decryptor()
else:
decryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
).decryptor()
decryptor.authenticate_additional_data(aad)
with pytest.raises(ValueError):
decryptor.finalize()
def test_gcm_tag_decrypt_mode(self, backend):
key = binascii.unhexlify(b"5211242698bed4774a090620a6ca56f3")
iv = binascii.unhexlify(b"b1e1349120b6e832ef976f5d")
aad = binascii.unhexlify(b"b6d729aab8e6416d7002b9faa794c410d8d2f193")
encryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
).encryptor()
encryptor.authenticate_additional_data(aad)
encryptor.finalize()
tag = encryptor.tag
decryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv, tag),
backend=backend
).decryptor()
decryptor.authenticate_additional_data(aad)
decryptor.finalize()
def test_gcm_tag_decrypt_finalize(self, backend):
key = binascii.unhexlify(b"5211242698bed4774a090620a6ca56f3")
iv = binascii.unhexlify(b"b1e1349120b6e832ef976f5d")
aad = binascii.unhexlify(b"b6d729aab8e6416d7002b9faa794c410d8d2f193")
encryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
).encryptor()
encryptor.authenticate_additional_data(aad)
encryptor.finalize()
tag = encryptor.tag
if (
backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
):
with pytest.raises(NotImplementedError):
decryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
).decryptor()
decryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv, tag=encryptor.tag),
backend=backend
).decryptor()
else:
decryptor = base.Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend=backend
).decryptor()
decryptor.authenticate_additional_data(aad)
if (
backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
):
with pytest.raises(NotImplementedError):
decryptor.finalize_with_tag(tag)
decryptor.finalize()
else:
decryptor.finalize_with_tag(tag)
@pytest.mark.supported(
only_if=lambda backend: (
not backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 or
backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
),
skip_message="Not supported on OpenSSL 1.0.1",
)
def test_gcm_tag_decrypt_finalize_tag_length(self, backend):
decryptor = base.Cipher(
algorithms.AES(b"0" * 16),
modes.GCM(b"0" * 12),
backend=backend
).decryptor()
with pytest.raises(ValueError):
decryptor.finalize_with_tag(b"tagtooshort")
def test_buffer_protocol(self, backend):
data = bytearray(b"helloworld")
enc = base.Cipher(
algorithms.AES(bytearray(b"\x00" * 16)),
modes.GCM(bytearray(b"\x00" * 12)),
backend
).encryptor()
enc.authenticate_additional_data(bytearray(b"foo"))
ct = enc.update(data) + enc.finalize()
dec = base.Cipher(
algorithms.AES(bytearray(b"\x00" * 16)),
modes.GCM(bytearray(b"\x00" * 12), enc.tag),
backend
).decryptor()
dec.authenticate_additional_data(bytearray(b"foo"))
pt = dec.update(ct) + dec.finalize()
assert pt == data
@pytest.mark.parametrize(
"mode",
[
modes.CBC(bytearray(b"\x00" * 16)),
modes.CTR(bytearray(b"\x00" * 16)),
modes.OFB(bytearray(b"\x00" * 16)),
modes.CFB(bytearray(b"\x00" * 16)),
modes.CFB8(bytearray(b"\x00" * 16)),
modes.XTS(bytearray(b"\x00" * 16)),
]
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
def test_buffer_protocol_alternate_modes(mode, backend):
data = bytearray(b"sixteen_byte_msg")
cipher = base.Cipher(
algorithms.AES(bytearray(b"\x00" * 32)), mode, backend
)
enc = cipher.encryptor()
ct = enc.update(data) + enc.finalize()
dec = cipher.decryptor()
pt = dec.update(ct) + dec.finalize()
assert pt == data