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 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
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
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.