Cybersecurity-Projects/PROJECTS/beginner/caesar-cipher/src/caesar_cipher/analyzer.py

76 lines
2.5 KiB
Python

"""
©AngelaMos | 2026
analyzer.py
Statistical frequency analysis for ranking Caesar cipher brute-force results
Provides the FrequencyAnalyzer class that scores decryption candidates
by comparing their letter distributions against expected English frequency
percentages using a chi-squared test. Lower scores indicate text that
more closely matches natural English.
Connects to:
constants.py - imports ENGLISH_LETTER_FREQUENCIES
main.py - crack command passes CaesarCipher.crack() output to rank_candidates()
"""
from collections import Counter
from caesar_cipher.constants import ENGLISH_LETTER_FREQUENCIES
class FrequencyAnalyzer:
"""
Analyzes text for English language patterns using letter frequency distribution
"""
def __init__(self) -> None:
"""
Initialize analyzer with English letter frequency reference data
"""
self.reference_frequencies = ENGLISH_LETTER_FREQUENCIES
def calculate_chi_squared(self, text: str) -> float:
"""
Calculate chi-squared statistic comparing text to expected English frequencies
"""
text_upper = text.upper()
letter_counts = Counter(char for char in text_upper if char.isalpha())
if not letter_counts:
return float("inf")
total_letters = sum(letter_counts.values())
chi_squared = 0.0
for letter, expected_freq in self.reference_frequencies.items():
observed_count = letter_counts.get(letter, 0)
expected_count = (expected_freq / 100) * total_letters
if expected_count > 0:
chi_squared += (
(observed_count - expected_count)**2
) / expected_count
return chi_squared
def score_text(self, text: str) -> float:
"""
Score text likelihood of being valid English (lower is better)
"""
return self.calculate_chi_squared(text)
def rank_candidates(self,
candidates: list[tuple[int,
str]]) -> list[tuple[int,
str,
float]]:
"""
Rank decryption candidates by their English frequency score
"""
scored = [
(shift,
text,
self.score_text(text)) for shift, text in candidates
]
return sorted(scored, key = lambda x: x[2])