Skip to content

Commit 73461ba

Browse files
committed
Playfair cipher
1 parent 89d48b5 commit 73461ba

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

ciphers/playfair_cipher.py

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import string
2+
import itertools
3+
4+
def chunker(seq, size):
5+
it = iter(seq)
6+
while True:
7+
chunk = tuple(itertools.islice(it, size))
8+
if not chunk:
9+
return
10+
yield chunk
11+
12+
13+
14+
def prepare_input(dirty):
15+
"""
16+
Prepare the plaintext by uppcasing it
17+
and seperating repeated letters with X's
18+
"""
19+
20+
dirty = ''.join([c.upper() for c in dirty if c in string.ascii_letters])
21+
clean = ""
22+
23+
if len(dirty) < 2:
24+
return dirty
25+
26+
for i in range(len(dirty)-1):
27+
clean += dirty[i]
28+
29+
if dirty[i] == dirty[i+1]:
30+
clean += 'X'
31+
32+
clean += dirty[-1]
33+
34+
if len(clean) & 1:
35+
clean += 'X'
36+
37+
return clean
38+
39+
def generate_table(key):
40+
41+
# I and J are used interchangably to allow
42+
# us to use a 5x5 table (25 letters)
43+
alphabet = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
44+
# we're using a list instead of a '2d' array because it makes the math
45+
# for setting up the table and doing the actual encoding/decoding simpler
46+
table = []
47+
48+
# copy key chars into the table if they are in `alphabet` ignoring duplicates
49+
for char in key.upper():
50+
if char not in table and char in alphabet:
51+
table.append(char)
52+
53+
# fill the rest of the table in with the remaining alphabet chars
54+
for char in alphabet:
55+
if char not in table:
56+
table.append(char)
57+
58+
return table
59+
60+
def encode(plaintext, key):
61+
table = generate_table(key)
62+
plaintext = prepare_input(plaintext)
63+
ciphertext = ""
64+
65+
# https://en.wikipedia.org/wiki/Playfair_cipher#Description
66+
for char1, char2 in chunker(plaintext, 2):
67+
row1, col1 = divmod(table.index(char1), 5)
68+
row2, col2 = divmod(table.index(char2), 5)
69+
70+
if row1 == row2:
71+
ciphertext += table[row1*5+(col1+1)%5]
72+
ciphertext += table[row2*5+(col2+1)%5]
73+
elif col1 == col2:
74+
ciphertext += table[((row1+1)%5)*5+col1]
75+
ciphertext += table[((row2+1)%5)*5+col2]
76+
else: # rectangle
77+
ciphertext += table[row1*5+col2]
78+
ciphertext += table[row2*5+col1]
79+
80+
return ciphertext
81+
82+
83+
def decode(ciphertext, key):
84+
table = generate_table(key)
85+
plaintext = ""
86+
87+
# https://en.wikipedia.org/wiki/Playfair_cipher#Description
88+
for char1, char2 in chunk(ciphertext, 2):
89+
row1, col1 = divmod(table.index(char1), 5)
90+
row2, col2 = divmod(table.index(char2), 5)
91+
92+
if row1 == row2:
93+
plaintext += table[row1*5+(col1-1)%5]
94+
plaintext += table[row2*5+(col2-1)%5]
95+
elif col1 == col2:
96+
plaintext += table[((row1-1)%5)*5+col1]
97+
plaintext += table[((row2-1)%5)*5+col2]
98+
else: # rectangle
99+
plaintext += table[row1*5+col2]
100+
plaintext += table[row2*5+col1]
101+
102+
return plaintext

0 commit comments

Comments
 (0)