Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
First solution in Clear category for Playfair Cipher Attack by tokiojapan55
CHARS = "abcdefghiklmnopqrstuvwxyz"
index = lambda x, y: (x % 5) + (y % 5) * 5
axis = lambda i: (i % 5, i // 5)
get_xy = lambda grid, m: None if m not in grid else axis(grid.index(m))
to_xy = lambda k, c: (k.find(c) % 5, k.find(c) // 5)
to_ch = lambda k, x, y: k[(y % 5) * 5 + (x % 5)]
def set_ch(grid, n, m):
for i in range(len(grid)):
grid[i] = m if i == n else grid[i].replace(m, '')
def pickup(key: str, p: str) -> str:
(x0, y0), (x1, y1) = to_xy(key, p[0]), to_xy(key, p[1])
if x0 == x1: # same column
return to_ch(key, x0, y0 + 1) + to_ch(key, x1, y1 + 1)
elif y0 == y1: # same row
return to_ch(key, x0 + 1, y0) + to_ch(key, x1 + 1, y1)
else: # diagonal
return to_ch(key, x1, y0) + to_ch(key, x0, y1)
def encode(key: str, pt: str) -> str:
return ''.join(pickup(key, p) for p in zip(pt[::2], pt[1::2]))
def process(grid, candidate, origin, rule, rule2):
length = len(grid)
m = candidate[0]
set_ch(grid, origin, m)
x, y = axis(origin)
for p, c in rule[m]:
if m == p or m == c:
t = c if m == p else p
yy = (y + (1 if m == p else -1)) % 5
for i in range(len(grid)):
if i // 5 != y and (i % 5 != x or i // 5 != yy):
grid[i] = grid[i].replace(t, '')
if any(len(g) == 0 for g in grid):
return None
fixed = [g for g in grid if len(g) == 1]
if fixed:
for p0, p1, c0, c1 in rule2:
if p0 in fixed and p1 in fixed:
x0, y0 = get_xy(grid, p0)
x1, y1 = get_xy(grid, p1)
if y0 == y1:
i0, i1 = index(x0 + 1, y0), index(x1 + 1, y1)
elif x0 == x1:
i0, i1 = index(x0, y0 + 1), index(x1, y1 + 1)
else:
i0, i1 = index(x1, y0), index(x0, y1)
if c0 not in grid[i0] or c1 not in grid[i1]:
return None
set_ch(grid, i0, c0)
set_ch(grid, i1, c1)
if len(candidate) == 1:
if all(len(g) == 1 for g in grid):
return grid
raise Exception
return None
for i in range(length):
if candidate[1] in grid[i]:
if g := process(grid[:], candidate[1:], i, rule, rule2):
return g
return None
def strategy(trans):
table = dict()
for p, c in trans:
table[p] = table.get(p, set()) | {(p, c)}
table[c] = table.get(c, set()) | {(p, c)}
candidate, cnt = '', 0
for k in table:
if cnt < len(table[k]):
cnt = len(table[k])
candidate = k
loop = True
while loop:
loop = False
m, cnt = '', 0
for p, c in table[candidate[-1]]:
k = c if p == candidate[-1] else p
if k not in candidate and cnt < len(table[k]):
m, cnt = k, len(table[k])
if not m:
m, cnt = '', 0
for k in table:
if k not in candidate and cnt < len(table[k]):
m, cnt = k, len(table[k])
if m:
loop = True
candidate += m
for k in CHARS:
if k not in candidate:
candidate += k
return candidate
def hack(pt: str, ct: str) -> list:
trans = {(p, c) for p, c in zip(pt, ct)}
candidate = strategy(trans)
rule = {k: [t for t in trans if k in t] for k in candidate}
rule2 = [p + c for p, c in zip(zip(pt[::2], pt[1::2]), zip(ct[::2], ct[1::2]))]
grid = process([CHARS] * 5 * 5, candidate, 0, rule, rule2)
return ''.join(grid)
def playfair_attack(plaintext: str, cryptogram: str) -> str:
return encode(hack(plaintext, cryptogram), "topsecretmessage")
Jan. 26, 2024