Finally got it working using built in SHA1 of the crypto module and a custom implementation of HMAC. Performance: ~16ms per token
var crypto = require('crypto'); var TOTP = function(secret) { var base32decode = function(encoded) { encoded = encoded.toUpperCase(); var result = [], buffer = 0, next = 0, bitsLeft = 0, charValue = 0, i = 0; for ( ; i < encoded.length; i++ ) { charValue = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.indexOf(encoded.charAt(i)); buffer <<= 5; buffer |= charValue & 31; bitsLeft += 5; if (bitsLeft >= 8) { result[next++] = (buffer >> (bitsLeft - 8)) & 0xFF; bitsLeft -= 8; } } return result; }; var intToByteArray = (x) => [0, 0, 0, 0, x >> 24 & 0xFF, x >> 16 & 0xFF, x >> 8 & 0xFF, x & 0xFF]; var keyBytes = base32decode(secret); if ( keyBytes.length > 64 ) { keyBytes = [].slice.call(crypto.SHA1(keyBytes)); } while ( keyBytes.length < 64 ) { keyBytes.push(0x00); } var o_key_pad = keyBytes.map((byte) => byte ^ 0x5c); var i_key_pad = keyBytes.map((byte) => byte ^ 0x36); var digest = function(messageBytes) { return crypto.SHA1(o_key_pad.concat(crypto.SHA1(i_key_pad.concat(messageBytes)))); }; this.generate = function(tokenPeriod, digits) { tokenPeriod = (tokenPeriod == undefined ? 30 : tokenPeriod) * 1000; digits = digits == undefined ? 6 : digits; var epoch = Math.floor(new Date().getTime() / tokenPeriod); var hmacBytes = digest(intToByteArray(epoch)); var offset = hmacBytes[hmacBytes.length - 1] & 0x0F; var dt = ((hmacBytes[offset] & 0x7f) << 24) | (hmacBytes[offset + 1] << 16) | (hmacBytes[offset + 2] << 8) | hmacBytes[offset + 3]; return dt % Math.pow(10, digits); }; }; var totp = new TOTP("NBQW44ZAO52XE43UEBUXG5BANZSSAZTVMNVWS3THEBTWM3RAMRZWY23HNIQGY23TMRVGO3DLNIQHGZDMNNTWUZDTNJTWY2ZAONSGWZDL"); setWatch(function() { var start = new Date().getTime(); var otp = totp.generate(); var end = new Date().getTime(); console.log(otp); console.log('Took: ' + (end - start) + 'ms'); }, BTN, {debounce:100,repeat:true, edge:"rising"});
@coajaxial started
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.
Finally got it working using built in SHA1 of the crypto module and a custom implementation of HMAC. Performance: ~16ms per token