Пример #1
0
def AES128_CBC_decrypt(ciphertext, key):
    blocks = list(grouper(BLOCKSIZE, ciphertext))
    # Initialization vector
    last_cipherblock = blocks[0]
    plaintext = bytearray()
    for block in blocks[1:]:
        block = bytes(block)
        decrypt = AES128_decrypt(block, key)
        decrypt = xorvec(last_cipherblock, decrypt)
        plaintext.extend(decrypt)
        last_cipherblock = block
    return plaintext
Пример #2
0
def AES128_CBC_encrypt(plaintext, key, iv=None):
    if iv is None:
        iv = os.urandom(BLOCKSIZE)
    last_cipherblock = iv
    ciphertext = bytearray(iv)
    for block in grouper(BLOCKSIZE, plaintext):
        block = bytes(block)
        encrypt = AES128_encrypt(
            xorvec(last_cipherblock, block), key)
        ciphertext.extend(encrypt)
        last_cipherblock = encrypt
    return ciphertext
Пример #3
0
def crack_constant_ctr_nonce(ciphertexts):
    cx_metric = english_letters_metric

    # Put all the first (second, third, etc.) bytes of the ciphertexts
    # togther into a single "message" that we'll run XOR-cipher
    # cracking on. Just skip ciphertexts that are too short to
    # contribute a byte to the i'th message.
    messages = []
    decrypts = []
    for i in range(max(len(m) for m in ciphertexts)):
        message = bytearray()
        for m in ciphertexts:
            if i >= len(m):
                continue
            message.append(m[i])
        messages.append(message)
        decrypts.append(crack_xorchar(message, metric=cx_metric))

    # For most key bytes, there are multiple keys that score the same
    # on the english_letters_metric. Use a "maximize printable
    # characters" metric to select which to use (as it turns out, this
    # just renders it more readable without significantly reducing the
    # work required to clean up the decryption by hand, but I'll take
    # what I can get).
    key = bytearray()
    for i in range(len(decrypts)):
        # print(i)
        decryptset = decrypts[i]

        position = []
        top = cx_metric(decryptset[0][1])
        for k, t in decryptset:
            if cx_metric(t) == top:
                position.append((k, t))

        position.sort(key=lambda t: printable_metric(t[1]))
        position.reverse()
        # for k, t in position:
        #     print(k, printable_metric(t), t)
        key.append(position[0][0])

    # Start decrypting!
    ret = []
    for m in ciphertexts:
        plaintext = xorvec(key[:len(m)], m)
        #print(m, '==>', plaintext)
        ret.append(plaintext)
    return ret
Пример #4
0
def run_p16():
    print('Problem 16')
    # Get a cookie using some random data.
    cookie = p16_cookie(b'ohai')
    assert(not p16_cookie_is_admin(cookie))
    # The second block of the cookie encodes the first block of the
    # prefix. Since we know that, we can modify the IV (the first
    # block of the cookie) such that the second block will decode to
    # what we want.
    new_first_block_plaintext = b';admin=true;'
    new_first_block_plaintext += bytes((-len(new_first_block_plaintext))
                                       % BLOCKSIZE)

    old_iv = cookie[:BLOCKSIZE]
    new_iv = xorvec(old_iv,
                    P16_PREFIX[:BLOCKSIZE],
                    new_first_block_plaintext)

    new_cookie = new_iv + cookie[BLOCKSIZE:]
    if p16_cookie_is_admin(new_cookie):
        print('We made an admin cookie!')
    else:
        print('Our best attempts at forgery were insufficient. We made this cookie:')
        print(p16_cookie_decode(new_cookie))
Пример #5
0
    def crack_helper(index, known_part):
        # index: character that we're trying to deduce
        # known_part: the last (blocksize - index - 1) bytes of the block
        #   plaintext, which we should know at this point.

        # If we are trying to crack the 2nd byte of 16, we want 15
        # bytes of padding. The i'th byte corresponds to
        # index = (i - 1), and we happen to want
        # BLOCKSIZE - (i - 1) pad bytes. Very convenient.
        pad_char = BLOCKSIZE - index
        tamper_suffix_len = pad_char - 1
        assert(tamper_suffix_len + index + 1 == BLOCKSIZE)
        # Create a suffix to the tamper IV such that AES_decrypt(block) XOR
        # prev_block XOR tamper_iv will have a suffix of pad_char.
        #
        # K = padding character
        # D[i] = AES_decrypt(block)[i]
        # V[i] = prev_block[i]
        # P[i] = known_part[i]
        # T[i] = tamper_iv[i]
        #
        # We know that P[i] = V[i] ^ D[i]. We want D[i] ^ T[i] = K.
        # T[i] = K ^ D[i]
        # D[i] = P[i] ^ V[i] (yes, we could theoretically keep this around
        #   instead of recomputing it every time)
        # T[i] = K ^ P[i] ^ V[i]
        pad_suffix = bytes([pad_char] * tamper_suffix_len)
        if tamper_suffix_len > 0:
            tamper_iv_suffix = xorvec(pad_suffix,
                                      known_part[-tamper_suffix_len:],
                                      prev_block[-tamper_suffix_len:])
        else:
            tamper_iv_suffix = bytes()
        assert(len(tamper_iv_suffix) == pad_char - 1)
        # OK, now we look for tamper_iv[index] (let's call that 'T')
        # such that
        #
        # (tamper_iv^aes_decrypt(block))[index] == pad_char.
        #
        # We do that by finding a tamper_iv that results in
        # tamper_iv^aes_decrypt(block) being a block with valid
        # padding, and we do that using the padding oracle. There's a
        # wrinkle here, though; there are sometimes *two*
        # candidates. Consider the case where the target plaintext
        # ends with '\x02\x55'. Replacing 0x55 by either 0x01 *or*
        # 0x02 will produce a correctly padded block. However, the
        # tamper_iv[index] that produced 0x01 will (probably) at some
        # point lead us down a blind alley where we can not find the
        # next tamper_iv[index], and, at that point, we backtrack.
        #
        # TODO: eliminate the probabilistic element from the above.
        candidates = []
        for T in range(256):
            tamper_iv = zero_prefix(bytes([T]) + tamper_iv_suffix, BLOCKSIZE)
            if oracle(tamper_iv + block):
                candidates.append(T)
        # Now, remember, for each valid T,
        # T ^ AES_decrypt(block)[index] = pad_char
        # We want to recover the *actual* plaintext, which is:
        # plaintext_byte = prev_block[index] ^ AES_decrypt(block)[index]
        #                = prev_block[index] ^ T ^ pad_char
        candidates = [prev_block[index] ^ T ^ pad_char for T in candidates]
        return [bytes([c]) + known_part for c in candidates]