Exemplo n.º 1
0
def findBlockSize(oracle, upper_bound=256):
    """ Given a function and assuming that it pads its input with some prefix and suffix and then applies a block cipher such that each block of the ciphertext depends only on the key and the corresponding block of the plaintext (for example, AES-ECB), find the block size of the cipher.

    Algorithm: 
        If the above description is true of a function, then the length of its output grows in discrete steps with the size of each step equal to the block size. So we compute the length of the function's output on empty input, and then feed the function increasingly long messages until the length of the output jumps. The difference between the new output length and the length on empty input is the block size.

    Args:
        function (function: Message -> Message): the function whose block size is to be determined.

        upper_bound (int): the upper bound on the range of possible block sizes to check. Must be an integer in [1, 255] inclusive.

    Returns:
        int: the likely block size of the cipher (a positive integer in range [1, 'upper_bound'-1]).

    Raises:
        AssertionError, "'upper_bound' must be an integer in range [1, 255] inclusive": if 'upper_bound' is invalid.

        InvalidAssumptions: in case the output length does not jump before 'upper_bound' is reached. 
    """
    assert (0 < upper_bound and upper_bound < 256), "'upper_bound' must be an integer in range [1, 255] inclusive"

    init_length = len(oracle(Message(b'')))
    block_size = 0
    for k in range(upper_bound):
        test_len = len(oracle(Message(b'\x00' * k)))
        if test_len > init_length:
            block_size = test_len - init_length
            return block_size
    raise InvalidAssumptions
Exemplo n.º 2
0
def AES_ECB(msg, key, fn='encrypt', strict=True):
    """ Encrypts or decrypts a message under a specified key using AES in ECB mode.

    Args:
        msg (Message): the message to be en/decrypted.
        
        key (Message): the key to be used for en/decryption.

        fn (string): the operation which should be performed using the cipher. Options are 'encrypt' (default) and 'decrypt'.
        
        strict (bool): if True, expect that padding scheme always adds padding bytes to messages even if the length of the message is a multiple of the block size (as PKCS#7 padding requires); if False, expect that padding bytes are added only if length of the message is not a multiple of the block size (as is desirable for non-terminal message blocks in AES-CBC). 

    Returns:
        Message: the en/decryption of 'msg' under 'key' using AES in ECB mode.

    Raises:
        InvalidFn: if 'fn' is not equal to 'encrypt' or 'decrypt'.
    """
    if fn not in valid_fns:
        raise InvalidFn

    cipher = AES.new(key.bytes, AES.MODE_ECB)
    if fn == 'encrypt':
        blocks = listBlocks(msg.pad(16, strict))
        out_blocks = [Message(cipher.encrypt(block.bytes)) for block in blocks]
    else:
        blocks = listBlocks(msg)
        out_blocks = [Message(cipher.decrypt(block.bytes)) for block in blocks]
    return joinBlocks(out_blocks)
Exemplo n.º 3
0
def test_Message_createDisplayInt(test_int, end):
    if test_int < 0:
        with raises(Exception):
            test_msg = Message(test_int, 'int', end)
    else:
        test_msg = Message(test_int, 'int', end)
        assert test_msg.int(end) == test_int
Exemplo n.º 4
0
def decryptPostfixECB(oracle):
    """ Given a function and assuming that it concatenates its input with a prefix and postfix, and then applies a block cipher such that each block of the ciphertext depends only on the key and the corresponding block of the plaintext (e.g., AES-ECB), decrypt the postfix without using the decryption key.

    Args:
        function (function: Message -> Message): a function which satisfies the assumptions described above.
        
        verbose (bool): if True, prints each block of the postfix marquee-style as it is decrypted.

    Returns:
        Message: the postfix which 'function' concatenates with its input before encryption.
    """
    block_size = findBlockSize(oracle)

    prefix_blocks = prefixBlocks(oracle, block_size)
    offset = prefixOffset(oracle, block_size, prefix_blocks)
    prefix_length = prefixLength(block_size, prefix_blocks, offset)
    postfix_length = postfixLength(oracle, block_size, prefix_length)
    num_blocks = int(ceil(postfix_length / block_size))

    known_bytes = Message(b'')
    out_blocks = [Message(b'\x00' * block_size)]
    for block in range(num_blocks):
        for ch in range(block_size):
            known_bytes += decryptPostfixByteECB(oracle, block_size, offset, known_bytes, prev_block=out_blocks[block])
        out_blocks.append(known_bytes)
        known_bytes = Message(b'')
    # slice off garbage bytes in first block and join blocks
    out = joinBlocks(out_blocks[1:])
    return out
