Skip to content

Commit bacd425

Browse files
Create two_square_cipher.py
1 parent 7530a41 commit bacd425

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed

ciphers/two_square_cipher.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
def generate_square(keyword: str) -> list:
2+
"""
3+
Generate a 5x5 Playfair square (I/J combined) from a keyword.
4+
"""
5+
keyword = keyword.upper().replace("J", "I")
6+
seen = set()
7+
square = []
8+
for char in keyword:
9+
if char.isalpha() and char not in seen:
10+
seen.add(char)
11+
square.append(char)
12+
for char in "ABCDEFGHIKLMNOPQRSTUVWXYZ":
13+
if char not in seen:
14+
seen.add(char)
15+
square.append(char)
16+
return [square[i : i + 5] for i in range(0, 25, 5)]
17+
18+
19+
def find_position(square, char):
20+
"""
21+
Return (row, col) of char in given 5x5 square.
22+
"""
23+
if char == "J":
24+
char = "I"
25+
for r in range(5):
26+
for c in range(5):
27+
if square[r][c] == char:
28+
return r, c
29+
return None
30+
31+
32+
def two_square_encrypt(plaintext: str, key1: str, key2: str) -> str:
33+
"""
34+
Encrypt plaintext using the Two-Square cipher.
35+
36+
>>> two_square_encrypt("HELLO", "KEYWORD", "EXAMPLE")
37+
'CEYLBX'
38+
"""
39+
plaintext = plaintext.upper().replace("J", "I")
40+
plaintext = "".join([c for c in plaintext if c.isalpha()])
41+
42+
# split into digraphs
43+
pairs = []
44+
i = 0
45+
while i < len(plaintext):
46+
a = plaintext[i]
47+
b = plaintext[i + 1] if i + 1 < len(plaintext) else "X"
48+
if a == b:
49+
b = "X"
50+
i += 1
51+
else:
52+
i += 2
53+
pairs.append((a, b))
54+
55+
square1 = generate_square(key1)
56+
square2 = generate_square(key2)
57+
58+
ciphertext = ""
59+
for a, b in pairs:
60+
r1, c1 = find_position(square1, a)
61+
r2, c2 = find_position(square2, b)
62+
ciphertext += square2[r1][c2]
63+
ciphertext += square1[r2][c1]
64+
return ciphertext
65+
66+
67+
def two_square_decrypt(ciphertext: str, key1: str, key2: str) -> str:
68+
"""
69+
Decrypt ciphertext using the Two-Square cipher.
70+
71+
>>> two_square_decrypt("CEYLBX", "KEYWORD", "EXAMPLE")
72+
'HELLOX'
73+
"""
74+
ciphertext = ciphertext.upper().replace("J", "I")
75+
ciphertext = "".join([c for c in ciphertext if c.isalpha()])
76+
77+
if len(ciphertext) % 2 != 0:
78+
raise ValueError("Ciphertext must have even length")
79+
80+
square1 = generate_square(key1)
81+
square2 = generate_square(key2)
82+
83+
plaintext = ""
84+
for i in range(0, len(ciphertext), 2):
85+
a, b = ciphertext[i], ciphertext[i + 1]
86+
r1, c1 = find_position(square2, a)
87+
r2, c2 = find_position(square1, b)
88+
plaintext += square1[r1][c2]
89+
plaintext += square2[r2][c1]
90+
return plaintext
91+
92+
93+
if __name__ == "__main__":
94+
from doctest import testmod
95+
96+
testmod()
97+
98+
# Example usage
99+
plaintext = "HELLO"
100+
key1 = "KEYWORD"
101+
key2 = "EXAMPLE"
102+
encrypted = two_square_encrypt(plaintext, key1, key2)
103+
decrypted = two_square_decrypt(encrypted, key1, key2)
104+
105+
print("\n\n")
106+
print("Plaintext:", plaintext)
107+
print("Encrypted:", encrypted)
108+
print("Decrypted:", decrypted)

0 commit comments

Comments
 (0)