Example #1
0
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()
Example #2
0
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)))
Example #3
0
    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):]
Example #4
0
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")
Example #5
0
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
Example #6
0
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
Example #7
0
    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)
Example #8
0
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