• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
8
9import pytest
10
11from cryptography.exceptions import (
12    AlreadyFinalized, _Reasons
13)
14from cryptography.hazmat.backends.interfaces import CipherBackend
15from cryptography.hazmat.primitives.ciphers import (
16    Cipher, algorithms, base, modes
17)
18
19from .utils import (
20    generate_aead_exception_test, generate_aead_tag_exception_test
21)
22from ...doubles import DummyCipherAlgorithm, DummyMode
23from ...utils import raises_unsupported_algorithm
24
25
26@pytest.mark.requires_backend_interface(interface=CipherBackend)
27class TestCipher(object):
28    def test_creates_encryptor(self, backend):
29        cipher = Cipher(
30            algorithms.AES(binascii.unhexlify(b"0" * 32)),
31            modes.CBC(binascii.unhexlify(b"0" * 32)),
32            backend
33        )
34        assert isinstance(cipher.encryptor(), base.CipherContext)
35
36    def test_creates_decryptor(self, backend):
37        cipher = Cipher(
38            algorithms.AES(binascii.unhexlify(b"0" * 32)),
39            modes.CBC(binascii.unhexlify(b"0" * 32)),
40            backend
41        )
42        assert isinstance(cipher.decryptor(), base.CipherContext)
43
44    def test_instantiate_with_non_algorithm(self, backend):
45        algorithm = object()
46        with pytest.raises(TypeError):
47            Cipher(algorithm, mode=None, backend=backend)
48
49
50@pytest.mark.requires_backend_interface(interface=CipherBackend)
51class TestCipherContext(object):
52    def test_use_after_finalize(self, backend):
53        cipher = Cipher(
54            algorithms.AES(binascii.unhexlify(b"0" * 32)),
55            modes.CBC(binascii.unhexlify(b"0" * 32)),
56            backend
57        )
58        encryptor = cipher.encryptor()
59        encryptor.update(b"a" * 16)
60        encryptor.finalize()
61        with pytest.raises(AlreadyFinalized):
62            encryptor.update(b"b" * 16)
63        with pytest.raises(AlreadyFinalized):
64            encryptor.finalize()
65        decryptor = cipher.decryptor()
66        decryptor.update(b"a" * 16)
67        decryptor.finalize()
68        with pytest.raises(AlreadyFinalized):
69            decryptor.update(b"b" * 16)
70        with pytest.raises(AlreadyFinalized):
71            decryptor.finalize()
72
73    def test_use_update_into_after_finalize(self, backend):
74        cipher = Cipher(
75            algorithms.AES(binascii.unhexlify(b"0" * 32)),
76            modes.CBC(binascii.unhexlify(b"0" * 32)),
77            backend
78        )
79        encryptor = cipher.encryptor()
80        encryptor.update(b"a" * 16)
81        encryptor.finalize()
82        with pytest.raises(AlreadyFinalized):
83            buf = bytearray(31)
84            encryptor.update_into(b"b" * 16, buf)
85
86    def test_unaligned_block_encryption(self, backend):
87        cipher = Cipher(
88            algorithms.AES(binascii.unhexlify(b"0" * 32)),
89            modes.ECB(),
90            backend
91        )
92        encryptor = cipher.encryptor()
93        ct = encryptor.update(b"a" * 15)
94        assert ct == b""
95        ct += encryptor.update(b"a" * 65)
96        assert len(ct) == 80
97        ct += encryptor.finalize()
98        decryptor = cipher.decryptor()
99        pt = decryptor.update(ct[:3])
100        assert pt == b""
101        pt += decryptor.update(ct[3:])
102        assert len(pt) == 80
103        assert pt == b"a" * 80
104        decryptor.finalize()
105
106    @pytest.mark.parametrize("mode", [DummyMode(), None])
107    def test_nonexistent_cipher(self, backend, mode):
108        cipher = Cipher(
109            DummyCipherAlgorithm(), mode, backend
110        )
111        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
112            cipher.encryptor()
113
114        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
115            cipher.decryptor()
116
117    def test_incorrectly_padded(self, backend):
118        cipher = Cipher(
119            algorithms.AES(b"\x00" * 16),
120            modes.CBC(b"\x00" * 16),
121            backend
122        )
123        encryptor = cipher.encryptor()
124        encryptor.update(b"1")
125        with pytest.raises(ValueError):
126            encryptor.finalize()
127
128        decryptor = cipher.decryptor()
129        decryptor.update(b"1")
130        with pytest.raises(ValueError):
131            decryptor.finalize()
132
133
134@pytest.mark.supported(
135    only_if=lambda backend: backend.cipher_supported(
136        algorithms.AES(b"\x00" * 16), modes.GCM(b"\x00" * 12)
137    ),
138    skip_message="Does not support AES GCM",
139)
140@pytest.mark.requires_backend_interface(interface=CipherBackend)
141class TestAEADCipherContext(object):
142    test_aead_exceptions = generate_aead_exception_test(
143        algorithms.AES,
144        modes.GCM,
145    )
146    test_aead_tag_exceptions = generate_aead_tag_exception_test(
147        algorithms.AES,
148        modes.GCM,
149    )
150
151
152@pytest.mark.requires_backend_interface(interface=CipherBackend)
153class TestModeValidation(object):
154    def test_cbc(self, backend):
155        with pytest.raises(ValueError):
156            Cipher(
157                algorithms.AES(b"\x00" * 16),
158                modes.CBC(b"abc"),
159                backend,
160            )
161
162    def test_ofb(self, backend):
163        with pytest.raises(ValueError):
164            Cipher(
165                algorithms.AES(b"\x00" * 16),
166                modes.OFB(b"abc"),
167                backend,
168            )
169
170    def test_cfb(self, backend):
171        with pytest.raises(ValueError):
172            Cipher(
173                algorithms.AES(b"\x00" * 16),
174                modes.CFB(b"abc"),
175                backend,
176            )
177
178    def test_cfb8(self, backend):
179        with pytest.raises(ValueError):
180            Cipher(
181                algorithms.AES(b"\x00" * 16),
182                modes.CFB8(b"abc"),
183                backend,
184            )
185
186    def test_ctr(self, backend):
187        with pytest.raises(ValueError):
188            Cipher(
189                algorithms.AES(b"\x00" * 16),
190                modes.CTR(b"abc"),
191                backend,
192            )
193
194    def test_gcm(self):
195        with pytest.raises(ValueError):
196            modes.GCM(b"")
197
198
199class TestModesRequireBytes(object):
200    def test_cbc(self):
201        with pytest.raises(TypeError):
202            modes.CBC([1] * 16)
203
204    def test_cfb(self):
205        with pytest.raises(TypeError):
206            modes.CFB([1] * 16)
207
208    def test_cfb8(self):
209        with pytest.raises(TypeError):
210            modes.CFB8([1] * 16)
211
212    def test_ofb(self):
213        with pytest.raises(TypeError):
214            modes.OFB([1] * 16)
215
216    def test_ctr(self):
217        with pytest.raises(TypeError):
218            modes.CTR([1] * 16)
219
220    def test_gcm_iv(self):
221        with pytest.raises(TypeError):
222            modes.GCM([1] * 16)
223
224    def test_gcm_tag(self):
225        with pytest.raises(TypeError):
226            modes.GCM(b"\x00" * 16, [1] * 16)
227