def detect_ecb_line(cipher_text, block_size=default_aes_block_size): ''' :param cipher_text: hexdecimal bytes string or string :param block_size: block size used :return: number of unique blocks, line number and the cipher it self as a tuple ''' min_unique_blocks = maxsize cipher_res = None cipher_line = 0 for line_number, cipher in enumerate(cipher_text): if isinstance(cipher, bytes): cipher = cipher.decode('utf-8') unique_blocks = len( set( get_blocks(arr=hex_to_bytes(cipher.strip()), block_size=block_size))) if unique_blocks < min_unique_blocks: min_unique_blocks = unique_blocks cipher_res = bytes(cipher.strip(), 'utf-8') cipher_line = line_number + 1 min_unique_blocks = min_unique_blocks if min_unique_blocks < len( list(get_blocks(arr=hex_to_bytes(cipher_res), block_size=block_size))) else None return min_unique_blocks, cipher_line, cipher_res
def byte_byte_ecb_break(unknown_str, hard=False): ''' :param unknown_str: unknown string we want to break as bytes string :param hard: if this hard ECB decryption :return: the discoverd string as bytes string ''' prefix_text = b'' if not hard else _urandom(randint(0, 100)) enc, dec = random_key_ecb_enc_dec(prefix_text, unknown_str) def single_block_break(block): discovered = b'' for i in range(len(block) - 1, -1, -1): prepad = b'A' * i expected_cipher = enc( plain_text=b''.join([prepad, block])[:len(block)]) for c in range(256): test = prepad + discovered + bytes([c]) if enc(plain_text=test) == expected_cipher: discovered += bytes([c]) break return discovered res = b'' for block in get_blocks(arr=unknown_str, block_size=find_block_size(enc_func=enc)): res += single_block_break(block=block) return res
def encrypt(self, plain_text): data = pad(data=plain_text, block_size=AES.block_size) cipher = [] iv = self.__iv for block in get_blocks(arr=data, block_size=AES.block_size): iv = self.__block_encrypt_ecb(plain_text=xor_strings(iv, block)) cipher.append(iv) return b''.join(cipher)
def decode_base64(base64_bytes): ''' :param base64_bytes: base64 bytes string :return: bytes string of the decoded base64 bytes in hexadecimal ''' chars_in_block = 4 ret = b'' for block in get_blocks(arr=base64_bytes, block_size=chars_in_block): bin_bits_to_trim = block.count(b'=') * 2 bin_arr = ''.join([ base64_char_to_bin(base64_char=chr(c)) for c in block if c != ord('=') ]) bin_arr = bin_arr[:len(bin_arr) - bin_bits_to_trim] for bin_block in get_blocks(arr=bin_arr, block_size=8): ret += bin_str_to_hex(bin_str=bin_block) return ret
def decrypt(self, cipher_text): plain = [] iv = self.__iv for block in get_blocks(arr=cipher_text, block_size=AES.block_size): plain.append( xor_strings(iv, self.__block_decrypt_ecb(cipher_text=block))) iv = block plain[len(plain) - 1] = unpad(data=plain[len(plain) - 1]) return b''.join(plain)
def encode_base64(hex_bytes): ''' :param hex_bytes: bytes string that contains hexadecimal numbers :return: byte string base64 representation of the input hex bytes ''' ret = b'' bytes_in_block = 3 padded, padded_bytes = pad_base64_bytes(hex_bytes=hex_bytes) bits_encode = 0 for block in get_blocks(arr=padded, block_size=bytes_in_block): bin_arr = ''.join( [hex_byte_to_bin(block[i]) for i in range(bytes_in_block)]) for bin_block in get_blocks(arr=bin_arr, block_size=6): ret += bytes(BASE64_TBL[int(bin_block, 2)], 'utf-8') if bits_encode <= (len(padded)*8 - (padded_bytes*8))\ else bytes('=', 'utf-8') bits_encode += 6 return ret
def find_key_size(cipher_text, start_range, end_range): distances = [] for key_size in range(start_range, end_range): blocks = list(get_blocks(arr=cipher_text, block_size=key_size)) hamming_distance_sum = 0 for b1, b2 in zip(blocks[::2], blocks[1::2]): hamming_distance_sum += hamming_distance(b1, b2) distances.append( (key_size, ((hamming_distance_sum / len(blocks) / 2) / key_size))) return sorted(distances, key=itemgetter(1))[0]
def repeated_key_xor_breaker(cipher_text): ''' :param cipher_text: cipher text as hexadecimal bytes string :return: decryption of the cipher text as bytes string ''' def find_key_size(cipher_text, start_range, end_range): distances = [] for key_size in range(start_range, end_range): blocks = list(get_blocks(arr=cipher_text, block_size=key_size)) hamming_distance_sum = 0 for b1, b2 in zip(blocks[::2], blocks[1::2]): hamming_distance_sum += hamming_distance(b1, b2) distances.append( (key_size, ((hamming_distance_sum / len(blocks) / 2) / key_size))) return sorted(distances, key=itemgetter(1))[0] key = [] cipher_text_hex_bytes = hex_to_bytes(cipher_text) key_size = find_key_size(cipher_text=cipher_text_hex_bytes, start_range=2, end_range=41)[0] blocks = [ list(block) for block in get_blocks(arr=cipher_text_hex_bytes, block_size=key_size) if len(block) == key_size ] for block in zip(*blocks): key.append( single_byte_xor_breaker(bytes_to_hex(bytes([x for x in block])))[1]) return hex_to_bytes( repeated_key_xor(plain_text=hex_to_bytes(cipher_text), key=bytes(key))), bytes(key)