Repository navigation
Expand file tree
/
Copy pathciphers.py
More file actions
181 lines (142 loc) · 6.62 KB
/
Copy pathciphers.py
File metadata and controls
181 lines (142 loc) · 6.62 KB
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# ciphers.py — Advanced cipher decryption for Text-Decryption
# Extends utils.py and Decryptor.py with modern and historical ciphers
import string
# ── Substitution Cipher ──────────────────────────────────────────────────────
def from_substitution(text, key):
"""
Decrypt a monoalphabetic substitution cipher.
key: 26-char string mapping ciphertext alphabet to plaintext.
Example: key = "QWERTYUIOPASDFGHJKLZXCVBNM"
"""
key = key.upper()
table = str.maketrans(key + key.lower(), string.ascii_uppercase + string.ascii_lowercase)
return text.translate(table)
# ── Playfair Cipher ──────────────────────────────────────────────────────────
def _build_playfair_matrix(key):
key = key.upper().replace("J", "I")
seen, matrix = set(), []
for c in key + string.ascii_uppercase:
if c not in seen and c != "J":
seen.add(c)
matrix.append(c)
return [matrix[i*5:(i+1)*5] for i in range(5)]
def _playfair_pos(matrix, c):
for r, row in enumerate(matrix):
if c in row:
return r, row.index(c)
return None, None
def from_playfair(ciphertext, key):
"""Decrypt Playfair cipher."""
matrix = _build_playfair_matrix(key)
ciphertext = ciphertext.upper().replace("J", "I").replace(" ", "")
plaintext = []
for i in range(0, len(ciphertext), 2):
a, b = ciphertext[i], ciphertext[i+1] if i+1 < len(ciphertext) else "X"
r1, c1 = _playfair_pos(matrix, a)
r2, c2 = _playfair_pos(matrix, b)
if r1 == r2:
plaintext += [matrix[r1][(c1-1) % 5], matrix[r2][(c2-1) % 5]]
elif c1 == c2:
plaintext += [matrix[(r1-1) % 5][c1], matrix[(r2-1) % 5][c2]]
else:
plaintext += [matrix[r1][c2], matrix[r2][c1]]
return ''.join(plaintext)
# ── Columnar Transposition ───────────────────────────────────────────────────
def from_columnar_transposition(ciphertext, key):
"""Decrypt columnar transposition cipher."""
key = key.upper()
order = sorted(range(len(key)), key=lambda i: key[i])
num_cols = len(key)
num_rows = len(ciphertext) // num_cols
remainder = len(ciphertext) % num_cols
col_lengths = [num_rows + (1 if order.index(i) < remainder else 0) for i in range(num_cols)]
cols, idx = [], 0
for col in range(num_cols):
length = col_lengths[order[col]]
cols.append(list(ciphertext[idx:idx+length]))
idx += length
# Re-order columns back
sorted_cols = [None] * num_cols
for i, o in enumerate(order):
sorted_cols[o] = cols[i]
return ''.join(sorted_cols[col][row] for row in range(num_rows + 1)
for col in range(num_cols)
if row < len(sorted_cols[col]))
# ── Four-Square Cipher ───────────────────────────────────────────────────────
def _make_square(key, alphabet="ABCDEFGHIKLMNOPQRSTUVWXYZ"):
key = key.upper().replace("J", "I")
seen, sq = set(), []
for c in key + alphabet:
if c not in seen:
seen.add(c)
sq.append(c)
return [sq[i*5:(i+1)*5] for i in range(5)]
def _sq_pos(sq, c):
for r, row in enumerate(sq):
if c in row:
return r, row.index(c)
return None, None
def from_four_square(ciphertext, key1, key2):
"""Decrypt Four-Square cipher given two keys."""
std = _make_square("")
sq1 = _make_square(key1)
sq2 = _make_square(key2)
ciphertext = ciphertext.upper().replace("J", "I").replace(" ", "")
plaintext = []
for i in range(0, len(ciphertext) - 1, 2):
a, b = ciphertext[i], ciphertext[i+1]
r1, c1 = _sq_pos(sq1, a)
r2, c2 = _sq_pos(sq2, b)
if None not in (r1, c1, r2, c2):
plaintext += [std[r1][c2], std[r2][c1]]
return ''.join(plaintext)
# ── Polybius Square ──────────────────────────────────────────────────────────
def from_polybius(text, key="ABCDEFGHIKLMNOPQRSTUVWXYZ"):
"""
Decrypt Polybius square cipher.
Input: pairs of digits like "43 15 13 13 34"
"""
key = key.upper().replace("J", "I")
square = {f"{(i//5)+1}{(i%5)+1}": key[i] for i in range(25)}
tokens = text.strip().split()
return ''.join(square.get(t, '?') for t in tokens)
# ── Beaufort Cipher ──────────────────────────────────────────────────────────
def from_beaufort(text, key):
"""Decrypt Beaufort cipher (reciprocal Vigenère variant)."""
key = key.upper()
result = []
ki = 0
for c in text:
if c.isalpha():
k = ord(key[ki % len(key)]) - ord('A')
base = ord('A') if c.isupper() else ord('a')
result.append(chr((k - (ord(c.upper()) - ord('A'))) % 26 + base))
ki += 1
else:
result.append(c)
return ''.join(result)
# ── Running Key Cipher ───────────────────────────────────────────────────────
def from_running_key(ciphertext, key):
"""Decrypt running key cipher using a long key (e.g., a sentence)."""
key = key.upper()
result = []
ki = 0
for c in ciphertext:
if c.isalpha():
k = ord(key[ki % len(key)]) - ord('A')
base = ord('A') if c.isupper() else ord('a')
result.append(chr((ord(c.upper()) - ord('A') - k) % 26 + base))
ki += 1
else:
result.append(c)
return ''.join(result)
# ── Demo ─────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== Substitution ===")
print(from_substitution("Itssg", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
print("\n=== Polybius ===")
print(from_polybius("23 15 12 12 34")) # -> HELLO (approx)
print("\n=== Beaufort ===")
print(from_beaufort("Rcaz", "KEY"))
print("\n=== Running Key ===")
print(from_running_key("Hsnlc", "KEYIS"))