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