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