Exemplo n.º 5
0
def prefixBlocks(oracle, block_size=16):
    """ Given a function and assuming that it concatenates its input with a prefix (and possibly suffix) and then applies a block cipher such that each block of the ciphertext depends only on the key and the corresponding block of the plaintext (e.g., AES-ECB), and given the block size of the cipher, find the number of blocks which consist exclusively of prefix bytes. 

    Algorithm:
        We first compute the output of the function on two different one-byte inputs: say, '\x00' and '\x01'. If the prefix fully occupies the first m blocks of the input to the block cipher, then the first m blocks of the two outputs will agree. If in addition the (m+1)st block is not fully occupied by the prefix, then the (m+1)st blocks of the two outputs will disagree. Therefore, the number of blocks fully occupied by the prefix is the number of blocks before the first disagreement in the two outputs.

    Args:
        function (function: Message -> Message): a function which is assumed to be of the form described above.

        block_size (int): the block size of the cipher assumed to be used by 'cipher'. Must be in [1, 255] inclusive. Default value is 16. 

    Returns:
        int: the number of complete blocks occupied by the prefix. 

    Raises:
        InvalidAssumptions: if the lengths of the two test vectors are unequal, or if their length is not a multiple of 'block_length'.
    """
    vec0 = oracle(Message(b'\x00'))
    vec1 = oracle(Message(b'\x01'))

    assert (0 < block_size and block_size < 256), "\'block_size\' must be an integer in range [1, 255] inclusive"
    if len(vec0) != len(vec1) or len(vec0) % block_size != 0:
        raise InvalidAssumptions
        
    num_blocks = int(len(vec0)/block_size)
    for k in range(num_blocks):
        low = block_size * k
        high = block_size * (k + 1)
        if vec0[low:high] != vec1[low:high]:
            return k
    return num_blocks
Exemplo n.º 6
0
 def __init__(self):
     self.oracle = Oracle(None, prefix, postfix)
     self.sep_field = Message(b';')
     self.sep_key = Message(b'=')
     self.default_keys = [
         Message(b'comment1'),
         Message(b'userdata'),
         Message(b'comment2')
     ]
Exemplo n.º 7
0
def forgeAdminCookieCBC(factory, try_byte):
    """ Produce a string which is validated as an admin token
    by tools.validateAuthString, without knowledge of that
    function's decryption key.

    Method:
        We'll produce an encryption of the following
        string (shown here with '|'s inserted to show
        divisions into blocks of 16 bytes):

'00000000000000000|comment1=cooking|%20MCs;userdata=|;comment2=%20like|%20a%20pound%20of|%20bacon'

        using tools.newAuthString (with empty input). Then
        we'll XOR this encrypted string with a second string
        such that the decryption of the modified string will
        have ";admin=heckyeahX" (where X is some character) as
        its 4th block.

        The reason we can do this is that flipping the j-th
        bit in the k-th block of an AES-CBC ciphertext will
        flip the j-th bit in the (k + 1)-th block of its
        decryption. (Notice that the k-th block of the cipher-
        text gets XOR'd with the decryption of the (k + 1)-th
        block of the ciphertext to produce the (k + 1)-th block
        of the plaintext!).

        Specifically, we'll modify the 3rd block of the
        ciphertext by XORing it with XOR(';admin=heckyeahX',
        ';comment2=%20lik'), where X runs over some range of 
        characters until the decryption of the modified string
        parses properly. If the decryption parses, it will also
        be validated as an admin token because it contains
        'admin' as a key.

        The reason our first choice of X might not succeed is 
        that, depending on the key (tools.rand_key), the 
        decryption of the 3rd block might contain some meta-
        characters which prevent the decrypted string from 
        parsing properly. In practice, this method usually 
        succeeds in one or two tries. 

    Arg:
        verbose (bool): if True, print some information about 
        the intermediate steps; silent if False.

    Returns:
        bool: True if a string which is validated as an admin
        token was successfully produced; False if not.
    """
    cookie = factory.newCookie('')
    current = Message(b';comment2=%20lik')
    new_cookie = Message(b';comment2=%20lik')
    wish = Message(b';admin=true;xx=') + Message(bytes([try_byte]))
    diff = XOR(current, wish)
    forged = cookie[:32] + XOR(cookie[32:48], diff) + cookie[48:]
    return forged
