def break_repeating_key_xor(ciphertext, max_size): rated = [(size, _rate_repeating_xor_keysize(ciphertext, size)) for size in range(1, max_size)] candidate_pairs = sorted(rated, key=operator.itemgetter(1)) candidates_sizes = [pair[0] for pair in candidate_pairs] candidate_keys = [_guess_xor_key_for_given_size(ciphertext, size) for size in candidates_sizes] candidate__decryptions = ( (key, xor_buffers(key, ciphertext)) for key in candidate_keys) def score(candidate): plaintext = candidate[1] return english_distance(plaintext) return find_minimal(candidate__decryptions, score)
def find_single_byte_xor(haystack): """ Find and decrypt single-byte xor ciphertext :param haystack: a collection of bytes objects one of which is an UTF-8 encoded English text encrypte with single-byte xor using an unknown key byte :return: The decrypted text most similar to English or none if none was found """ candidate_decryptions = (break_single_byte_xor(blob) for blob in haystack) def score(decryption): return english_distance(decryption[1]) return find_minimal(candidate_decryptions, score)
def break_single_byte_xor(encrypted): """ Brute-force single-byte xor :param: encrypted UTF-8 encoded english text encrypted with single byte xor using an unknown key byte :return: A pair of the key leading to the best decryption based on similarity to English and that decryption. None if no decryption could be found. """ keys = (byte_from_int(ii) for ii in range(256)) def add_decryption(key): return key, xor_buffers(key, encrypted) candidates = (add_decryption(key) for key in keys) def score(candidate): return english_distance(candidate[1]) return find_minimal(candidates, score)
def test_first_element_if_equal(self): assert util.find_minimal([5, 4, 3, 2, 1], lambda x: 42) == 5
def test_none_in_criterion_gives_none(self): assert util.find_minimal([1, 2, 3], lambda x: None) is None
def test_nones(self): assert util.find_minimal([None, None, None], lambda x: 42) is None
def test_empty_gen(self): assert util.find_minimal([], lambda x: 42) is None
def test_second_arg_must_be_callable(self): with pytest.raises(TypeError): util.find_minimal([1, 2, 3], 42)
def test_first_arg_must_be_gen(self): with pytest.raises(TypeError): util.find_minimal(42, lambda x: x)
def test_square_sign(self): assert util.find_minimal([-2, 1, 2], lambda x: x * x) == 1
def test_trivial_case(self): assert util.find_minimal([3, 1, 2], lambda x: x) == 1