• Hello,

    I've just gone down a rabbit hole trying to discover how best to do this, and have written some Python code that seems to work. I think it should be OK to translate this into Javascript.

    I found a Javascript SHA1 implementation that works in the Espruino simulator at http://webtoolkit.info/javascript_sha1.html

    I had some Python code to do TOTP tokens so had a go at rewriting it to use my own HMAC algorithm based on the Wikipedia page https://en.wikipedia.org/wiki/Hash-based_message_authentication_code

    I think the Javascript code above returns the digest as hex - it will need to be changed to return an array of bytes but I haven't looked how to do that. But once that is done, I think my python call to sha1() can just use the Javascript code.

    The "get_hotp_token" function was the original one - I forget where it came from. From what I can see my function and this function return the same results.

    I hope it should be OK to transliterate my Python code into Javascript. The 'chr' and 'ord' function should be removed - this is as the code uses Python strings, but Javascript arrays won't have this problem. Likewise, the 'b'\x00' *' part is a Python thing that would need to be rewritten for Javascript.

    I hope my code when combined with the Javascript SHA1 code gives some help anyway.

    # SHA1 block size = 512 bits, or 64 bytes
    # https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
    
    def colin_hmac(key,msg):
        # https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
        # https://raw.githubusercontent.com/python/cpython/master/Lib/hmac.py
        # key is 10 bytes, msg is 8 bytes
        # Pad key to be 64 bytes long
        blocksize = 64
        key = key + b'\x00' * (blocksize - len(key)) # Pad key with 0 byte values
        # Build up new strings with the key XORed with 0x36 and 0x5C
        k36=""
        k5c=""
        for c in key:
            k36 = k36+chr(ord(c) ^ 0x36)
            k5c = k5c+chr(ord(c) ^ 0x5c)
            
        hmac=sha1(k5c + sha1(k36 + msg).digest())
        return hmac.digest()
    
    def colin_hotp_token(secret, intervals_no):
        # Key is a 10 byte array
        key = base64.b32decode(secret, True)
        # Python debug : print the key as bytes
        [#print](https://forum.espruino.com/search/?q=%23print) "KEY",[ord(x) for x in key]
        [#keybytes](https://forum.espruino.com/search/?q=%23keybytes)=[0, 68, 50, 20, 199, 66, 64, 17, 12, 133]
        # msg is 8 bytes long - number with last byte = least significant
        msg = struct.pack(">Q", intervals_no)
        [#msgbytes](https://forum.espruino.com/search/?q=%23msgbytes)=[0, 0, 0, 0, 0, 0, 0, 1]
    
        colin_hm=colin_hmac(key,msg)
        binary=offset=ord(colin_hm[19]) & 0x0f
        binary=((ord(colin_hm[offset]) & 0x7f) << 24) | ((ord(colin_hm[offset+1]) & 0xff) << 16) | ((ord(colin_hm[offset+2]) & 0xff) << 8 ) | (ord(colin_hm[offset+3]) & 0xff)
        # We want the last 6 digits
        return binary % 1000000
        
    def get_hotp_token(secret, intervals_no):
        key = base64.b32decode(secret, True)
        msg = struct.pack(">Q", intervals_no)
        hm = hmac.new(key, msg, hashlib.sha1)
        h = hm.digest() # Return byte array
        o = ord(h[19]) & 0x0f
        h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000   
        return h
    
    
About

Avatar for ColinP @ColinP started