Exemplo n.º 8
0
def test_challenge02_example():
    hex_1 = '1c0111001f010100061a024b53535009181c'
    hex_2 = '686974207468652062756c6c277320657965'
    true_xor_hex = '746865206b696420646f6e277420706c6179'

    msg_1 = Message(hex_1, 'hex')
    msg_2 = Message(hex_2, 'hex')
    test_xor_hex = XOR(msg_1, msg_2).hex()

    assert true_xor_hex == test_xor_hex
Exemplo n.º 9
0
 def isAdminCookie(self, msg):
     decr_msg = self.oracle.decryptCTR(msg)
     try:
         token = Token.fromMsg(decr_msg, Message(b';'), Message(b'='))
     except IndexError:
         raise InvalidToken
     try:
         return token.data[Message(b'admin')] == Message(b'true')
     except KeyError:
         return False
Exemplo n.º 10
0
def findAffixLengthCTR(oracle):
    prefix_len, suffix_len = 0, 0
    ciphertext_1 = oracle.encryptCTR(Message(b'\x00'))
    ciphertext_2 = oracle.encryptCTR(Message(b'\x01'))
    diff = XOR(ciphertext_1, ciphertext_2)
    for offset in range(len(diff)):
        if diff[offset] != Message(b'\x00'):
            prefix_len = offset
    suffix_len = len(ciphertext_1) - 1 - prefix_len
    return (prefix_len, suffix_len)    
Exemplo n.º 11
0
 def __init__(self, key=None, prefix=None, postfix=None):
     if key is None:
         key = randMsg(16)
     if prefix is None:
         prefix = Message(b'')
     if postfix is None:
         postfix = Message(b'')
     self.key = key
     self.prefix = prefix
     self.postfix = postfix
Exemplo n.º 12
0
 def checkHMACSHA1_insecure(self, msg, hmac):
     true_hmac_bytes = Message(hmacSHA1(self.key, msg), 'hex').bytes
     test_hmac_bytes = Message(hmac, 'hex').bytes
     for (byte1, byte2) in zip(true_hmac_bytes, test_hmac_bytes):
         if byte1 != byte2:
             return False
         sleep(.05)
     if len(true_hmac_bytes) != len(test_hmac_bytes):
         return False
     else:
         return True
Exemplo n.º 13
0
def hmacSHA1(key, msg):
    s = SHA1()
    block_size = 64
    hash_size = 20
    if len(key) > block_size:
        key = Message(s.hash(key), 'hex')
    key += Message(b'\x00' * (block_size - len(key)))
    outer_key = XOR(key, Message(b'\x5c' * block_size))
    inner_key = XOR(key, Message(b'\x36' * block_size))
    inner_msg = Message(s.hash(inner_key), 'hex') + msg
    hmac = s.hash(outer_key + inner_msg)
    return hmac
Exemplo n.º 14
0
 def isAdminAuthCookie(self, mac_pair):
     (cookie, mac) = mac_pair
     if not self.oracle.checkMACMD4(cookie, mac):
         raise BadMAC
     try:
         token = Token.fromMsg(cookie, Message(b';'), Message(b'='))
     except IndexError:
         raise InvalidToken
     try:
         return token.data[Message(b'admin')] == Message(b'true')
     except KeyError:
         return False
