defbytesxor(a, b): iflen(a) > len(b): returnbytes([x ^ y for x, y inzip(a[:len(b)], b)]) else: returnbytes([x ^ y for x, y inzip(a, b[:len(a)])]) print(bytesxor(bytes.fromhex('1c0111001f010100061a024b53535009181c'), bytes.fromhex('686974207468652062756c6c277320657965')).hex())
defscore(s): ans = 0 for c in s.lower(): if c inb" etaoinshrdlu": ans += 2 elif32 <= c <= 126: ans += 1 else: ans -= 5 return ans
defhamming(a, b): returnsum((x ^ y).bit_count() for x, y inzip(a, b))
defone_byte_xor(s): best = None for i inrange(256): x = bytes([c ^ i for c in s]) cur = score(x) if best isNoneor cur > best[0]: best = (cur, i, x) return best
data = base64.b64decode(requests.get("https://cryptopals.com/static/challenge-data/6.txt").text) cand = []
for keysize inrange(2, 41): blocks = [data[i : i + keysize] for i inrange(0, keysize * 8, keysize)] iflen(blocks[-1]) < keysize: continue dist = 0 for i inrange(len(blocks) - 1): dist += hamming(blocks[i], blocks[i + 1]) / keysize cand.append((dist / (len(blocks) - 1), keysize))
best = None
for _, keysize insorted(cand)[:5]: key = b"" for i inrange(keysize): block = bytes([data[j] for j inrange(i, len(data), keysize)]) key += bytes([one_byte_xor(block)[1]]) plain = bytes([data[i] ^ key[i % len(key)] for i inrange(len(data))]) cur = score(plain) if best isNoneor cur > best[0]: best = (cur, key, plain)