def decrypt(self, ciphertext: bytes, password: bytes, size: int, salt: bytes = None, iv: bytes = None): """ Processes arguments to an intermediate state, then passes to the _decrypt_ function. Processes output from the _decrypt_ function for use as output. :param ciphertext: bytes :param password: bytes :param size: int :param salt: bytes :param iv: bytes :return: bytes """ # Convert plaintext to a list of blocks # Each block is 16 GF objects blocks = self.to_blocks(ciphertext) if salt is None: # If salt is not given via argument, generate a random one salt = urandom(self.salt_size) # Generate an iterable that loops over the key schedule repeatedly key = iter_key([ GF(i) for i in phash(self.hash_algo, password, salt, self.hash_iters, size // 8) ], size, reverse=True) if iv is None: iv = [GF(i) for i in urandom(16)] elif len(iv) != 16: raise ValueError else: # convert iv to a list of GF objects iv = [GF(i) for i in iv] # select appropriate decryption key size # raise a ValueError if the given key size is invalid if size == 128: dec_func = decrypt_128 elif size == 192: dec_func = decrypt_192 elif size == 256: dec_func = decrypt_256 else: raise ValueError # Actually perform the decryption blocks = self._decrypt_(blocks, key, iv, dec_func) # Process and return newly decrypted blocks to a usable format return self.from_blocks(blocks)
def test_input_1(self): ciphertext = hex_to_arr(r'69c4e0d86a7b0430d8cdb78070b4c55a') key = hex_to_arr(r'000102030405060708090a0b0c0d0e0f') expected_out = hex_to_arr(r'00112233445566778899aabbccddeeff') test_out = Core.decrypt_128(ciphertext, iter_key(key, 128, reverse=True)) for expected, test in zip(expected_out, test_out): self.assertEqual(expected, test)
def test_input_1(self): ciphertext = hex_to_arr(r'8ea2b7ca516745bfeafc49904b496089') key = hex_to_arr(r'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f') expected_out = hex_to_arr(r'00112233445566778899aabbccddeeff') test_out = Core.decrypt_256(ciphertext, iter_key(key, 256, reverse=True)) for expected, test in zip(expected_out, test_out): self.assertEqual(expected, test)
def test_input_1(self): ciphertext = hex_to_arr(r'dda97ca4864cdfe06eaf70a0ec0d7191') key = hex_to_arr(r'000102030405060708090a0b0c0d0e0f1011121314151617') expected_out = hex_to_arr(r'00112233445566778899aabbccddeeff') test_out = Core.decrypt_192(ciphertext, iter_key(key, 192, reverse=True)) for expected, test in zip(expected_out, test_out): self.assertEqual(expected, test)
def encrypt(self, plaintext: bytes, password: bytes, size: int, salt: bytes = None, iv: bytes = None): """ Processes arguments to an intermediate state, then passes to the _encrypt_ function. Processes output from the _encrypt_ function for use as output. :param plaintext: bytes :param password: bytes :param size: int :param salt: bytes :param iv: bytes :return: bytes """ # Convert plaintext to a list of blocks # Each block is 16 GF objects blocks = self.to_blocks(plaintext) if salt is None: # If salt is not given via argument, generate a random one salt = urandom(self.salt_size) # Generate an iterable that loops over the key schedule repeatedly key = iter_key([ GF(i) for i in phash(self.hash_algo, password, salt, self.hash_iters, size // 8) ], size) if iv is None: # If iv is not given via argument, generate a random one iv = [GF(i) for i in urandom(16)] elif len(iv) != 16: # If iv is given via argument, make sure it's usable raise ValueError else: # convert iv to a list of GF objects iv = [GF(i) for i in iv] # select appropriate encryption key size # raise a ValueError if the given key size is invalid if size == 128: enc_func = encrypt_128 elif size == 192: enc_func = encrypt_192 elif size == 256: enc_func = encrypt_256 else: raise ValueError # Actually perform the encryption blocks, *outputs = self._encrypt_(blocks, key, iv, enc_func) # Process newly encrypted blocks to a usable format out = self.from_blocks(blocks) # If an IV is returned in the format of a list with GF elements # Convert it back to bytes without any stripping of trailing 0s if len(outputs): outputs = [self.from_blocks(outputs, False)] # Insert the salt into the outputs outputs.insert(0, salt) return (out, *outputs)
def test_variable_plaintext(self): for expected_plaintext, key, ciphertext in self.tests['Variable Plaintext']: with self.subTest(expected_plaintext=expected_plaintext, key=key, ciphertext=ciphertext): test_plaintext = Core.decrypt_128(hex_to_arr(ciphertext), iter_key(hex_to_arr(key), 128, reverse=True)) for e_item, t_item in zip(hex_to_arr(expected_plaintext), test_plaintext): self.assertEqual(e_item, t_item)
def test_variable_key(self): for plaintext, key, expected_ciphertext in self.tests['Variable Key']: with self.subTest(plaintext=plaintext, key=key, expected_ciphertext=expected_ciphertext): test_ciphertext = Core.encrypt_128(hex_to_arr(plaintext), iter_key(hex_to_arr(key), 128)) for e_item, t_item in zip(hex_to_arr(expected_ciphertext), test_ciphertext): self.assertEqual(e_item, t_item)