Exemplo n.º 15
0
def postfixLength(oracle, block_size=16, prefix_length=0):
    """ Given a function and assuming that it concatenates its input with a prefix and postfix, and then applies a block cipher such that each block of the ciphertext depends only on the key and the corresponding block of the plaintext (e.g., AES-ECB), and given the block size of the cipher and the length of the prefix, find the length of the postfix.

    Algorithm:
        Find the length of the output of the function when given the empty string as input. Feed the function increasingly long messages of 0 bytes until the length of the output jumps up to the next multiple of the block size.

        Here is an illustration of the input to the function's cipher. First with the empty message as our input:

    ...pp|pppPPPPPPPPPPPPP|PPPPxxxxxxxxxxxx|
   ^ prefix  ^ postfix         ^  padding

        then with one 0 byte as input:

    ...pp|ppp0PPPPPPPPPPPP|PPPPPxxxxxxxxxxx|

        then more...
     
    ...pp|ppp00000000000PP|PPPPPPPPPPPPPPPPx|
     
        then just enough to make the length jump:

    ...pp|ppp000000000000P|PPPPPPPPPPPPPPPPP|xxxxxxxxxxxxxxxx|
     
        The number of bytes we had to enter to get to the first jump in output length is the number of bytes of padding which appeared at the end of the postfix in the input to the function's cipher when we fed the empty message to the function. Subtracting the number of padding bytes and the length of the prefix from the length of the output on the empty string, we get the length of the postfix.

    Args:
        function (function: Message -> Message): a function satisfying the assumptions above. 

        block_size (int): the block size of the cipher used in 'function'. Must be in range [1, 255] inclusive. Default value is 16.

        prefix_length (int): the length of the prefix which 'function' prepends to messages before encryption. Must be a nonnegative integer.

    Returns: 
        int: the length of the postfix which 'function' appends to messages before encryption. A nonnegative integer.

    Raises:
        InvalidAssumptions: if the output length does not jump in 'block_size' steps or fewer. This may indicate that the specified block size is incorrect.
    """
    assert (1 <= block_size and block_size <= 256), "\'block_size\' must be an integer in [1, 256] inclusive"
    assert (0 <= prefix_length), "\'prefix_length\' must be a nonnegative integer"

    empty_len = len(oracle(Message(b'')))
    for k in range(1, (block_size + 1)):
        fill = Message(b'\x00' * k)
        test_len = len(oracle(fill))
        if test_len > empty_len:
            break
    if k > block_size:
        raise InvalidAssumptions
    postfix_length = empty_len - prefix_length - k
    return postfix_length
Exemplo n.º 16
0
def crackEditableCTR(ciphertext, edit_fn):
    keystream = Message(b'')
    for offset in range(len(ciphertext)):
        for msg_byte in [Message(bytes([val])) for val in range(256)]:
            found_byte = False
            edit = edit_fn(ciphertext, offset, msg_byte)
            if edit[offset] == Message(b'\x00'):
                found_byte = True
                keystream += msg_byte
                break
        if msg_byte is Message(b'\xff') and not found_byte:
            raise Exception('Keystream byte not found')
    plaintext = XOR(keystream, ciphertext)
    return plaintext
Exemplo n.º 17
0
 def newResetToken(self, email, test_mode=False):
     email_msg = Message(email, 'ascii')
     token = self.encryptMT19937_32_CTR(email_msg)
     if test_mode:
         return (self.time_seed, self.key, token)
     else:
         return token
Exemplo n.º 18
0
def mtResetFindSeed(reset_fn, user_input, test_mode=False, verbose=False):
    now = int(time())
    hour = 3600
    if test_mode:
        (time_seed, seed, ciphertext) = reset_fn(user_input, test_mode=True)
    else:
        ciphertext = reset_fn(user_input, test_mode=False)
    user_msg = Message(user_input, 'ascii')
    known_len = len(user_msg)
    prefix_len = len(ciphertext) - known_len
    known_ctext = ciphertext[prefix_len:]
    known_keystream = XOR(user_msg, known_ctext)

    if verbose:
        print("Password reset token:", ciphertext)
        print("Prefix length:", prefix_len)
        print("Known slice of keystream:", known_keystream)

    guess_rand_seed = mtCTRCheckSeedRange(known_keystream, prefix_len, 0,
                                          2**16 - 1)
    if guess_rand_seed:
        if test_mode:
            assert (seed == guess_rand_seed and time_seed is False)
        return (False, guess_rand_seed)
    guess_time_seed = mtCTRCheckSeedRange(known_keystream, prefix_len,
                                          now - hour, now + 1)
    if guess_time_seed:
        if test_mode:
            assert (seed == guess_time_seed and time_seed is True)
        return (True, guess_time_seed)
    if test_mode:
        return (None, None)
    else:
        return None
