#!/usr/bin/env python # -*- coding: utf-8 -*- import random import sys from Crypto.Cipher import AES BLOCK_SIZE = 16 # bytes INIT_VEC = 'This is an IV456' # hardcoding this is a terrible idea EXAMPLE_TEXT = """Friends, Romans, countrymen, lend me your ears; I come to bury Caesar, not to praise him. The evil that men do lives after them; The good is oft interred with their bones; So let it be with Caesar. The noble Brutus Hath told you Caesar was ambitious: If it were so, it was a grievous fault, And grievously hath Caesar answer’d it. Here, under leave of Brutus and the rest– For Brutus is an honourable man; So are they all, all honourable men– Come I to speak in Caesar’s funeral. He was my friend, faithful and just to me: But Brutus says he was ambitious; And Brutus is an honourable man. He hath brought many captives home to Rome Whose ransoms did the general coffers fill: Did this in Caesar seem ambitious? When that the poor have cried, Caesar hath wept: Ambition should be made of sterner stuff: Yet Brutus says he was ambitious; And Brutus is an honourable man. You all did see that on the Lupercal I thrice presented him a kingly crown, Which he did thrice refuse: was this ambition? Yet Brutus says he was ambitious; And, sure, he is an honourable man. I speak not to disprove what Brutus spoke, But here I am to speak what I do know. You all did love him once, not without cause: What cause withholds you then, to mourn for him? O judgment! thou art fled to brutish beasts, And men have lost their reason. Bear with me; My heart is in the coffin there with Caesar, And I must pause till it come back to me.""" class InvalidPadding(Exception): pass def blockify(text, block_size=BLOCK_SIZE): return [text[i:i+block_size] for i in range(0, len(text), block_size)] def key_gen(): return "".join([chr(random.getrandbits(8)) for _ in xrange(BLOCK_SIZE)]) def validate_padding(padded_text): return all([n == padded_text[-1] for n in padded_text[-ord(padded_text[-1]):]]) def pkcs7_pad(text): length = BLOCK_SIZE - (len(text) % BLOCK_SIZE) text += chr(length) * length return text def pkcs7_depad(text): if not validate_padding(text): raise InvalidPadding() return text[:-ord(text[-1])] def encrypt(plaintext, key, init_vec): cipher = AES.new(key, AES.MODE_CBC, init_vec) padded_text = pkcs7_pad(plaintext) ciphertext = cipher.encrypt(padded_text) return ciphertext def decrypt(ciphertext, key, init_vec): cipher = AES.new(key, AES.MODE_CBC, init_vec) padded_text = cipher.decrypt(ciphertext) plaintext = pkcs7_depad(padded_text) return plaintext def numberify(characters): return map(lambda x: ord(x), characters) def stringify(numbers): return "".join(map(lambda x: chr(x), numbers)) if __name__ == "__main__": my_key = key_gen() IV = numberify(INIT_VEC) ciphertext = numberify(encrypt(EXAMPLE_TEXT, my_key, INIT_VEC)) blocks = blockify(ciphertext) cleartext = [] for block_num, (c1, c2) in enumerate(zip([IV]+blocks, blocks)): print "cracking block {} out of {}".format(block_num+1, len(blocks)) i2 = [0] * 16 p2 = [0] * 16 for i in xrange(15,-1,-1): for b in xrange(0,256): prefix = c1[:i] pad_byte = (BLOCK_SIZE-i) suffix = [pad_byte ^ val for val in i2[i+1:]] evil_c1 = prefix + [b] + suffix try: decrypt(stringify(c2), my_key, stringify(evil_c1)) except InvalidPadding: pass else: i2[i] = evil_c1[i] ^ pad_byte p2[i] = c1[i] ^ i2[i] break cleartext+=p2 # print "i2:", i2 # print "c2:", c2 # print "p2:", p2 # print "block:[{}]".format(stringify(p2)) # print "expected:[{}]".format(EXAMPLE_TEXT[(16 * block_num):(16 * block_num)+16]) print "=========================" print stringify(cleartext) print "========================="