예제 #1
0
파일: caesar.py 프로젝트: cinguva/lantern
def crack(ciphertext, *fitness_functions, min_key=0, max_key=26):
    """Break ``ciphertext`` by enumerating keys between ``min_key`` and ``max_key``.

    Example:
        >>> decryptions = crack("KHOOR", fitness.english.quadgrams)
        >>> print(decryptions[0])
        HELLO

    Args:
        ciphertext (str): The text to decrypt
        *fitness_functions (variable length argument list): Functions to score decryption with

    Keyword Args:
        min_key (int): Key to start with
        max_key (int): Key to stop at (exclusive)

    Returns:
        Sorted list of decryptions

    Raises:
        ValueError: If min_key exceeds max_key
        ValueError: If no fitness_functions are given
    """
    if min_key >= max_key:
        raise ValueError("min_key cannot exceed max_key")

    decryptions = []
    for key in range(min_key, max_key):
        plaintext = decrypt(key, ciphertext)
        decryptions.append(
            Decryption(plaintext, key, score(plaintext, *fitness_functions)))

    return sorted(decryptions, reverse=True)
예제 #2
0
 def next_node_inner_climb(node):
     # Swap 2 characters in the key
     a, b = random.sample(range(len(node)), 2)
     node[a], node[b] = node[b], node[a]
     plaintext = decrypt(node, ciphertext)
     node_score = score(plaintext, *fitness_functions)
     return node, node_score, Decryption(plaintext, ''.join(node),
                                         node_score)
예제 #3
0
def crack(ciphertext, *fitness_functions, key_period=None, max_key_period=30):
    """Break ``ciphertext`` by finding (or using the given) key_period then breaking ``key_period`` many Caesar ciphers.

    Example:
        >>> decryptions = crack("OMSTV", fitness.ChiSquared(analysis.frequency.english.unigrams))
        >>> print(decryptions[0])
        HELLO

    Args:
        ciphertext (str): The text to decrypt
        *fitness_functions (variable length argument list): Functions to score decryption with

    Keyword Args:
        key_period (int): The period of the key
        max_key_period (int): The maximum period the key could be

    Returns:
        Sorted list of decryptions

    Raises:
        ValueError: If key_period or max_key_period are less than or equal to 0
        ValueError: If no fitness_functions are given
    """
    if max_key_period <= 0 or (key_period is not None and key_period <= 0):
        raise ValueError("Period values must be positive integers")

    original_text = ciphertext
    # Make the assumption that non alphabet characters have not been encrypted
    # TODO: This is fairly poor code. Once languages are a thing, there should be some nice abstractions for this stuff
    ciphertext = remove(ciphertext, string.punctuation + string.whitespace + string.digits)
    periods = [int(key_period)] if key_period else key_periods(ciphertext, max_key_period)

    # Decrypt for every valid period
    period_decryptions = []
    for period in periods:
        if period >= len(ciphertext):
            continue

        # Collect the best decryptions for every column
        column_decryptions = [shift.crack(col, *fitness_functions)[0] for col in split_columns(ciphertext, period)]
        key = _build_key(decrypt.key for decrypt in column_decryptions)

        plaintext = decrypt(key, original_text)
        period_decryptions.append(Decryption(plaintext, key, score(plaintext, *fitness_functions)))

    return sorted(period_decryptions, reverse=True)
예제 #4
0
def test_score_with_single_function():
    """Single function accepted instead of iterable"""
    assert score("example", lambda _: 15) == 15
예제 #5
0
def test_score_invalid():
    """Score raises ValueError when given no scoring functions"""
    with pytest.raises(ValueError):
        score("example")
예제 #6
0
def test_score_is_averaged_positive_and_negative():
    """Score is averaged over number of functions used"""
    assert score("example", lambda _: -10, lambda _: 2) == -4