javascriptnode.jscryptographyhmacgoogle-closure-library

goog.cryp.Hmac cannot reproduce result from crypt from node js


I am trying to reproduce HMAC signature described here.

The example is using crypt from nodejs, however, in my case, I need to use the google closure library. So I tried to reproduce the HMAC signature using the goog.crypt.Hmac library.

Below are my experiment code.

let crypto = require('crypto');
require("google-closure-library");

goog.require("goog.crypt.Hmac");
goog.require("goog.crypt");
goog.require('goog.crypt.Sha1');

function forceUnicodeEncoding(string) {
    return decodeURIComponent(encodeURIComponent(string));
}

function nodejs_crypto(string_to_sign, secret) {
    signature = crypto.createHmac('sha1', secret)
        .update(forceUnicodeEncoding(string_to_sign))
        .digest('base64')
        .trim();
    return signature
}

function goog_crypto(string_to_sign, secret) {
    const hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), goog.crypt.stringToByteArray(secret));
    const hash = hmac.getHmac(forceUnicodeEncoding(string_to_sign));
    return hash.toString()
}

const string_to_sign = "message";
const secret = "secret";
const sig1 = nodejs_crypto(string_to_sign, secret);
const sig2 = goog_crypto(string_to_sign, secret);

console.log(sig1);
// DK9kn+7klT2Hv5A6wRdsReAo3xY=

console.log(sig2);
// 12,175,100,159,238,228,149,61,135,191,144,58,193,23,108,69,224,40,223,22

I can hardly find any examples online for goog.crypt.Hmac.

Here are my problems:

  1. I am not sure if goog_crypto is implemented correctly.
  2. why hash.String() returns an array-like thing?
  3. how can I convert the hmac hash to base64 string using the closure lib.

Solution

    1. Your goog_crypto function looks right, since the result is identical, albeit represented in base64 vs array of integers (bytes)
    2. The return value of getHmac, i.e hash in your code, is an array of integers - as documented here ... an array.toString() is much the same as array.join()
    3. if you want your goog_crypto function to return base64, use nodejs Buffer toString to do the hard work for you

    i.e

    function goog_crypto(string_to_sign, secret) {
        const hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), goog.crypt.stringToByteArray(secret));
        const hash = hmac.getHmac(forceUnicodeEncoding(string_to_sign));
        return Buffer.from(hash).toString('base64');
    }
    

    Since hash is an array of Numbers, Buffer.from(hash) creates a Buffer from the numbers in hash - buffer.toString('base64') returns the buffer data encoded in base64


    Regarding point 1: to prove the results you are getting in your code are the same

    const sig1 = "DK9kn+7klT2Hv5A6wRdsReAo3xY=";
    const sig2 = '12,175,100,159,238,228,149,61,135,191,144,58,193,23,108,69,224,40,223,22';
    const sig1AsNumArrayString = atob(sig1).split('').map(c => c.charCodeAt(0)).toString();
    console.log(sig2 === sig1AsNumArrayString)