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.exceptions import AlreadyFinalized, _Reasons 13from cryptography.hazmat.backends.interfaces import CipherBackend 14from cryptography.hazmat.primitives import ciphers 15from cryptography.hazmat.primitives.ciphers import modes 16from cryptography.hazmat.primitives.ciphers.algorithms import ( 17 AES, 18 ARC4, 19 Blowfish, 20 CAST5, 21 Camellia, 22 IDEA, 23 SEED, 24 TripleDES, 25) 26 27from ...utils import ( 28 load_nist_vectors, 29 load_vectors_from_file, 30 raises_unsupported_algorithm, 31) 32 33 34class TestAES(object): 35 @pytest.mark.parametrize( 36 ("key", "keysize"), 37 [(b"0" * 32, 128), (b"0" * 48, 192), (b"0" * 64, 256)], 38 ) 39 def test_key_size(self, key, keysize): 40 cipher = AES(binascii.unhexlify(key)) 41 assert cipher.key_size == keysize 42 43 def test_invalid_key_size(self): 44 with pytest.raises(ValueError): 45 AES(binascii.unhexlify(b"0" * 12)) 46 47 def test_invalid_key_type(self): 48 with pytest.raises(TypeError, match="key must be bytes"): 49 AES(u"0" * 32) 50 51 52class TestAESXTS(object): 53 @pytest.mark.requires_backend_interface(interface=CipherBackend) 54 @pytest.mark.parametrize( 55 "mode", (modes.CBC, modes.CTR, modes.CFB, modes.CFB8, modes.OFB) 56 ) 57 def test_invalid_key_size_with_mode(self, mode, backend): 58 with pytest.raises(ValueError): 59 ciphers.Cipher(AES(b"0" * 64), mode(b"0" * 16), backend) 60 61 def test_xts_tweak_not_bytes(self): 62 with pytest.raises(TypeError): 63 modes.XTS(32) 64 65 def test_xts_tweak_too_small(self): 66 with pytest.raises(ValueError): 67 modes.XTS(b"0") 68 69 @pytest.mark.requires_backend_interface(interface=CipherBackend) 70 def test_xts_wrong_key_size(self, backend): 71 with pytest.raises(ValueError): 72 ciphers.Cipher(AES(b"0" * 16), modes.XTS(b"0" * 16), backend) 73 74 75class TestGCM(object): 76 @pytest.mark.parametrize("size", [7, 129]) 77 def test_gcm_min_max(self, size): 78 with pytest.raises(ValueError): 79 modes.GCM(b"0" * size) 80 81 82class TestCamellia(object): 83 @pytest.mark.parametrize( 84 ("key", "keysize"), 85 [(b"0" * 32, 128), (b"0" * 48, 192), (b"0" * 64, 256)], 86 ) 87 def test_key_size(self, key, keysize): 88 cipher = Camellia(binascii.unhexlify(key)) 89 assert cipher.key_size == keysize 90 91 def test_invalid_key_size(self): 92 with pytest.raises(ValueError): 93 Camellia(binascii.unhexlify(b"0" * 12)) 94 95 def test_invalid_key_type(self): 96 with pytest.raises(TypeError, match="key must be bytes"): 97 Camellia(u"0" * 32) 98 99 100class TestTripleDES(object): 101 @pytest.mark.parametrize("key", [b"0" * 16, b"0" * 32, b"0" * 48]) 102 def test_key_size(self, key): 103 cipher = TripleDES(binascii.unhexlify(key)) 104 assert cipher.key_size == 192 105 106 def test_invalid_key_size(self): 107 with pytest.raises(ValueError): 108 TripleDES(binascii.unhexlify(b"0" * 12)) 109 110 def test_invalid_key_type(self): 111 with pytest.raises(TypeError, match="key must be bytes"): 112 TripleDES(u"0" * 16) 113 114 115class TestBlowfish(object): 116 @pytest.mark.parametrize( 117 ("key", "keysize"), 118 [(b"0" * (keysize // 4), keysize) for keysize in range(32, 449, 8)], 119 ) 120 def test_key_size(self, key, keysize): 121 cipher = Blowfish(binascii.unhexlify(key)) 122 assert cipher.key_size == keysize 123 124 def test_invalid_key_size(self): 125 with pytest.raises(ValueError): 126 Blowfish(binascii.unhexlify(b"0" * 6)) 127 128 def test_invalid_key_type(self): 129 with pytest.raises(TypeError, match="key must be bytes"): 130 Blowfish(u"0" * 8) 131 132 133class TestCAST5(object): 134 @pytest.mark.parametrize( 135 ("key", "keysize"), 136 [(b"0" * (keysize // 4), keysize) for keysize in range(40, 129, 8)], 137 ) 138 def test_key_size(self, key, keysize): 139 cipher = CAST5(binascii.unhexlify(key)) 140 assert cipher.key_size == keysize 141 142 def test_invalid_key_size(self): 143 with pytest.raises(ValueError): 144 CAST5(binascii.unhexlify(b"0" * 34)) 145 146 def test_invalid_key_type(self): 147 with pytest.raises(TypeError, match="key must be bytes"): 148 CAST5(u"0" * 10) 149 150 151class TestARC4(object): 152 @pytest.mark.parametrize( 153 ("key", "keysize"), 154 [ 155 (b"0" * 10, 40), 156 (b"0" * 14, 56), 157 (b"0" * 16, 64), 158 (b"0" * 20, 80), 159 (b"0" * 32, 128), 160 (b"0" * 48, 192), 161 (b"0" * 64, 256), 162 ], 163 ) 164 def test_key_size(self, key, keysize): 165 cipher = ARC4(binascii.unhexlify(key)) 166 assert cipher.key_size == keysize 167 168 def test_invalid_key_size(self): 169 with pytest.raises(ValueError): 170 ARC4(binascii.unhexlify(b"0" * 34)) 171 172 def test_invalid_key_type(self): 173 with pytest.raises(TypeError, match="key must be bytes"): 174 ARC4(u"0" * 10) 175 176 177class TestIDEA(object): 178 def test_key_size(self): 179 cipher = IDEA(b"\x00" * 16) 180 assert cipher.key_size == 128 181 182 def test_invalid_key_size(self): 183 with pytest.raises(ValueError): 184 IDEA(b"\x00" * 17) 185 186 def test_invalid_key_type(self): 187 with pytest.raises(TypeError, match="key must be bytes"): 188 IDEA(u"0" * 16) 189 190 191class TestSEED(object): 192 def test_key_size(self): 193 cipher = SEED(b"\x00" * 16) 194 assert cipher.key_size == 128 195 196 def test_invalid_key_size(self): 197 with pytest.raises(ValueError): 198 SEED(b"\x00" * 17) 199 200 def test_invalid_key_type(self): 201 with pytest.raises(TypeError, match="key must be bytes"): 202 SEED(u"0" * 16) 203 204 205def test_invalid_backend(): 206 pretend_backend = object() 207 208 with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): 209 ciphers.Cipher(AES(b"AAAAAAAAAAAAAAAA"), modes.ECB, pretend_backend) 210 211 212@pytest.mark.supported( 213 only_if=lambda backend: backend.cipher_supported( 214 AES(b"\x00" * 16), modes.ECB() 215 ), 216 skip_message="Does not support AES ECB", 217) 218@pytest.mark.requires_backend_interface(interface=CipherBackend) 219class TestCipherUpdateInto(object): 220 @pytest.mark.parametrize( 221 "params", 222 load_vectors_from_file( 223 os.path.join("ciphers", "AES", "ECB", "ECBGFSbox128.rsp"), 224 load_nist_vectors, 225 ), 226 ) 227 def test_update_into(self, params, backend): 228 key = binascii.unhexlify(params["key"]) 229 pt = binascii.unhexlify(params["plaintext"]) 230 ct = binascii.unhexlify(params["ciphertext"]) 231 c = ciphers.Cipher(AES(key), modes.ECB(), backend) 232 encryptor = c.encryptor() 233 buf = bytearray(len(pt) + 15) 234 res = encryptor.update_into(pt, buf) 235 assert res == len(pt) 236 assert bytes(buf)[:res] == ct 237 238 @pytest.mark.supported( 239 only_if=lambda backend: backend.cipher_supported( 240 AES(b"\x00" * 16), modes.GCM(b"0" * 12) 241 ), 242 skip_message="Does not support AES GCM", 243 ) 244 def test_update_into_gcm(self, backend): 245 key = binascii.unhexlify(b"e98b72a9881a84ca6b76e0f43e68647a") 246 iv = binascii.unhexlify(b"8b23299fde174053f3d652ba") 247 ct = binascii.unhexlify(b"5a3c1cf1985dbb8bed818036fdd5ab42") 248 pt = binascii.unhexlify(b"28286a321293253c3e0aa2704a278032") 249 c = ciphers.Cipher(AES(key), modes.GCM(iv), backend) 250 encryptor = c.encryptor() 251 buf = bytearray(len(pt) + 15) 252 res = encryptor.update_into(pt, buf) 253 assert res == len(pt) 254 assert bytes(buf)[:res] == ct 255 encryptor.finalize() 256 c = ciphers.Cipher(AES(key), modes.GCM(iv, encryptor.tag), backend) 257 decryptor = c.decryptor() 258 res = decryptor.update_into(ct, buf) 259 decryptor.finalize() 260 assert res == len(pt) 261 assert bytes(buf)[:res] == pt 262 263 @pytest.mark.supported( 264 only_if=lambda backend: backend.cipher_supported( 265 AES(b"\x00" * 16), modes.GCM(b"0" * 12) 266 ), 267 skip_message="Does not support AES GCM", 268 ) 269 def test_finalize_with_tag_already_finalized(self, backend): 270 key = binascii.unhexlify(b"e98b72a9881a84ca6b76e0f43e68647a") 271 iv = binascii.unhexlify(b"8b23299fde174053f3d652ba") 272 encryptor = ciphers.Cipher( 273 AES(key), modes.GCM(iv), backend 274 ).encryptor() 275 ciphertext = encryptor.update(b"abc") + encryptor.finalize() 276 277 decryptor = ciphers.Cipher( 278 AES(key), modes.GCM(iv, tag=encryptor.tag), backend 279 ).decryptor() 280 decryptor.update(ciphertext) 281 decryptor.finalize() 282 with pytest.raises(AlreadyFinalized): 283 decryptor.finalize_with_tag(encryptor.tag) 284 285 @pytest.mark.parametrize( 286 "params", 287 load_vectors_from_file( 288 os.path.join("ciphers", "AES", "ECB", "ECBGFSbox128.rsp"), 289 load_nist_vectors, 290 ), 291 ) 292 def test_update_into_multiple_calls(self, params, backend): 293 key = binascii.unhexlify(params["key"]) 294 pt = binascii.unhexlify(params["plaintext"]) 295 ct = binascii.unhexlify(params["ciphertext"]) 296 c = ciphers.Cipher(AES(key), modes.ECB(), backend) 297 encryptor = c.encryptor() 298 buf = bytearray(len(pt) + 15) 299 res = encryptor.update_into(pt[:3], buf) 300 assert res == 0 301 res = encryptor.update_into(pt[3:], buf) 302 assert res == len(pt) 303 assert bytes(buf)[:res] == ct 304 305 def test_update_into_buffer_too_small(self, backend): 306 key = b"\x00" * 16 307 c = ciphers.Cipher(AES(key), modes.ECB(), backend) 308 encryptor = c.encryptor() 309 buf = bytearray(16) 310 with pytest.raises(ValueError): 311 encryptor.update_into(b"testing", buf) 312 313 @pytest.mark.supported( 314 only_if=lambda backend: backend.cipher_supported( 315 AES(b"\x00" * 16), modes.GCM(b"\x00" * 12) 316 ), 317 skip_message="Does not support AES GCM", 318 ) 319 def test_update_into_buffer_too_small_gcm(self, backend): 320 key = b"\x00" * 16 321 c = ciphers.Cipher(AES(key), modes.GCM(b"\x00" * 12), backend) 322 encryptor = c.encryptor() 323 buf = bytearray(5) 324 with pytest.raises(ValueError): 325 encryptor.update_into(b"testing", buf) 326 327 def test_update_into_auto_chunking(self, backend, monkeypatch): 328 key = b"\x00" * 16 329 c = ciphers.Cipher(AES(key), modes.ECB(), backend) 330 encryptor = c.encryptor() 331 # Lower max chunk size so we can test chunking 332 monkeypatch.setattr(encryptor._ctx, "_MAX_CHUNK_SIZE", 40) 333 buf = bytearray(527) 334 pt = b"abcdefghijklmnopqrstuvwxyz012345" * 16 # 512 bytes 335 processed = encryptor.update_into(pt, buf) 336 assert processed == 512 337 decryptor = c.decryptor() 338 # Change max chunk size to verify alternate boundaries don't matter 339 monkeypatch.setattr(decryptor._ctx, "_MAX_CHUNK_SIZE", 73) 340 decbuf = bytearray(527) 341 decprocessed = decryptor.update_into(buf[:processed], decbuf) 342 assert decbuf[:decprocessed] == pt 343 344 def test_max_chunk_size_fits_in_int32(self, backend): 345 # max chunk must fit in signed int32 or else a call large enough to 346 # cause chunking will result in the very OverflowError we want to 347 # avoid with chunking. 348 key = b"\x00" * 16 349 c = ciphers.Cipher(AES(key), modes.ECB(), backend) 350 encryptor = c.encryptor() 351 backend._ffi.new("int *", encryptor._ctx._MAX_CHUNK_SIZE) 352