Skip to content

Instantly share code, notes, and snippets.

@jaybosamiya
Created October 14, 2017 03:57
Show Gist options
  • Save jaybosamiya/331d5935112c19386e68a7f57da80d8b to your computer and use it in GitHub Desktop.
Save jaybosamiya/331d5935112c19386e68a7f57da80d8b to your computer and use it in GitHub Desktop.
Attack repeating key xor
def attack_repeating_key_xor(ciphertext, keysize=None, score=None):
from itertools import cycle
def xor(enc, k):
return ''.join(chr(ord(a) ^ k) for a in enc)
def score_english(string):
freq = dict()
freq['a'] = 834
freq['b'] = 154
freq['c'] = 273
freq['d'] = 414
freq['e'] = 1260
freq['f'] = 203
freq['g'] = 192
freq['h'] = 611
freq['i'] = 671
freq['j'] = 23
freq['k'] = 87
freq['l'] = 424
freq['m'] = 253
freq['n'] = 680
freq['o'] = 770
freq['p'] = 166
freq['q'] = 9
freq['r'] = 568
freq['s'] = 611
freq['t'] = 937
freq['u'] = 285
freq['v'] = 106
freq['w'] = 234
freq['x'] = 20
freq['y'] = 204
freq['z'] = 6
freq[' '] = 2320
ret = 0
for c in string.lower():
if c in freq:
ret += freq[c]
return ret
if score is None:
score = score_english
if keysize is None:
def hamming(x, y):
assert(len(x) == len(y))
def popcount(a):
if a == 0:
return 0
else:
return (a % 2) + popcount(a / 2)
return sum(popcount(ord(a) ^ ord(b)) for a, b in zip(x, y))
def norm_dist(keysize):
numblocks = (len(ciphertext) / keysize)
blocksum = 0
for i in range(numblocks - 1):
a = ciphertext[i * keysize: (i+1) * keysize]
b = ciphertext[(i+1) * keysize: (i+2) * keysize]
blocksum += hamming(a, b)
blocksum /= float(numblocks)
blocksum /= float(keysize)
return blocksum
keysize = min(range(2, min(40, len(ciphertext))), key=norm_dist)
key = [None]*keysize
for i in range(keysize):
d = ciphertext[i::keysize]
key[i] = max(range(256), key=lambda k: score(xor(d, k)))
plaintext = ''.join(chr(ord(a) ^ b) for a, b in zip(ciphertext, cycle(key)))
return plaintext
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment