cryptopals

https://cryptopals.com/
Log | Files | Refs

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