0
votes

I want to implement the hmac algorithm with SHA-1 by the definition from RFC 2104. The code is running but the results aren't the same as the test-vectors from RFC. I'm not sure if I'm loading the values correctly(String to Hex, or String to Bytes?).

As template I've used the pseudo-code from wikipedia

I'm not sure about the terms 'blocksize' and 'output size'. In the code from wikipedia the outputsize is one of the input values but never used.

This is my code so far: First I'm setting up a hash-function, then I'm converting my input-strings (key and message) into hex values. Next step is to to look if key hast go get hashed or filled with zeros. Next I'm xor-ing the single chars from the key with those values (I don't know where they come from, but they're in every example without any comment). Last but not least I'm combining an inner string(I_key_pad + message) and hash it which results in an outer strings that im combining with the outer pad and hash it again.

    import hashlib
    from functools import reduce
    
    
    def hmac(key, message, hashfunc):
        hasher = hashlib.sha1
        blocksize = 40
        message = toHex(message) #is this right?
        key = toHex(key)
        #alternative: loading values as bytes
        #message = bytes(message, 'utf-8')
        #key = bytes(key, 'utf-8')
        if len(key) > blocksize:
            key = hasher(key)
        else:
            #key = key.ljust(blocksize, '0') #filling from right to left
            #key = key.ljust(blocksize, b'\0') #same as above but for bytes
            key = pad(key, blocksize)  #filling from left to right
    
        val1 = 0x5c 
        val2 = 0x36 
    
        i = 0
        o_key_pad = ""
        i_key_pad = ""
        while i < blocksize:
            o_key_pad += str(ord(key[i]) ^ val1)
            i_key_pad += str(ord(key[i]) ^ val2)
    
            i += 1
    
        tmp_string = str(i_key_pad) + str(message)
        tmp_string = tmp_string.encode()
        inner_hash = hasher(tmp_string).hexdigest()
        fullstring = str(o_key_pad) + inner_hash
        fullstring = fullstring.encode()
        fullstring = hasher(fullstring).hexdigest()
        print(fullstring)
    
    
    def pad(key, blocksize):
        key = str(key)
        while len(key) < blocksize:
            key = '0' + key
        key = key
        return key
    
    
    def toHex(s):
        lst = []
        for ch in s:
            hv = hex(ord(ch)).replace('0x', '')
            if len(hv) == 1:
                hv = '0' + hv
            lst.append(hv)
    
        return reduce(lambda x, y: x + y, lst)
    
    
    
    def main():
        while (1):
            key = input("key = ")
            message = input("message = ")
            hash = input("hash (0: SHA-256, 1: SHA-1) = ")
            hmac(key, message, hash)
    
    
    if __name__ == "__main__":
        main()
1
Can you please provide a complete, verifiable example that we can just run? That is a lot of code to expect people to get working by themselves and debug from the ground up.MisterMiyagi
Hey @MisterMiyagi thanks for the comment. I've added a few things. It's all about the hmac()-function. My main problem isn't that my code wouldn't work. It's more a theory to practise thing.Kodiak
I mean "provide samples for expected input and expected/actual output" so that people do not have to read through an entire RFC.MisterMiyagi
I don't understand why you are converting data to hex-encoded values, that looks wrong to me. Always work in bytes. This is python3, correct?President James K. Polk
@JamesKPolk yes this is python3. I think I've done this because of desperation. After I've finished the base of the code I've tried like 8 hours to come to the right resultsKodiak

1 Answers

0
votes

I'm not understanding all the steps in your code, but here's a short example showing HMAC-SHA1 using only hashlib.sha1, with a helper function xor.

import hashlib

def xor(x, y):
    return bytes(x[i] ^ y[i] for i in range(min(len(x), len(y))))

def hmac_sha1(key_K, data):
    if len(key_K) > 64:
        raise ValueError('The key must be <= 64 bytes in length')
    padded_K = key_K + b'\x00' * (64 - len(key_K))
    ipad = b'\x36' * 64
    opad = b'\x5c' * 64
    h_inner = hashlib.sha1(xor(padded_K, ipad))
    h_inner.update(data)
    h_outer = hashlib.sha1(xor(padded_K, opad))
    h_outer.update(h_inner.digest())
    return h_outer.digest()


def do_tests():
    # test 1
    k = b'\x0b' * 20
    data = b"Hi There"
    result = hmac_sha1(k, data)
    print(result.hex())
    # add tests as desired