Frequency Analysis
Break monoalphabetic substitution ciphers using letter frequency statistics.
Frequency Analysis
Frequency analysis counts how often each letter appears in a ciphertext and compares those counts to the expected frequency of letters in natural language. It's the primary attack against monoalphabetic substitution ciphers and the first mathematical tool in cryptanalysis history.
Why It Works
In English, letter frequencies are remarkably stable across large texts:
E: 12.7% T: 9.1% A: 8.2% O: 7.5% I: 7.0%
N: 6.7% S: 6.3% H: 6.1% R: 6.0% D: 4.3%
L: 4.0% C: 2.8% U: 2.8% M: 2.4% W: 2.4%
F: 2.2% G: 2.0% Y: 2.0% P: 1.9% B: 1.5%
V: 1.0% K: 0.8% J: 0.2% X: 0.2% Q: 0.1% Z: 0.1%Key insight: In a monoalphabetic substitution, each letter maps to exactly one other letter. The most frequent letter in the ciphertext likely represents E, the second most frequent likely represents T, and so on.
Index of Coincidence (IC)
The IC measures whether a text has letter-frequency patterns consistent with natural language. It's used to identify the cipher type before breaking it.
Step-by-Step Attack
Count Frequencies
from collections import Counter
ciphertext = "KHOOR ZRUOG WKLV LV D WHVW"
clean = ''.join(c for c in ciphertext.upper() if c.isalpha())
freq = Counter(clean)
total = len(clean)
print(f"{'Letter':8} {'Count':6} {'Freq%':8}")
print("-" * 24)
for letter, count in sorted(freq.items(), key=lambda x: -x[1]):
print(f" {letter} {count:4d} {count/total*100:5.1f}%")Compare to English
english_freq_order = "ETAOINSHRDLCUMWFGYPBVKJXQZ"
cipher_freq_order = ''.join(l for l, _ in sorted(freq.items(), key=lambda x: -x[1]))
print(f"English: {english_freq_order}")
print(f"Ciphertext: {cipher_freq_order}")
# Most frequent in cipher → likely 'E'
# Second most frequent → likely 'T'Build and Refine Mapping
# Start with frequency-based guesses
mapping = {}
for cipher_char, eng_char in zip(cipher_freq_order, english_freq_order):
mapping[cipher_char] = eng_char
# Apply mapping
def apply_mapping(text, mapping):
return ''.join(mapping.get(c, '_') if c.isalpha() else c for c in text.upper())
decrypted = apply_mapping(ciphertext, mapping)
print(decrypted)
# Refine: look at common words, digrams, trigrams
# Update mapping interactively until it makes sense
mapping['K'] = 'H' # manual refinement
mapping['O'] = 'E'
mapping['R'] = 'L'Use Pattern Words
Single-letter words: A or I
Two-letter words: IT, IN, IS, AN, OR, TO, OF, AT, BE, WE, HE
Three-letter words: THE (most common!), AND, FOR, ARE, BUT, NOT
Common digrams: TH, HE, IN, ER, AN, RE, ON, EN
Common trigrams: THE, AND, ING, ION, ENT, FOR# Find candidate single-letter words
words = ciphertext.split()
single = [w for w in words if len(w) == 1]
print("Single-letter words:", set(single))
# If "D" appears as a single letter → D probably means "a" or "i"Automated Tools
For CTF, these are the fastest approaches:
quipqiup.com (Best Automated Solver)
Go to quipqiup.com
Navigate to https://quipqiup.com.
Paste ciphertext
Paste your ciphertext into the text box.
Click Solve
Click "Solve" - this tries all possible substitution mappings.
Read Output
Usually gives readable English in seconds for long enough ciphertext.
dcode.fr
Paste ciphertext
Paste your ciphertext.
Analyze Chart
See the visual frequency chart and explore the auto-solve options.
CyberChef
Open CyberChef
Navigate to CyberChef.
Add Operation
Search for and add the Frequency Distribution operation.
View Results
This will generate and show letter frequencies.
Python Full Implementation
Caesar Cipher (Special Case of Substitution)
Caesar is monoalphabetic with a fixed shift - only 25 possible keys:
def caesar_brute_force(ciphertext):
for shift in range(1, 26):
decrypted = ''
for c in ciphertext:
if c.isalpha():
base = ord('A') if c.isupper() else ord('a')
decrypted += chr((ord(c) - base - shift) % 26 + base)
else:
decrypted += c
print(f"Shift {shift:2d}: {decrypted}")
caesar_brute_force("KHOOR ZRUOG")
# Shift 3: HELLO WORLD ← readable!Quick Cipher Identification Reference
| Observation | Likely cipher |
|---|---|
| All letters shifted by the same amount | Caesar (26 shifts) |
| Letters scrambled, same frequency profile | Monoalphabetic substitution |
| Monoalphabetic (frequency analysis works) | |
| – | Vigenère (Kasiski / Friedman) |
| One-time pad or strong modern cipher | |
| Symbols instead of letters | Pigpen, Morse, etc. |
| Numbers as A–Z mapping | A=1, B=2 style encoding |
| Uppercase only, spaces stripped | Common CTF setup |
Checklist
- Calculate : → frequency analysis; → Vigenère
- Count frequencies: sort by count, compare to E,T,A,O,I...
- Try quipqiup.com first - solves most English text automatically
- dcode.fr/monoalphabetic-substitution for manual + automated
- Identify single-letter words (A or I), short words (THE, AND)
- Use digrams/trigrams to resolve ambiguous mappings
- For Caesar specifically: brute force all 25 shifts
- ROT13? echo "text" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
- Polish until plaintext is fully readable English
Practice Challenges
- Cryptopals Set 1 Challenge 3 - single-byte XOR (same technique)
- CryptoHack - Encoding section
- Beginner-friendly crypto challenge sets and exercises
- dcode.fr exercises
Last updated on