Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Cracking Vigenère Ciphers with Python: A Hands-On Cryptography Tutorial

Learn to implement Vigenère encryption, decryption, and dictionary attacks in Python. Perfect for CS6035 Fall 2025 students and anyone wanting to understand symmetric-key cryptography through practical coding.

Vigenère cipher Python CS6035 cryptography project symmetric key encryption tutorial dictionary attack Python crack Vigenère cipher Python encryption decryption cryptography assignment help Vigenere square implementation cybersecurity coding practice cryptanalysis for beginners Python cryptography library common word key attack Vigenère cipher example fall 2025 CS6035 Python unit test cryptography brute force dictionary attack

Introduction: Why Vigenère Still Matters in 2026

In an era of quantum-resistant algorithms and AI-generated encryption, the Vigenère cipher might seem like ancient history. Yet this 16th-century technique remains the perfect teaching tool for understanding symmetric-key cryptography—the foundation of modern protocols like AES. As you work through the CS6035 Cryptography project this fall, mastering Vigenère will give you hands-on experience with key expansion, modular arithmetic, and frequency analysis. Plus, the same pattern-recognition skills you'll use to crack a Vigenère cipher are exactly what cybersecurity professionals use to spot weaknesses in today's apps and networks. Let's build your own encrypt/decrypt functions and then break a real cipher using a dictionary attack.

Understanding the Vigenère Cipher

The Vigenère cipher encrypts text by applying a keyword that shifts each letter by a different amount. It's like a password manager that uses a master password to generate unique passwords for each site—except here the master password is your keyword, and each letter position gets a shift based on that keyword's letters.

How Encryption Works

Imagine your message is GEORGIA and your key is TECH. First, extend the key to match the message length: TECHTEC. Then, for each letter pair (message, key), find the cipher letter using a Vigenère square (or simply add their alphabetical indices modulo 26). For example:

  • G (6) + T (19) = 25 → Z
  • E (4) + E (4) = 8 → I
  • O (14) + C (2) = 16 → Q
  • And so on...

Result: ZIQYZMC. This is exactly what your vigenere_encrypt_message function will compute.

How Decryption Works

To decrypt, reverse the process: cipher letter minus key letter (mod 26). For Z (25) minus T (19) = 6 → G. Your vigenere_decrypt_cipher function will do this for each letter, ignoring spaces and punctuation.

Implementing Encryption and Decryption in Python

Your starter code provides empty functions. Let's fill them in step by step.

Step 1: Clean the Input

Remove spaces and punctuation, convert to uppercase. Python's str.isalpha() helps filter.

def clean_text(text):
    return ''.join(ch.upper() for ch in text if ch.isalpha())

Step 2: Extend the Keyword

Repeat the keyword to match the cleaned message length.

def extend_keyword(keyword, length):
    keyword = keyword.upper()
    return (keyword * (length // len(keyword) + 1))[:length]

Step 3: Encrypt Function

Use ord('A') as base. For each pair (m_char, k_char), compute (ord(m_char) - 65 + ord(k_char) - 65) % 26 + 65 and convert back to char.

def vigenere_encrypt_message(m, keyword):
    m_clean = clean_text(m)
    key = extend_keyword(keyword, len(m_clean))
    cipher = ''
    for i in range(len(m_clean)):
        shift = (ord(m_clean[i]) - 65 + ord(key[i]) - 65) % 26
        cipher += chr(shift + 65)
    return cipher

Step 4: Decrypt Function

Subtract instead of add.

def vigenere_decrypt_cipher(c, keyword):
    c_clean = clean_text(c)
    key = extend_keyword(keyword, len(c_clean))
    msg = ''
    for i in range(len(c_clean)):
        shift = (ord(c_clean[i]) - 65 - (ord(key[i]) - 65)) % 26
        msg += chr(shift + 65)
    return msg

Test with the example: encrypt 'GEORGIA' with 'TECH' should give 'ZIQYZMC', and decrypting that should return 'GEORGIA'.

Task 3: Breaking the Cipher with a Dictionary Attack

Now for the fun part: you're given a ciphertext and a list of common English words. One of those words was used as the key. Your job: try each word as a key, decrypt the cipher, and check if the result contains only valid dictionary words. Since the original message also uses words from the same list, you can verify by splitting the decrypted text into words and checking if each exists in the dictionary list.

Implementation Strategy

  1. Load the dictionary words into a set for O(1) lookup.
  2. For each candidate key, call vigenere_decrypt_cipher.
  3. Split the decrypted message into words (by space or punctuation).
  4. If every word is in the dictionary set, you've found the key!
def crack_vigenere(cipher, dictionary):
    word_set = set(dictionary)
    for key in dictionary:
        decrypted = vigenere_decrypt_cipher(cipher, key)
        words = decrypted.split()  # adjust if needed
        if all(w.upper() in word_set for w in words):
            return key, decrypted
    return None, None

This brute-force approach works because the key space is limited to common words—exactly why using simple words as keys is insecure. In real-world cryptography, keys must be high-entropy random strings.

Why This Matters in 2026: From Crypto to AI Safety

Understanding classic ciphers isn't just academic. The same pattern-matching logic powers modern AI models that detect phishing emails or analyze network traffic. When you crack a Vigenère cipher, you're training your brain to spot statistical anomalies—a skill that's invaluable for careers in cybersecurity, data science, and even AI safety research. Plus, with the rise of quantum computing, hybrid encryption schemes are becoming standard, and knowing the fundamentals helps you appreciate the trade-offs in algorithm design.

Common Pitfalls and Debugging Tips

  • Case sensitivity: Always convert to uppercase before processing.
  • Non-alphabetic characters: Preserve spaces? The project likely expects you to ignore them. Check the unit tests.
  • Keyword length: Ensure your key extension doesn't exceed the message length.
  • Modulo operation: Python's % works correctly for negative numbers, but double-check when subtracting for decryption.

Testing Your Code Locally

Use the provided unit tests. Run python -m unittest test_task_vigenere to see if you pass. Remember: passing locally doesn't guarantee the autograder will accept it—edge cases matter. Test with different keywords and messages, including those with spaces and punctuation.

Conclusion: You've Just Built a Crypto Tool

By implementing Vigenère encryption, decryption, and a dictionary attack, you've gained practical skills that extend far beyond this assignment. You now understand symmetric-key weaknesses, the importance of key entropy, and how to automate cryptanalysis. As you move on to RSA and other tasks in the CS6035 project, remember that every algorithm has its vulnerabilities—and Python gives you the power to explore them.