Exemplo n.º 19
0
def paddingOracleBlock(validation_oracle, block, prev_block, block_size=16):
    """ Given an implementation of AES-CBC whose decryption
    function rejects messages with invalid PKCS#7 padding, and
    a pair of consecutive blocks of an AES-CBC ciphertext,
    decrypt the second block without using the decryption key.
    
    Method:
        See the docstring of tools.paddingOracleByte.

    Args:
        block (Message): the block of the ciphertext which is
        to be decrypted.

        prev_block (Message): the block of the ciphertext 
        which precedes 'block'.

        block_size (int): the block size of the AES cipher. 
        Default value is 16.

    Returns:
        Message: the decryption of 'block'.
    """
    known_bytes = Message(b'')
    for offset in range(1, 17):
        known_bytes = paddingOracleByte(validation_oracle, block, prev_block, known_bytes, offset, block_size) + known_bytes
    return known_bytes
Exemplo n.º 20
0
def decryptSeries(factory, cookie_vals, cookie_id_byte, verbatim=True):
    found = [False for _ in range(cookie_vals)] 
    goal = [True for _ in range(cookie_vals)]
    decrypts = [Message(b'') for _ in range(cookie_vals)]
    
    while found != goal:
        cookie = factory.newCookie()
        decryption = paddingOracle(factory.isValidCookie, cookie)
        cookie_val = int(decryption[cookie_id_byte].bytes)
        if not found[cookie_val]:
            found[cookie_val] = True
            decrypts[cookie_val] = decryption
            print("Found cookie", cookie_val)
    print("Putting it all together...\n")
    if not verbatim:
        decrypts = [decrypt.stripPad(strict=False) for decrypt in decrypts]
    series = Message(b'\n'.join(decrypt.bytes for decrypt in decrypts))
    return(series)
Exemplo n.º 21
0
 def __init__(self, time_seed=False):
     self.prefix = randMsg(0, 20)
     self.postfix = Message(b'')
     if time_seed == 'coin_flip':
         time_seed = True if randint(0, 1) else False
     if time_seed is True:
         self.key = int(time())
     else:
         self.key = randint(0, 2**16 - 1)
     self.time_seed = time_seed
Exemplo n.º 22
0
    def message(cls) -> Message:
        """Esse método estatico retorna ou cria uma instancia da 
        classe Message.

        Returns:
            Message: instancia de Message.
        """
        if not cls._instance:
            cls._instance = Message()
        return cls._instance
Exemplo n.º 23
0
def test_Message_padThenStripPad(test_msg, block_size, strict):
    if block_size < 1 or block_size > 255:
        with raises(AssertionError):
            test_msg.pad(block_size, strict).stripPad(block_size, strict)
    elif strict is False and test_msg == Message(b''):
        with raises(AssertionError):
            test_msg.pad(block_size, strict).stripPad(block_size, strict)
    else:
        orig_msg = test_msg
        test_msg.pad(block_size, strict).stripPad(block_size, strict)
        assert orig_msg == test_msg
