1

I've been using source code from AWS Lambda in Action - Poccia, to create users in a User Pool and Identity Pool. I keep getting the error:

Response:
{
  "errorMessage": "RequestId: f6511085-f22c-11e7-be27-534dfc5d6456 Process exited before completing request"
}

Request ID:
"f6511085-f22c-11e7-be27-534dfc5d6456"

Function Logs:
START RequestId: f6511085-f22c-11e7-be27-534dfc5d6456 Version: $LATEST
2018-01-05T15:27:38.890Z    f6511085-f22c-11e7-be27-534dfc5d6456    TypeError: Pass phrase must be a buffer
    at TypeError (native)
    at pbkdf2 (crypto.js:576:20)
    at Object.exports.pbkdf2 (crypto.js:558:10)
    at computeHash (/var/task/lib/cryptoUtils.js:10:10)
    at InternalFieldObject.ondone (/var/task/lib/cryptoUtils.js:19:4)
END RequestId: f6511085-f22c-11e7-be27-534dfc5d6456
REPORT RequestId: f6511085-f22c-11e7-be27-534dfc5d6456  Duration: 113.62 ms Billed Duration: 200 ms     Memory Size: 128 MB Max Memory Used: 33 MB  
RequestId: f6511085-f22c-11e7-be27-534dfc5d6456 Process exited before completing request

I'm new to AWS Services and am not sure why this error is occurring. Below is the Lambda function I'm attempting to use and following is the cryptoUtils.js script it's referencing.

console.log('Loading function');
//Loading standard module, such as crypto and the AWS SDK
var AWS = require('aws-sdk');
var crypto = require('crypto');
var cryptoUtils = require('./lib/cryptoUtils.js'); //Loading the cryptoUtils.js module shared code, included in the uploaded ZIP archive
var config = require('./config.json'); //Loading the configuration in the config.json file, included in the uploaded ZIP archive


var dynamodb = new AWS.DynamoDB({
  accessKeyId: 'usingKEYfromIAM',
  secretAccessKey: 'usingKEYfromIAM',
}); //Getting the Amazon DynamoDB service object
var ses = new AWS.SES(); //Getting Amazon SES service object

function storeUser(email, password, salt, fn) { //The storeUser() function stores the new user in the DynamoDB table.
  var len = 128;
  crypto.randomBytes(len, function(err, token) { //Arandom token sent in the validation email and used to validate a user
    if (err) return fn(err);
    token = token.toString('hex');
    dynamodb.putItem({ //Putting an item in the DynamoDB table
      TableName: config.DDB_TABLE, //The table name is taken from the config.json configuration file.
      //Most of the data is string ("S"), but the verifiede attribute is Boollean ("BOOL"), 
      //new users aren't verified (false), and the randomly generated token is stored in the "verifyToken" attribute
      Item: {
        email: {
          S: email
        },
        passwordHash: {
          S: password
        },
        passwordSalt: {
          S: salt
        },
        verified: {
          BOOL: false
        },
        verifyToken: {
          S: token
        }
      },
      ConditionExpression: 'attribute_not_exists (email)' //This condition avoids overwriting existing users (with the same email).
    }, function(err, data) {
      if (err) return fn(err);
      else fn(null, token); //The storeUser() function returns the randomly generated token.
    });
  });
}

function sendVerificationEmail(email, token, fn) { //The send-VerificationEmail() funciton sends the verification email to the new user.
  var subject = 'Verification Email for ' + config.EXTERNAL_NAME;
  //The verification link, to the verify.hrml page, passes the randomly generated token as a query parameter.
  var verificationLink = config.VERIFICATION_PAGE + '?email=' + encodeURIComponent(email) + '&verify=' + token;
  ses.sendEmail({ //Sending the email in HTML format
    Source: config.EMAIL_SOURCE,
    Destination: {
      ToAddresses: [
        email
      ]
    },
    Message: {
      Subject: {
        Data: subject
      },
      Body: {
        Html: {
          Data: '<html><head>' + '<meta http-equiv= "Content-Type" content="test/html; charset=UTF-8" />' +
            '<title>' + subject + '</title>' + '</head><body>' + 'Please <a href="' + verificationLink +
            '">click here to verify your email address</a> or a copy & paste the following link in a browser:' +
            '<br><br>' + '<a href="' + verificationLink + '">' + verificationLink + '</a>' + '</body></html>'
        }
      }
    }
  }, fn);
}

