break_fixed_nonce_ctr_statistically.py (1545B)
1 from Crypto.Cipher import AES 2 from Crypto import Random 3 from Crypto.Util import Counter 4 5 frequent_letters = "etaoishr ETAOISHR" 6 key = Random.new().read(AES.block_size) 7 8 def encrypt(plaintext): 9 return AES.new(key, AES.MODE_CTR, counter=Counter.new(128)).encrypt(plaintext) 10 11 def decrypt(key, msg): 12 return ''.join([chr(ord(key[i % len(key)]) ^ ord(char)) for i, char in enumerate(msg)]) 13 14 def get_candidate_key_byte(transposed_block): 15 candidate = '' 16 candidate_frequency = 0 17 18 for c in range(256): 19 plaintext = ''.join([chr(c ^ ord(a)) for a in transposed_block]) 20 frequency = sum([plaintext.count(frequent_letters[n]) for n in range(len(frequent_letters))]) 21 22 if frequency > candidate_frequency: 23 candidate = chr(c) 24 candidate_frequency = frequency 25 26 return candidate 27 28 def get_key(file, key_length): 29 split_file = [file[i:i + key_length] for i in range(0, len(file), key_length)] 30 transposed_blocks = [''.join([block[x] for block in split_file[:len(split_file) - 1]]) for x in range(key_length)] 31 return ''.join([get_candidate_key_byte(block) for block in transposed_blocks]) 32 33 with open('files/20.txt') as f: 34 ciphertexts = [encrypt(line.decode("base64")) for line in f.read().splitlines()] 35 36 key_length = min(len(ct) for ct in ciphertexts) 37 ciphertext = ''.join([ct[:key_length] for ct in ciphertexts]) 38 plaintext = decrypt(get_key(ciphertext, key_length), ciphertext) 39 40 for line in [plaintext[i:i + key_length] for i in range(0, len(plaintext), key_length)]: 41 print line