Exemplo n.º 24
0
def keystreamCTR(key, length, offset=0, nonce=None, block_size=16):
    """
    Generate a sequence of keystream bytes, of a specified length and beginning at a specified offset, for AES in CTR mode. 

    Args:
        key (Message): the symmetric key to be used for en/decryption. Must have length equal to 'block_size'.

        length (int): the length of the desired keystream. Must be a positive integer.

        offset (int): the offset into the full keystream of the first byte of the sequence. Must be a nonnegative integer; default value is 0.

        nonce (Message): a one-time Message instance used to randomize the keystream. Must have length equal to 'block_size'/2. Default value is a message of zero bytes of appropriate length.

        block_size (int): the length in bytes of a block. Must be an even integer in range [2, 254] inclusive. Default value is 16.

    Returns:
        Message: the portion of the keystream of length 'length' and beginning at 'offset' bytes into the full AES-CTR keystream produced using key 'key' and nonce 'nonce. 

    Raises:
        AssertionError, "Block size must be an even integer in range [2, 254] inclusive": if 'block_size' is invalid.
        
        AssertionError, "Length of keystream must be positive": if 'length' is invalid.

        AssertionError, "Offset must be nonnegative": if 'offset' is invalid.
    """
    assert (0 < block_size and block_size < 256 and block_size % 2 == 0), "Block size must be an even integer in range [2, 254] inclusive"
    assert(0 < length), "Length of keystream must be positive"
    assert(0 <= offset), "Offset must be nonnegative"
    if nonce is None:
        nonce = Message(b'\x00' * int(block_size / 2))
    start_block = int(offset / block_size)
    end_block = int((offset + length) / block_size)
    start_block_offset = offset % block_size
    keystream = Message(b'')
    ctr = start_block
    while ctr <= end_block:
        ctr_msg = nonce + Message(hex(ctr).lstrip('0x').rjust(16, '0'), 'hex', 'little')
        keystream += AES_ECB(ctr_msg, key, fn='encrypt', strict=False)
        ctr += 1
    keystream = keystream[start_block_offset: start_block_offset + length]
    return keystream
Exemplo n.º 25
0
def hammingDistance(msg1, msg2):
    """ Compute the Hamming distance between the byte arrays represented by two Message instances.

    If the two messages are not of equal byte length, then the shorter message is padded with zero bytes (on the high-order side) to the length of the longer message. 

    Args:
        msg1 (Message): one of the two messages to be compared.

        msg2 (Message): the other message to be compared.
        
    Returns:
        int: the number of places at which the bit values of the byte arrays represented by 'msg1' and 'msg2'. Always a nonnegative integer. 
    """
    if len(msg1) < len(msg2):
        msg1 = Message(b'\x00' * (len(msg2) - len(msg1))) + msg1
    elif len(msg2) < len(msg1):
        msg2 = Message(b'\x00' * (len(msg1) - len(msg2))) + msg2

    xor = XOR(msg1, msg2)
    dist = sum(int(bit) for bit in xor.bin())
    return dist
Exemplo n.º 26
0
    def formPad(self, msg_len):
        # encode the bit length of msg as a big-endian 8-byte word
        orig_bit_len = msg_len * 8
        orig_len_msg = Message(orig_bit_len, 'int')
        orig_len_msg_padded = Message(b'\x00' *
                                      (8 - len(orig_len_msg))) + orig_len_msg

        # add a '1' bit to msg on the less-significant end and fill it out to a byte with 0s
        pad = Message(b'\x80')
        padded_msg_len = msg_len + len(pad)

        # pad with 0 bytes until padded message length is 8 bytes short of a multiple of 64 bytes
        if (padded_msg_len % 64 <= 56):
            pad += Message(b'\x00' * (56 - (padded_msg_len % 64)))
        else:
            pad += Message(b'\x00' * (56 + 64 - (padded_msg_len % 64)))

        # add encoding of bit length of the original msg
        # length of padded msg should now be a multiple of 64 bytes
        pad += orig_len_msg_padded
        assert ((msg_len + len(pad)) % 64 == 0)
        return pad
Exemplo n.º 27
0
def breakIVKeyCBC(factory):
    block_size = 16
    user_input = 'A' * 16 * 3
    cookie = factory.newCookie(user_input)
    cookie_first_block = cookie[:block_size]
    broken_cookie = cookie_first_block + Message(
        b'\x00' * 16) + cookie_first_block

    with open('data/challenge27.log', 'w') as error_log:
        with redirect_stderr(error_log):
            try:
                factory.isAdminCookie(broken_cookie)
            except InvalidAscii:
                pass

    with open('data/challenge27.log', 'rb') as error_log:
        decrypt = Message(bytes(error_log.read(block_size * 3)))

    decrypt_first_block = decrypt[:block_size]
    decrypt_third_block = decrypt[-block_size:]
    key = XOR(decrypt_first_block, decrypt_third_block)
    return (key)
