File size: 1,913 Bytes
cada743
 
 
 
 
 
 
a935848
 
cada743
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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,
    }