def detect_single_byte_xor(ciphers): """Detects single byte xor cipher from the list of ciphers.""" for cipher in ciphers: text, _ = FrequencyAnalyzer.break_single_byte_xor(cipher) if text and FrequencyAnalyzer.is_english(text): return text return None
def break_repeating_xor(cipher, key_len=0): """Breaks repeating key xor cipher. Returns (plaintext, key)""" key_len = key_len if key_len > 0 else Crypto.break_key_length(cipher) # Solve each block as if it was single-character XOR. key = ''.join([FrequencyAnalyzer.break_single_byte_xor( cipher[i::key_len])[1] for i in range(0, key_len)]) return FrequencyAnalyzer.get_repeating_xor(cipher, key), key
def break_repeating_xor(cipher, key_len=0): """Breaks repeating key xor cipher. Returns (plaintext, key)""" key_len = key_len if key_len > 0 else Crypto.break_key_length(cipher) # Solve each block as if it was single-character XOR. key = ''.join([ FrequencyAnalyzer.break_single_byte_xor(cipher[i::key_len])[1] for i in range(0, key_len) ]) return FrequencyAnalyzer.get_repeating_xor(cipher, key), key
def flip_cipher_to_add_admin_cbc(aes_cbc, has_admin): """Flip bits in text until we get admin in cipher.""" # this ensures at least one block has all X's block_size = 16 cipher = aes_cbc('x' * 2 * block_size) flipper = FrequencyAnalyzer.get_repeating_xor('x' * block_size, ';admin=true;') # XXXXX xor ;admin=true; xor XXXXX => ;admin=true; for i, block in enumerate(Crypto.get_blocks(cipher, block_size)): flipped_block = FrequencyAnalyzer.get_repeating_xor(flipper, block) flipped_cipher = (cipher[:i * block_size] + flipped_block + cipher[(i + 1) * block_size:]) if has_admin(flipped_cipher): return True return False
def hmac_sha1(key, message): """HMAC implementation using sha1 as hash function""" blocksize = 64 # keys longer than blocksize are shortened if (len(key) > blocksize): key = sha1(key) # keys shorter than blocksize are zero-padded if (len(key) < blocksize): key = key + b'\x00' * (blocksize - len(key)) o_key_pad = FrequencyAnalyzer.get_repeating_xor('\x5c' * blocksize, key) i_key_pad = FrequencyAnalyzer.get_repeating_xor('\x36' * blocksize, key) return sha1(o_key_pad + sha1(i_key_pad + message))
def test_break_single_byte_xor(self): """Challenge 3""" expected = "Cooking MC's like a pound of bacon" hex_str = Crypto.get_lines('data/3.txt')[0] cipher = binascii.unhexlify(hex_str) text, _ = FrequencyAnalyzer.break_single_byte_xor(cipher) self.assertEqual(expected, text)
def test_fixed_xor(self): """Challenge 2""" expected = binascii.unhexlify('746865206b696420646f6e277420706c6179') actual = FrequencyAnalyzer.get_repeating_xor( binascii.unhexlify('1c0111001f010100061a024b53535009181c'), binascii.unhexlify('686974207468652062756c6c277320657965')) self.assertEqual(expected, actual)
def flip_cipher_to_add_admin_cbc(aes_cbc, has_admin): """Flip bits in text until we get admin in cipher.""" # this ensures at least one block has all X's block_size = 16 cipher = aes_cbc('x'*2*block_size) flipper = FrequencyAnalyzer.get_repeating_xor( 'x'*block_size, ';admin=true;') # XXXXX xor ;admin=true; xor XXXXX => ;admin=true; for i, block in enumerate(Crypto.get_blocks(cipher, block_size)): flipped_block = FrequencyAnalyzer.get_repeating_xor(flipper, block) flipped_cipher = (cipher[:i*block_size] + flipped_block + cipher[(i+1)*block_size:]) if has_admin(flipped_cipher): return True return False
def extract_key_if_key_is_same_as_key(cipher, oracle): """Extract key given encrypt and decrypt functions. Exploit the fact that key is same is IV.""" bs = 16 blocks = Crypto.get_blocks(cipher) cipher = blocks[2] + chr(0)*bs + blocks[2] + blocks[3] text = oracle(cipher) return FrequencyAnalyzer.get_repeating_xor(text[0:bs], text[2*bs:])
def extract_key_if_key_is_same_as_key(cipher, oracle): """Extract key given encrypt and decrypt functions. Exploit the fact that key is same is IV.""" bs = 16 blocks = Crypto.get_blocks(cipher) cipher = blocks[2] + chr(0) * bs + blocks[2] + blocks[3] text = oracle(cipher) return FrequencyAnalyzer.get_repeating_xor(text[0:bs], text[2 * bs:])
def break_aes_using_padding_leak(cipher, init_vector, has_valid_padding): """Decrypts cipher given has_valid_padding function which decrypts and return true if result has valid padding, false otherwise. We will use this leak to break the cipher and return plaintext""" block_size = 16 mutate = lambda text, i, c: text[:i] + c + text[i + 1:] # get padding size pad_size = 0 # second_last block index sl_block = len(cipher) - block_size * 2 for i in range(block_size): # check if pad_size is block_size - i change = 'b' if cipher[sl_block + i] == 'a' else 'a' if not has_valid_padding(mutate(cipher, sl_block + i, change), init_vector): pad_size = block_size - i break # we know pad size which means we know last pad_size bytes of result. prexor = FrequencyAnalyzer.get_repeating_xor( chr(pad_size) * pad_size, cipher[-pad_size - block_size:-block_size]) iv_and_cipher = init_vector + cipher for i in range(len(prexor), len(cipher)): pad_size = (len(prexor) % block_size) + 1 # decrypt byte at target_index in this iteration. target_index = len(cipher) - len(prexor) - 1 for char in range(256): # temper iv_and_cipher attack = mutate(iv_and_cipher, target_index, chr(char)) xor = FrequencyAnalyzer.get_repeating_xor( chr(pad_size) * (pad_size - 1), prexor[:pad_size - 1]) attack = attack[:target_index + 1] + xor # add next block attack = (attack + iv_and_cipher[len(attack):len(attack) + block_size]) flipped_iv = attack[:block_size] flipped_cipher = attack[block_size:] if has_valid_padding(flipped_cipher, flipped_iv): prexor = chr(pad_size ^ char) + prexor break blocks = zip(Crypto.get_blocks(iv_and_cipher), Crypto.get_blocks(prexor)) return Crypto.unpad_pkcs7(''.join( [FrequencyAnalyzer.get_repeating_xor(a, b) for a, b in blocks]))
def break_aes_using_padding_leak(cipher, init_vector, has_valid_padding): """Decrypts cipher given has_valid_padding function which decrypts and return true if result has valid padding, false otherwise. We will use this leak to break the cipher and return plaintext""" block_size = 16 mutate = lambda text, i, c: text[:i] + c + text[i+1:] # get padding size pad_size = 0 # second_last block index sl_block = len(cipher) - block_size*2 for i in range(block_size): # check if pad_size is block_size - i change = 'b' if cipher[sl_block+i] == 'a' else 'a' if not has_valid_padding( mutate(cipher, sl_block+i, change), init_vector): pad_size = block_size - i break # we know pad size which means we know last pad_size bytes of result. prexor = FrequencyAnalyzer.get_repeating_xor( chr(pad_size)*pad_size, cipher[-pad_size-block_size:-block_size]) iv_and_cipher = init_vector + cipher for i in range(len(prexor), len(cipher)): pad_size = (len(prexor) % block_size) + 1 # decrypt byte at target_index in this iteration. target_index = len(cipher) - len(prexor) - 1 for char in range(256): # temper iv_and_cipher attack = mutate(iv_and_cipher, target_index, chr(char)) xor = FrequencyAnalyzer.get_repeating_xor( chr(pad_size)*(pad_size-1), prexor[:pad_size-1]) attack = attack[:target_index+1] + xor # add next block attack = (attack + iv_and_cipher[len(attack):len(attack)+block_size]) flipped_iv = attack[:block_size] flipped_cipher = attack[block_size:] if has_valid_padding(flipped_cipher, flipped_iv): prexor = chr(pad_size^char) + prexor break blocks = zip( Crypto.get_blocks(iv_and_cipher), Crypto.get_blocks(prexor)) return Crypto.unpad_pkcs7(''.join( [FrequencyAnalyzer.get_repeating_xor(a, b) for a, b in blocks]))
def decrypt_cbc_using_ecb(cipher, key): """Implement AES CBC using AES ECB.""" iv = '\x00'*16 text = '' aes = AES.new(key, AES.MODE_ECB) for block in Crypto.get_blocks(cipher): temp = aes.decrypt(block) text += FrequencyAnalyzer.get_repeating_xor(temp, iv) iv = block return Crypto.unpad_pkcs7(text)
def encrypt_cbc_using_ecb(text, key): """Implement AES CBC using AES ECB.""" iv = '\x00' * 16 cipher = '' aes = AES.new(key, AES.MODE_ECB) for block in Crypto.get_blocks(Crypto.pad_pkcs7(text)): block = FrequencyAnalyzer.get_repeating_xor(block, iv) iv = aes.encrypt(block) cipher += iv return cipher
def encrypt_cbc_using_ecb(text, key): """Implement AES CBC using AES ECB.""" iv = '\x00'*16 cipher = '' aes = AES.new(key, AES.MODE_ECB) for block in Crypto.get_blocks(Crypto.pad_pkcs7(text)): block = FrequencyAnalyzer.get_repeating_xor(block, iv) iv = aes.encrypt(block) cipher += iv return cipher
def decrypt_cbc_using_ecb(cipher, key): """Implement AES CBC using AES ECB.""" iv = '\x00' * 16 text = '' aes = AES.new(key, AES.MODE_ECB) for block in Crypto.get_blocks(cipher): temp = aes.decrypt(block) text += FrequencyAnalyzer.get_repeating_xor(temp, iv) iv = block return Crypto.unpad_pkcs7(text)
def test_break_aes_ctr_fixed_nonce2(self): """Challenge 20""" block_size = 16 quote = lambda t: t counter = lambda: chr(25) * block_size aes_ctr, _ = Crypto.generate_aes_oracle('', '', AES.MODE_CTR, quote, block_size, counter) texts = [base64.b64decode(l) for l in open('data/20.txt').readlines()] ciphers = [aes_ctr(text) for text in texts] expected = FrequencyAnalyzer.get_repeating_xor(ciphers[0], texts[0])[:block_size] actual = Crypto.break_aes_ctr_with_fixed_nonce(ciphers, block_size) self.assertEquals(expected, actual)
def test_break_aes_ctr_fixed_nonce2(self): """Challenge 20""" block_size = 16 quote = lambda t: t counter = lambda: chr(25)*block_size aes_ctr, _ = Crypto.generate_aes_oracle( '', '', AES.MODE_CTR, quote, block_size, counter) texts = [base64.b64decode(l) for l in open('data/20.txt').readlines()] ciphers = [aes_ctr(text) for text in texts] expected = FrequencyAnalyzer.get_repeating_xor( ciphers[0], texts[0])[:block_size] actual = Crypto.break_aes_ctr_with_fixed_nonce(ciphers, block_size) self.assertEquals(expected, actual)
def test_get_repeating_xor(self): """Challenge 5""" expected, one, two = Crypto.get_lines('data/5.txt') xor = FrequencyAnalyzer.get_repeating_xor(one + '\n' + two, "ICE") self.assertEqual(expected, binascii.hexlify(xor))