Exemplo n.º 28
0
 def newUserProfile(self, email):
     user_data = OrderedDict.fromkeys(self.default_keys)
     user_data[Message(b'email')] = Message(email, 'ascii')
     user_data[Message(b'uid')] = Message(str(randint(uid_min, uid_max)), 'ascii')
     user_data[Message(b'role')] = Message(b'user')
     token = Token(user_data, self.sep_field, self.sep_key)
     encr_token = self.oracle.encryptECB(token.msg)
     return encr_token
Exemplo n.º 29
0
def decryptPostfixByteECB(oracle, block_size=16, offset=0, prev_bytes='', prev_block=None):
    """ Given a function which appends a prefix and suffix to user-supplied messages and then encrypts the result with a block cipher in which the encryption of a block depends only on the key and the corresponding block of the plaintext (e.g., AES-ECB), and some data about the function (specified in the arg list) and optionally some known bytes of the postfix, decrypts the next byte of the postfix without using the decryption key.

    Args:
        function (function: Message -> Message):  a function which satisfies (or probably satisfies) the description above.

        block_size (int): the block size of the cipher used by 'function'. Must be in range [1, 255] inclusive. Default value is 16.

        offset (int): the number of bytes remaining in the last full block touched by the prefix. Must be in range [1, 'block_size' - 1] inclusive.

        prev_bytes (string): the bytes preceding the target byte in the same block as the target byte. Must have length in range [0, 'block_size' - 1] inclusive.

        prev_block (string): the block preceding the block containing the target byte. If target byte belongs to the first block of the prefix, instead pass '\x00' * 'block_size'. Must have length equal to 'block_size'.

    Returns:
        Message: the message of length 1 representing the byte of the postfix following 'prev_bytes', or of the first byte of the next block of the postfix if 'prev_bytes' was empty. 
    """
    assert (0 < block_size and block_size < 256), "\'block_size\' must be a in integer in [1, 256] inclusive."
    assert (0 <= offset and offset < block_size), "\'offset\' must be a positive integer less than \'block_size\'"
    assert (len(prev_bytes) >= 0 and len(prev_bytes) < block_size), "\'prev_bytes\' must have length in [0, \'block_size\' - 1] inclusive."
    
    known_len = len(prev_bytes)
    assert (0 <= known_len and known_len < block_size), "The length of \'known_bytes\' must be in [0, \'block_size\'-1] inclusive."

    if prev_block is None:
        prev_block = Message(b'\x00' * block_size)
    assert (len(prev_block) == block_size), "If a \'prev_block\' is provided, its length must be equal to \'block_size\'."

    filler_len = block_size - 1 - known_len
    null_fill = Message(b'\x00' * block_size)
    for test_byte in [Message(bytes([k])) for k in range(256)]:
        test_vec = prev_block[known_len + 1:] + prev_bytes + test_byte
        padded_test_vec = null_fill[:offset] + test_vec + null_fill[:filler_len]
        ciphertext = oracle(padded_test_vec)
        if isAES_ECB(ciphertext):
            return test_byte
    return Message(b'')
Exemplo n.º 30
0
def extendMACMD4(msg, mac, add_msg, key_len):
    state_vars = 4
    state_hex_digs = 8
    state = [
        Message(mac[k * state_hex_digs:(k + 1) * state_hex_digs], 'hex',
                'little').int() for k in range(state_vars)
    ]

    s = MD4()
    orig_msg_pad = s.formPad(len(msg) + key_len)
    new_msg = msg + orig_msg_pad + add_msg
    new_msg_pad = s.formPad(len(new_msg) + key_len)

    extended_mac = s.hash(add_msg + new_msg_pad, state, pad=False)
    return (new_msg, extended_mac)