• Nice - thanks for posting up! So is that HMAC pretty much standard such that it can interact with other devices? Would you be willing to have that put into Espruino as a module? It seems like it'd be pretty useful to a lot of people

  • Here is a generic implementation of HMAC. I used the pseudo code from Wikipedia (https://en.wikipedia.org/wiki/HMAC) to implement this in Espruino-compatible JS.

    var HMAC = function(keyBytes, hash, blockSize) {
      var toArray = (input) => [].slice.call(input);
    
      keyBytes = toArray(keyBytes);
    
      if ( keyBytes.length > blockSize ) {
        keyBytes = toArray(hash(keyBytes));
      }
    
      while ( keyBytes.length < blockSize ) {
        keyBytes.push(0x00);
      }
    
      var o_key_pad = keyBytes.map((byte) => byte ^ 0x5c);
      var i_key_pad = keyBytes.map((byte) => byte ^ 0x36);
    
      this.digest = function(messageBytes) {
        return toArray(hash(o_key_pad.concat(hash(i_key_pad.concat(messageBytes)))));
      };
    };
    

    So, to implement a HMAC_SHA1, you can just "curry" the function:

    var crypto = require('crypto');
    
    var HMAC_SHA1 = function(keyBytes) {
      return new HMAC(keyBytes, crypto.SHA1, 64 /* Block size of SHA1 in bytes */);
    };
    

    Usage:

    var key = "some secret key";
    var message = "This is my message";
    var hmac = new HMAC_SHA1(ByteArray.fromUTF8String(key));
    console.log(ByteArray.toHexString(hmac.digest(ByteArray.fromUTF8String(message))));
    

    THis will print ba38a78074db157c10feb8ed1845975ecdf2b5d9. You can verify this here: https://www.freeformatter.com/hmac-generator.html

    I used some helper functions to convert byte arrays to string and vice-versa:

    var ByteArray = {
      fromUTF8String: function(str)
      {
        var out = [], p = 0;
        for (var i = 0; i < str.length; i++) {
          var c = str.charCodeAt(i);
          if (c < 128) {
            out[p++] = c;
          } else if (c < 2048) {
            out[p++] = (c >> 6) | 192;
            out[p++] = (c & 63) | 128;
          } else if (
              ((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
              ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
            // Surrogate Pair
            c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
            out[p++] = (c >> 18) | 240;
            out[p++] = ((c >> 12) & 63) | 128;
            out[p++] = ((c >> 6) & 63) | 128;
            out[p++] = (c & 63) | 128;
          } else {
            out[p++] = (c >> 12) | 224;
            out[p++] = ((c >> 6) & 63) | 128;
            out[p++] = (c & 63) | 128;
          }
        }
        return out;
      },
      toUTF8String: function(bytes)
      {
        var out = [], pos = 0, c = 0;
        while (pos < bytes.length) {
          var c1 = bytes[pos++];
          if (c1 < 128) {
            out[c++] = String.fromCharCode(c1);
          } else if (c1 > 191 && c1 < 224) {
            var c2 = bytes[pos++];
            out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
          } else if (c1 > 239 && c1 < 365) {
            // Surrogate Pair
            var c2 = bytes[pos++];
            var c3 = bytes[pos++];
            var c4 = bytes[pos++];
            var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) -
                0x10000;
            out[c++] = String.fromCharCode(0xD800 + (u >> 10));
            out[c++] = String.fromCharCode(0xDC00 + (u & 1023));
          } else {
            var c2 = bytes[pos++];
            var c3 = bytes[pos++];
            out[c++] =
                String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
          }
        }
        return out.join('');
      },
      toHexString: function(bytes)
      {
        var output = '';
        for ( var i = 0; i < bytes.length; i++ ) {
          var hexByte = bytes[i].toString(16);
          output += hexByte.length > 1 ? hexByte : '0' + hexByte;
        }
        return output;
      }
    };
    

    I will try to create a module, I will post further progress here.

About

Avatar for coajaxial @coajaxial started