• 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'.index­Of(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("NBQW44ZAO52XE43UEBUXG5BANZSSAZTVMN­VWS3THEBTWM3RAMRZWY23HNIQGY23TMRVGO3DLNI­QHGZDMNNTWUZDTNJTWY2ZAONSGWZDL");
    
    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"});
    
About

Avatar for coajaxial @coajaxial started