1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import absolute_import, division, print_function
6
7import binascii
8import os
9
10import pytest
11
12from cryptography.hazmat.backends.interfaces import CipherBackend
13from cryptography.hazmat.primitives.ciphers import algorithms, base, modes
14
15from .utils import _load_all_params, generate_encrypt_test
16from ...doubles import DummyMode
17from ...utils import load_nist_vectors
18
19
20@pytest.mark.supported(
21    only_if=lambda backend: backend.cipher_supported(
22        algorithms.AES(b"\x00" * 32), modes.XTS(b"\x00" * 16)
23    ),
24    skip_message="Does not support AES XTS",
25)
26@pytest.mark.requires_backend_interface(interface=CipherBackend)
27class TestAESModeXTS(object):
28    @pytest.mark.parametrize(
29        "vector",
30        # This list comprehension excludes any vector that does not have a
31        # data unit length that is divisible by 8. The NIST vectors include
32        # tests for implementations that support encryption of data that is
33        # not divisible modulo 8, but OpenSSL is not such an implementation.
34        [
35            x
36            for x in _load_all_params(
37                os.path.join("ciphers", "AES", "XTS", "tweak-128hexstr"),
38                ["XTSGenAES128.rsp", "XTSGenAES256.rsp"],
39                load_nist_vectors,
40            )
41            if int(x["dataunitlen"]) / 8.0 == int(x["dataunitlen"]) // 8
42        ],
43    )
44    def test_xts_vectors(self, vector, backend):
45        key = binascii.unhexlify(vector["key"])
46        tweak = binascii.unhexlify(vector["i"])
47        pt = binascii.unhexlify(vector["pt"])
48        ct = binascii.unhexlify(vector["ct"])
49        cipher = base.Cipher(algorithms.AES(key), modes.XTS(tweak), backend)
50        enc = cipher.encryptor()
51        computed_ct = enc.update(pt) + enc.finalize()
52        assert computed_ct == ct
53        dec = cipher.decryptor()
54        computed_pt = dec.update(ct) + dec.finalize()
55        assert computed_pt == pt
56
57
58@pytest.mark.supported(
59    only_if=lambda backend: backend.cipher_supported(
60        algorithms.AES(b"\x00" * 16), modes.CBC(b"\x00" * 16)
61    ),
62    skip_message="Does not support AES CBC",
63)
64@pytest.mark.requires_backend_interface(interface=CipherBackend)
65class TestAESModeCBC(object):
66    test_cbc = generate_encrypt_test(
67        load_nist_vectors,
68        os.path.join("ciphers", "AES", "CBC"),
69        [
70            "CBCGFSbox128.rsp",
71            "CBCGFSbox192.rsp",
72            "CBCGFSbox256.rsp",
73            "CBCKeySbox128.rsp",
74            "CBCKeySbox192.rsp",
75            "CBCKeySbox256.rsp",
76            "CBCVarKey128.rsp",
77            "CBCVarKey192.rsp",
78            "CBCVarKey256.rsp",
79            "CBCVarTxt128.rsp",
80            "CBCVarTxt192.rsp",
81            "CBCVarTxt256.rsp",
82            "CBCMMT128.rsp",
83            "CBCMMT192.rsp",
84            "CBCMMT256.rsp",
85        ],
86        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
87        lambda iv, **kwargs: modes.CBC(binascii.unhexlify(iv)),
88    )
89
90
91@pytest.mark.supported(
92    only_if=lambda backend: backend.cipher_supported(
93        algorithms.AES(b"\x00" * 16), modes.ECB()
94    ),
95    skip_message="Does not support AES ECB",
96)
97@pytest.mark.requires_backend_interface(interface=CipherBackend)
98class TestAESModeECB(object):
99    test_ecb = generate_encrypt_test(
100        load_nist_vectors,
101        os.path.join("ciphers", "AES", "ECB"),
102        [
103            "ECBGFSbox128.rsp",
104            "ECBGFSbox192.rsp",
105            "ECBGFSbox256.rsp",
106            "ECBKeySbox128.rsp",
107            "ECBKeySbox192.rsp",
108            "ECBKeySbox256.rsp",
109            "ECBVarKey128.rsp",
110            "ECBVarKey192.rsp",
111            "ECBVarKey256.rsp",
112            "ECBVarTxt128.rsp",
113            "ECBVarTxt192.rsp",
114            "ECBVarTxt256.rsp",
115            "ECBMMT128.rsp",
116            "ECBMMT192.rsp",
117            "ECBMMT256.rsp",
118        ],
119        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
120        lambda **kwargs: modes.ECB(),
121    )
122
123
124@pytest.mark.supported(
125    only_if=lambda backend: backend.cipher_supported(
126        algorithms.AES(b"\x00" * 16), modes.OFB(b"\x00" * 16)
127    ),
128    skip_message="Does not support AES OFB",
129)
130@pytest.mark.requires_backend_interface(interface=CipherBackend)
131class TestAESModeOFB(object):
132    test_ofb = generate_encrypt_test(
133        load_nist_vectors,
134        os.path.join("ciphers", "AES", "OFB"),
135        [
136            "OFBGFSbox128.rsp",
137            "OFBGFSbox192.rsp",
138            "OFBGFSbox256.rsp",
139            "OFBKeySbox128.rsp",
140            "OFBKeySbox192.rsp",
141            "OFBKeySbox256.rsp",
142            "OFBVarKey128.rsp",
143            "OFBVarKey192.rsp",
144            "OFBVarKey256.rsp",
145            "OFBVarTxt128.rsp",
146            "OFBVarTxt192.rsp",
147            "OFBVarTxt256.rsp",
148            "OFBMMT128.rsp",
149            "OFBMMT192.rsp",
150            "OFBMMT256.rsp",
151        ],
152        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
153        lambda iv, **kwargs: modes.OFB(binascii.unhexlify(iv)),
154    )
155
156
157@pytest.mark.supported(
158    only_if=lambda backend: backend.cipher_supported(
159        algorithms.AES(b"\x00" * 16), modes.CFB(b"\x00" * 16)
160    ),
161    skip_message="Does not support AES CFB",
162)
163@pytest.mark.requires_backend_interface(interface=CipherBackend)
164class TestAESModeCFB(object):
165    test_cfb = generate_encrypt_test(
166        load_nist_vectors,
167        os.path.join("ciphers", "AES", "CFB"),
168        [
169            "CFB128GFSbox128.rsp",
170            "CFB128GFSbox192.rsp",
171            "CFB128GFSbox256.rsp",
172            "CFB128KeySbox128.rsp",
173            "CFB128KeySbox192.rsp",
174            "CFB128KeySbox256.rsp",
175            "CFB128VarKey128.rsp",
176            "CFB128VarKey192.rsp",
177            "CFB128VarKey256.rsp",
178            "CFB128VarTxt128.rsp",
179            "CFB128VarTxt192.rsp",
180            "CFB128VarTxt256.rsp",
181            "CFB128MMT128.rsp",
182            "CFB128MMT192.rsp",
183            "CFB128MMT256.rsp",
184        ],
185        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
186        lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv)),
187    )
188
189
190@pytest.mark.supported(
191    only_if=lambda backend: backend.cipher_supported(
192        algorithms.AES(b"\x00" * 16), modes.CFB8(b"\x00" * 16)
193    ),
194    skip_message="Does not support AES CFB8",
195)
196@pytest.mark.requires_backend_interface(interface=CipherBackend)
197class TestAESModeCFB8(object):
198    test_cfb8 = generate_encrypt_test(
199        load_nist_vectors,
200        os.path.join("ciphers", "AES", "CFB"),
201        [
202            "CFB8GFSbox128.rsp",
203            "CFB8GFSbox192.rsp",
204            "CFB8GFSbox256.rsp",
205            "CFB8KeySbox128.rsp",
206            "CFB8KeySbox192.rsp",
207            "CFB8KeySbox256.rsp",
208            "CFB8VarKey128.rsp",
209            "CFB8VarKey192.rsp",
210            "CFB8VarKey256.rsp",
211            "CFB8VarTxt128.rsp",
212            "CFB8VarTxt192.rsp",
213            "CFB8VarTxt256.rsp",
214            "CFB8MMT128.rsp",
215            "CFB8MMT192.rsp",
216            "CFB8MMT256.rsp",
217        ],
218        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
219        lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
220    )
221
222
223@pytest.mark.supported(
224    only_if=lambda backend: backend.cipher_supported(
225        algorithms.AES(b"\x00" * 16), modes.CTR(b"\x00" * 16)
226    ),
227    skip_message="Does not support AES CTR",
228)
229@pytest.mark.requires_backend_interface(interface=CipherBackend)
230class TestAESModeCTR(object):
231    test_ctr = generate_encrypt_test(
232        load_nist_vectors,
233        os.path.join("ciphers", "AES", "CTR"),
234        ["aes-128-ctr.txt", "aes-192-ctr.txt", "aes-256-ctr.txt"],
235        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
236        lambda iv, **kwargs: modes.CTR(binascii.unhexlify(iv)),
237    )
238
239
240@pytest.mark.parametrize(
241    "mode",
242    [
243        modes.CBC(bytearray(b"\x00" * 16)),
244        modes.CTR(bytearray(b"\x00" * 16)),
245        modes.OFB(bytearray(b"\x00" * 16)),
246        modes.CFB(bytearray(b"\x00" * 16)),
247        modes.CFB8(bytearray(b"\x00" * 16)),
248        modes.XTS(bytearray(b"\x00" * 16)),
249        # Add a dummy mode for coverage of the cipher_supported check.
250        DummyMode(),
251    ],
252)
253@pytest.mark.requires_backend_interface(interface=CipherBackend)
254def test_buffer_protocol_alternate_modes(mode, backend):
255    data = bytearray(b"sixteen_byte_msg")
256    key = algorithms.AES(bytearray(os.urandom(32)))
257    if not backend.cipher_supported(key, mode):
258        pytest.skip("AES in {} mode not supported".format(mode.name))
259    cipher = base.Cipher(key, mode, backend)
260    enc = cipher.encryptor()
261    ct = enc.update(data) + enc.finalize()
262    dec = cipher.decryptor()
263    pt = dec.update(ct) + dec.finalize()
264    assert pt == data
265