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