def brute_force_ecb_padding(plaintext, block_size, oracle): """Helper function to brute force the PKCS7 padding for the ECB break :param plaintext: The current plaintext guess :param block_size: The block size of the oracle :param oracle: ECB encryption oracle that encrypts AES-128-ECB(your-string || unknown-string, random-key) :return: The correct unknown-string or None on failure to match """ last_block_start = round_down(len(plaintext), block_size) expected_ciphertext = oracle('') # The plaintext may have padding bytes in it so try padding all slices of the last block # When the encryption of the plaintext + padding matches the encryption of an empty string we know the plaintext # is correct for i in xrange(last_block_start, len(plaintext)): plaintext_guess = plaintext[:i] # Now pad it manually since the hidden string will be appended plaintext_guess_w_padding = pkcs7_pad(plaintext_guess, block_size) guess_ciphertext = oracle(plaintext_guess_w_padding)[:len(expected_ciphertext)] if guess_ciphertext == expected_ciphertext: return plaintext_guess return None
def break_ecb_encryption(oracle): """Break ECB encryption with a chosen plaintext attack :param oracle: ECB encryption oracle that encrypts AES-128-ECB(your-string || unknown-string, random-key) :return: The unknown-string encrypted by the ECB encryption oracle """ block_size = detect_oracle_block_size(oracle) mode = detect_aes_mode(oracle) if mode != AesMode.ECB: raise Exception('Oracle mode != ECB') # Account for oracles that prepend data oracle = get_no_prefix_oracle(oracle, block_size) total_length = len(oracle('')) plaintext = 'A' * (block_size-1) for i in xrange(total_length): decrypt_oracle = get_decryption_oracle(oracle, plaintext[-(block_size-1):], block_size) current_block_base = round_down(i, block_size) input_pad = 'A' * (block_size - (i % block_size) - 1) ciphertext = oracle(input_pad)[current_block_base:current_block_base+block_size] try: plaintext += decrypt_oracle[ciphertext] except KeyError: # If we're on the last block we will start running into problems with changing padding. Just brute force # the padding bytes. if (total_length - i) < block_size: # Drop the A's added to the plaintext that allowed us to brute force the first block plaintext = plaintext[15:] plaintext = brute_force_ecb_padding(plaintext, block_size, oracle) break else: raise return plaintext