exports.handler = (event, context, callback) => { //The function that's exported and can be invoked using AWS Lambda as createUser
  //Getting the input parameters (email, password) from the event
  var email = event.email;
  var clearPassword = event.password;

  //Using compute-Hash() from cryptoUtils.js to salt the password.
  cryptoUtils.computeHash(clearPassword, function(err, salt, hash) {
    if (err) {
      callback('Error in hash: ' + err);
    } else {
      storeUser(email, hash, salt, function(err, token) { //Storing the user via the storeUser()function
        if (err) {
          if (err.code == 'ConditionalCheckFailedException') { //Checking if the database error is due to the email being already prsent in the database
            //userID already found
            callback(null, {
              created: false
            });
          } else {
            callback('Error in storUser: ' + err);
          }
        } else {
          sendVerificationEmail(email, token, function(err, data) { //Sending the verification email
            if (err) {
              callback('Error in sendVerificationEmail: ' + err);
            } else {
              callback(null, {
                created: true
              });
            }
          });
        }
      });
    }
  });
};

var crypto = require('crypto');

function computeHash(password, salt, fn) {
  var len = 512;
  var iterations = 4096;
  var digest = 'sha512';


  if (3 == arguments.length) {
    crypto.pbkdf2(password, salt, iterations, len, digest, function(err, derivedKey) {
      if (err) return fn(err);
      else fn(null, salt, derivedKey.toString('base64'));
    });
  } else {
    fn = salt;
    crypto.randomBytes(len, function(err, solat) {
      if (err) return fn(err);
      salt = salt.toString('base64');
      computeHash(password, salt, fn);
    });
  }
}

module.exports.computeHash = computeHash;

If anybody has any suggestions or needs more information to help me determine why the error is occurring I would greatly appreciate it. Thank you.

Ariya Darvishi
  • 97
  • 1
  • 1
  • 10
jwright
  • 11
  • 3

3 Answers3

0

The password you're passing is a number?

If so:

  • Convert it to String.

  • If you don't want to do that, you can pass a Buffer object:

Pass a Buffer object using the class Method Buffer.from(string[, encoding])
https://nodejs.org/api/buffer.html#buffer_class_method_buffer_from_string_encoding

crypto.pbkdf2(Buffer.from(password, 'utf8'), salt, iterations, len, digest, function(err, derivedKey) {
    if (err) return fn(err);
    else fn(null, salt, derivedKey.toString('base64'));
});

Hope it helps!

Ele
  • 33,468
  • 7
  • 37
  • 75
  • Thank you for your response! I implemented the code you recommended (shown above) but the same error still occurs. Do you have any more suggestions? – jwright Jan 05 '18 at 21:30
0

var crypto = require('crypto');

function computeHash(password, salt, fn) {
 // Bytesize. The larger the numbers, the better the security, but the longer it will take to complete
 var len = 512;
 var iterations = 4096;
 var digest = 'sha512';

 if (3 == arguments.length) {
 crypto.pbkdf2(Buffer.from(password, 'utf8'), salt, iterations, len, digest, function(err, derivedKey) {
    if (err) return fn(err);
    else fn(null, salt, derivedKey.toString('base64'));
});
 } else {
  fn = salt;
  crypto.randomBytes(len, function(err, salt) {
   if (err) return fn(err);
   salt = salt.toString('base64');
   computeHash(password, salt, fn);
  });
 }
}

module.exports.computeHash = computeHash;
jwright
  • 11
  • 3
0

Error "TypeError: Pass phrase must be a buffer"

does suggest trying a Buffer conversion of input String

Before your test of Buffer.from(password, 'utf8')

did you verify input value is encoding 'utf8', and not some other encoding such as base64 or latin1 ?

List of encodings that Node.js supports

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320