Search code examples
node.jshashaws-sdkcryptojs

Node 6 upgrade caused SignatureDoesNotMatch errors from aws-sdk because of crypto.DEFAULT_ENCODING='binary'


I have moved from node 4 to node 6 and this caused anything using the aws-sdk to return the following error message:

SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

Looking into the code this seems to be caused by my code calling crypto to create and validate hashes and sets

 crypto.DEFAULT_ENCODING = 'binary' 

If I change this from 'binary' to 'buffer' (which is the default) the aws-sdk works but my code cannot validate old hashes.

This is the code used to create and check the hashes:

  crypto.pbkdf2(password, salt, iterations, keysize, function (err, derivedKey) {
      callback(null, [salt.toString('base64'), new Buffer(derivedKey).toString('base64')])
  });

Looking at the crypto documentation for crypto.DEFAULT_ENCODING I am not sure what the difference is between using buffer and binary.

Is there a way to use the binary encoding only for this crypto method and not affect the aws-sdk ?

If not is there a way of transforming a hash produced by pbkdf2 using 'buffer' encoding to what it would have produced if it was using the 'binary' encoding?


Solution

  • Looking at the crypto.pbkdf2 source code found here. The DEFAULT_ENCODING variable is only used if it is not set to "buffer" (which is the default). If it is set to some other encoding this piece of code gets executed to change the result to the desired encoding:

     // at this point, we need to handle encodings.
    if (callback) {
      function next(er, ret) {
        if (ret)
          ret = ret.toString(encoding);
        callback(er, ret);
      }
      PBKDF2(password, salt, iterations, keylen, digest, next);
    } else {
      var ret = PBKDF2(password, salt, iterations, keylen, digest);
      return ret.toString(encoding);
    }
    

    So if you remove the DEFAULT_ENCODING or set it to "buffer" and then use result.toString("binary") on the result of the pbkdf2 function you should get the exact same result as if you were using DEFAULT_ENCODING="binary" as demonstrated by the following example:

    var crypto = require("crypto");
    
    function defaultBuffer(){
        crypto.DEFAULT_ENCODING = "buffer";
        crypto.pbkdf2(password, salt, 5, 12, function (err, derivedKey) {
            console.log("Key (Buffer + toString('binary')): "+ derivedKey.toString('binary'))
          });
    
    }
    function defaultBinary(){
        crypto.DEFAULT_ENCODING = "binary";
        crypto.pbkdf2(password, salt, 5, 12, function (err, derivedKey) {
            console.log("Key (binary): "+ derivedKey)
          });
    }
    var password = 'password';
    var salt = 'salt';
    defaultBuffer();
    defaultBinary();
    

    This produces the following result:

    Key (Buffer + toString('binary')): ®?åõp~óçÁ

    Key (binary): ®?åõp~óçÁ