Search code examples
node.jsnode-crypto

Crypto in NodeJS fails when unicode characters


I'm trying to validate data integrity using NodeJS Crypto library. It requires calculating the Hmac of a JSON string.

After some test, I've been able to locate the issue: it happens whenever it contains a unicode character. For instance:

var hasher = crypto.createHmac("sha256", 'secret_key');
hasher.write('{"timezone":"(GMT-05:00) Eastern Time (US \u0026 Canada)"}');
hasher.end();

var calculatedHmac = new Buffer(hasher.read(), 'utf8').toString('base64');
console.log(calculatedHmac);

However this returns the wrong hmac. The same code in PHP matched the signature I received from the third party service:

$data = '{"timezone":"(GMT-05:00) Eastern Time (US \u0026 Canada)"}';

$calculated_hmac = base64_encode(hash_hmac('sha256', $data, 'secret_key', true));
var_dump($calculated_hmac); // Result is correct here

If I remove the "\u0026" in the NodeJS payload, then I get the same correct result as in PHP.

Am I doing something wrong here?

Thanks!


Solution

  • The \uXXXX escape sequence notation is only supported by PHP from version 7 (reference).

    If you're using an older PHP version, these escape sequences will be passed literally (so \u0026 is a 6-character substring), whereas Node will interpret it as a single character (&).

    If you want Node to stop interpreting the escape sequence, you need to escape the backslash:

    hasher.write('{"timezone":"(GMT-05:00) Eastern Time (US \\u0026 Canada)"}');
    

    When you do that, the result for Node and PHP will be the same (0CE0++Kn9mi5xd7nAz/mWOrr7939RWwzfxhBzxAWtAk= to be exact).