Code for How to Crack the Affine Cipher in Python Tutorial


View on Github

affine_cipher_decrypt.py

# Import the needed libraries.
import string
from colorama import Fore, init

# Initialise colorama.
init()


# Function to get Euclidean Algorithm.
def extended_gcd(a, b):
    """
    Extended Euclidean Algorithm to find the greatest common divisor
    and coefficients x, y such that ax + by = gcd(a, b).
    """
    if a == 0:
        return (b, 0, 1)
    else:
        g, x, y = extended_gcd(b % a, a)
        return (g, y - (b // a) * x, x)


# Function to get the modular Inverse
def modular_inverse(a, m):
    """
    Compute the modular multiplicative inverse of a modulo m.
    Raises an exception if the modular inverse does not exist.
    """
    g, x, y = extended_gcd(a, m)
    if g != 1:
        raise Exception('Modular inverse does not exist')
    else:
        return x % m


# Function to decrypt our message.
def affine_decrypt(ciphertext, a, b):
    """
    Decrypt a message encrypted with the Affine Cipher using
    the given key components a and b.
    """
    alphabet = string.ascii_uppercase
    m = len(alphabet)
    plaintext = ''

    # Compute the modular multiplicative inverse of a.
    a_inv = modular_inverse(a, m)

    # Iterate through each character in the ciphertext.
    for char in ciphertext:
        # Check if the character is in the alphabet
        if char in alphabet:
            # If it's an alphabet letter, decrypt it.
            # Find the index of the character in the alphabet.
            c = alphabet.index(char)
            # Apply the decryption formula: a_inv * (c - b) mod m.
            p = (a_inv * (c - b)) % m
            # Append the decrypted character to the plaintext.
            plaintext += alphabet[p]
        else:
            # If the character is not in the alphabet, keep it unchanged.
            plaintext += char

    # Return the decrypted plaintext.
    return plaintext


# Function to peform brute force attack.
def affine_brute_force(ciphertext):
    """
    Brute-force attack to find possible keys for an Affine Cipher
    and print potential decryptions for manual inspection.
    """
    alphabet = string.ascii_uppercase
    m = len(alphabet)

    # Iterate through possible values for a.
    for a in range(1, m):
        # Ensure a and m are coprime.
        if extended_gcd(a, m)[0] == 1:
            # Iterate through possible values for b.
            for b in range(0, m):
                # Decrypt using the current key.
                decrypted_text = affine_decrypt(ciphertext, a, b)

                # Print potential decryption for manual inspection.
                print(f"Key (a={a}, b={b}): {decrypted_text}")


ciphertext = input(f"{Fore.GREEN}[?] Enter Message to decrypt: ")

# Perform a brute-force attack to find potential decrypted message.
affine_brute_force(ciphertext)