def attack(ciphertext): guessed_clear = b'' split_string = lambda x, n: [x[i:i + n] for i in range(0, len(x), n)] blocks = split_string(ciphertext, BLOCK_SIZE) for block_n in range( len(blocks) - 1, 0, -1): #build pair of blocks starting from end of message spliced_ciphertext = blocks[block_n - 1] + blocks[block_n] decoded_bytes = b'?' * BLOCK_SIZE #output of block cipher decoding values ##GET VALUE OF SECRET BYTE byte for byte in range(BLOCK_SIZE - 1, -1, -1): new_pad_len = BLOCK_SIZE - byte #Build hacked ciphertext tail with values to obtain desired padding hacked_ciphertext_tail = b'' for padder_index in range(1, new_pad_len): hacked_ciphertext_tail += bytearray.fromhex( '{:02x}'.format(new_pad_len ^ decoded_bytes[byte + padder_index])) for i in range(0, 256): attack_str = bytearray.fromhex('{:02x}'.format( (i ^ spliced_ciphertext[byte]))) hacked_ciphertext = spliced_ciphertext[: byte] + attack_str + hacked_ciphertext_tail + spliced_ciphertext[ byte + 1 + new_pad_len - 1:] if (is_padding_ok(hacked_ciphertext)): test_correctness = hacked_ciphertext[:byte - 1] + bytearray.fromhex( '{:02x}'.format(( 1 ^ hacked_ciphertext[ byte])) ) + hacked_ciphertext[ byte:] if (not is_padding_ok(test_correctness)): continue decoded_bytes = decoded_bytes[:byte] + bytearray.fromhex( '{:02x}'.format(hacked_ciphertext[byte] ^ new_pad_len)) + decoded_bytes[byte + 1:] guessed_clear = bytearray.fromhex( '{:02x}'.format(i ^ new_pad_len)) + guessed_clear break return guessed_clear[:-guessed_clear[-1]] #remove padding!
def attack_message(msg): cipherfake=[0] * 16 plaintext = [0] * 16 current = 0 message="" #I devide the list of bytes in blocks, and I put them in another list number_of_blocks = int(len(msg)/BLOCK_SIZE) blocks = [[]] * number_of_blocks for i in (range(number_of_blocks)): blocks[i] = msg[i * BLOCK_SIZE: (i + 1) * BLOCK_SIZE] for z in range(len(blocks)-1): #for each message, I calculate the number of block for itera in range (1,17): #the length of each block is 16. I start by one because than I use its in a counter for v in range(256): cipherfake[-itera]=v if is_padding_ok(bytes(cipherfake)+blocks[z+1]): #the idea is that I put in 'is_padding_ok' the cipherfake(array of all 0) plus the last block #if the function return true I found the value current=itera plaintext[-itera]= v^itera^blocks[z][-itera] for w in range(1,current+1): cipherfake[-w] = plaintext[-w]^itera+1^blocks[z][-w] #for decode the second byte I must set the previous bytes with 'itera+1' for i in range(16): if plaintext[i] >= 32: char = chr(int(plaintext[i])) message += char #print("Crack: " + message + "\n") return str.encode(message)
def attack(ciphertext): blocks = [ciphertext[i:i + BLOCK_SIZE] for i in range(0, len(ciphertext), BLOCK_SIZE)] current_block = len(blocks)-1 plaintext = "" # Cycle over blocks starting from last one and last one -1. while current_block > 0: pad_len = get_padding(blocks, current_block) idx_paderror = BLOCK_SIZE - pad_len iv_copy = bytearray(blocks[current_block-1]) # Need another copy of the IV to send it to the oracle for decrypt so that I do not modify blocks while idx_paderror > 0: idx_reverse = BLOCK_SIZE - 1 while idx_reverse >= idx_paderror: # Here I modify the bytes of padding increasing them with XOR iv_copy[idx_reverse] = iv_copy[idx_reverse] ^ pad_len ^ (pad_len + 1) idx_reverse -= 1 for guessedValue in range(0, 256): # Here I find the guessed value and decrypt the current index iv_copy[idx_reverse] = guessedValue if is_padding_ok(bytes(iv_copy) + blocks[current_block]): plaintext += chr((pad_len + 1) ^ guessedValue ^ blocks[current_block - 1][idx_reverse]) break pad_len += 1 idx_paderror -= 1 # }END WHILE that Cycle over bytes. current_block -= 1 # }END WHILE that Cycle over blocks. return str.encode(plaintext[::-1])
def get_pad_len(encrypted_msg): array = bytearray(encrypted_msg) pad_len = 16 while True: array[-(pad_len + BLOCK_SIZE)] ^= 1 if not is_padding_ok(bytes(array)): return pad_len pad_len -= 1
def decrypt_block(array, pad_len): cleartext = bytearray() for current_pl in range(pad_len, BLOCK_SIZE): new_pl = current_pl + 1 for i in range(1, new_pl): value = current_pl ^ new_pl array[-(i + BLOCK_SIZE)] = array[-(i + BLOCK_SIZE)] ^ value c = array[-(new_pl + BLOCK_SIZE)] for byte in range(0, 256): array[-(new_pl + BLOCK_SIZE)] = byte if is_padding_ok(bytes(array)): cleartext.append(c ^ new_pl ^ byte) break return cleartext[::-1]
def get_padding(blocks, current_block): idx_paderror = 0 if current_block < len(blocks) - 1: # If I am not visiting the last block, I have no padding. idx_paderror = BLOCK_SIZE else: # Else I find the Pad Length. iv_fake = bytearray(blocks[current_block - 1]) for byte in iv_fake: # Check if it is == 255 because if I do byte+1 I could get a value out of range iv_fake[idx_paderror] = byte - 1 if iv_fake[idx_paderror] == 255 else byte + 1 if not is_padding_ok(bytes(iv_fake) + blocks[current_block]): break idx_paderror += 1 return BLOCK_SIZE - idx_paderror