Spaces:
Running
Running
| from collections import Counter | |
| FREQ = { | |
| "a": 8.167, "b": 1.492, "c": 2.782, "d": 4.253, "e": 12.702, | |
| "f": 2.228, "g": 2.015, "h": 6.094, "i": 6.966, "j": 0.153, | |
| "k": 0.772, "l": 4.025, "m": 2.406, "n": 6.749, "o": 7.507, | |
| "p": 1.929, "q": 0.095, "r": 5.987, "s": 6.327, "t": 9.056, | |
| "u": 2.758, "v": 0.978, "w": 2.360, | |
| "x": 0.150, "y": 1.974, "z": 0.074, | |
| } | |
| def caesar_decrypt(text: str, key: int) -> str: | |
| return "".join( | |
| chr((ord(c) - 65 - key) % 26 + 65) if c.isascii() and c.isupper() | |
| else chr((ord(c) - 97 - key) % 26 + 97) if c.isascii() and c.islower() | |
| else c | |
| for c in text | |
| ) | |
| def chi_squared(text: str) -> float: | |
| letters = [c.lower() for c in text if c.isascii() and c.isalpha()] | |
| n = len(letters) | |
| if not n: | |
| return float("inf") | |
| count = Counter(letters) | |
| return sum( | |
| ((count.get(c, 0) - (FREQ[c] / 100 * n)) ** 2) / (FREQ[c] / 100 * n) | |
| for c in FREQ | |
| ) | |
| def caesar_attack(text: str, progress_callback=None) -> dict: | |
| import time | |
| if progress_callback: | |
| progress_callback(0, 260, "Starting attack...") | |
| results = [] | |
| for k in range(26): | |
| if progress_callback: | |
| for step in range(10): | |
| progress_callback((k * 10) + step, 260, f"Testing shift {k} ({step * 10}%)...") | |
| time.sleep(0.005) | |
| score = chi_squared(caesar_decrypt(text, k)) | |
| plaintext = caesar_decrypt(text, k) | |
| results.append((k, score, plaintext)) | |
| if progress_callback: | |
| progress_callback((k * 10) + 10, 260, f"Testing shift {k} (100%)...") | |
| results.sort(key=lambda x: x[1]) | |
| best_key, best_score, best_plaintext = results[0] | |
| if progress_callback: | |
| progress_callback(260, 260, "Complete") | |
| return { | |
| "guessed_key": best_key, | |
| "guessed_plaintext": best_plaintext, | |
| } | |