def hmac_sha256(key, message): """Returns the HMAC-SHA256 for the given key and message. Written following Wikipedia pseudo-code.""" if len(key) > 64: key = sha256(key).digest() if len(key) < 64: key += b'\x00' * (64 - len(key)) o_key_pad = xor_data(b'\x5c' * 64, key) i_key_pad = xor_data(b'\x36' * 64, key) return sha256(o_key_pad + sha256(i_key_pad + message).digest()).hexdigest()
def hmac_sha1(key, message): """Returns the HMAC-SHA1 for the given key and message. Written following Wikipedia pseudo-code.""" if len(key) > 64: key = unhexlify(sha1(key)) if len(key) < 64: key += b'\x00' * (64 - len(key)) o_key_pad = xor_data(b'\x5c' * 64, key) i_key_pad = xor_data(b'\x36' * 64, key) return sha1(o_key_pad + unhexlify(sha1(i_key_pad + message)))
def edit(self, ciphertext, offset, new_text): """Changes the underlying plaintext of the given ciphertext at offset so that it contains new_text. Returns the new corresponding ciphertext. """ # Get the indexes of the first and last block that will be affected by the change start_block = int(offset / AES.block_size) end_block = int((offset + len(new_text) - 1) / AES.block_size) # Find the keystream that would be used to encrypt the bytes in the affected blocks keystream = b'' cipher = AES.new(self._key, AES.MODE_ECB) for block in range(start_block, end_block + 1): # Use the block number as counter (since we "know" that the counter starts from 0) # and set the nonce to 0 (we also "know" that). keystream += cipher.encrypt(struct.pack('<QQ', 0, block)) # Find the precise bytes of the found keystream that would be used to encrypt new_text key_offset = offset % AES.block_size keystream = keystream[key_offset:key_offset + len(new_text)] # Encrypt new_text with the computed same-length keystream insert = xor_data(new_text, keystream) # Insert the new encrypted chunk in the ciphertext overwriting the underlying bytes at offset return ciphertext[:offset] + insert + ciphertext[offset + len(insert):]
def get_key_from_insecure_cbc(encryption_oracle): """Recovers the key from the lazy encryption oracle using the key also as iv. The approach used is the simple one outlined in the challenge description. """ block_length = find_block_length(encryption_oracle.encrypt) prefix_length = find_prefix_length(encryption_oracle.encrypt, block_length) # Create three different blocks of plaintext and encrypt their concatenation p_1 = 'A' * block_length p_2 = 'B' * block_length p_3 = 'C' * block_length ciphertext = encryption_oracle.encrypt(p_1 + p_2 + p_3) # Force the ciphertext to be "C_1, 0, C_1" forced_ciphertext = ciphertext[prefix_length:prefix_length + block_length] + b'\x00' * block_length + \ ciphertext[prefix_length:prefix_length + block_length] # Expect an exception from the lazy oracle try: encryption_oracle.decrypt_and_check_admin(forced_ciphertext) except Exception as e: forced_plaintext = e.args[1] # Compute the key and return it # The first block of the plaintext will be equal to (decryption of c_1 XOR iv). # The last block of the plaintext will be equal to (decryption of c_1 XOR 0). # Therefore, to get the iv (which we know is equal to the key), we can just # xor the first and last blocks together. return xor_data(forced_plaintext[:block_length], forced_plaintext[-block_length:]) raise Exception("Was not able to hack the key")
def aes_ctr(data, key, nonce): """Encrypts or decrypts with AES-CTR mode.""" output = b'' counter = 0 # Takes a block size of input at each time (or less if a block-size is not available), and XORs # it with the encrypted concatenation of nonce and counter. while data: # Get the little endian bytes concatenation of nonce and counter (each 64bit values) concatenated_nonce_and_counter = struct.pack('<QQ', nonce, counter) # Encrypt the concatenation of nonce and counter encrypted_counter = aes_ecb_encrypt(concatenated_nonce_and_counter, key) # XOR the encrypted value with the input data output += xor_data(encrypted_counter, data[:AES.block_size]) # Update data to contain only the values that haven't been encrypted/decrypted yet data = data[AES.block_size:] # Update the counter as prescribed in the CTR mode of operation counter += 1 return output
def ctr_bit_flip(oracle): """Performs a stream cipher bit flipping attack to accomplish admin privileges in the decrypted data.""" plaintext = b'?admin?true' ciphertext = oracle.encrypt(plaintext) # Prepare the data with which we want to XOR our goal ciphertext substring goal_text = b';admin=true' insert = xor_data(plaintext, goal_text) # Find the position where our goal ciphertext substring starts prefix_length = get_prefix_length(oracle) # Force our goal ciphertext block to be the encryption of our goal text forced_ciphertext = ciphertext[:prefix_length] + \ xor_data(ciphertext[prefix_length:prefix_length + len(plaintext)], insert) + \ ciphertext[prefix_length + len(plaintext):] return forced_ciphertext
def encrypt(self, plaintext): """Uses the MT19937 PRNG to generate a keystream of enough bytes (at least as long as the text we want to encrypt/decrypt), and then XORs it with the input text. """ keystream = b'' # We use all the bits of the PRNG outputs (there is no need take just 16 bits per output) while len(keystream) < len(plaintext): keystream += struct.pack('>L', self._rng.extract_number()) return xor_data(plaintext, keystream)
def crack_ctr_same_nonce(ciphertexts): """Attempt to automate the process of cracking AES-CTR when the same nonce is used repeatedly. The approach is to take all the bytes that were encrypted with the same byte of the keystream and use the singlechar_xor crypto hack that we used before to find each byte of the key. """ keystream = b'' # Take the i-th character of each ciphertext to form a column of bytes that were XORed against the same byte for i in range(max(map(len, ciphertexts))): column = b'' for c in ciphertexts: column += bytes([c[i]]) if i < len(c) else b'' # Get the most likely character that was used for the XOR keystream += get_keystream_byte(column) # Once we got the keystream, get we can easily get all the plaintexts plaintexts = [] for c in ciphertexts: plaintexts.append(xor_data(c, keystream)) return plaintexts