All files / node-user-accounts-boilerplate-nahid/crypt PBKDF2.js

87.88% Statements 29/33
80% Branches 12/15
100% Functions 8/8
87.88% Lines 29/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99    4x 4x                                     31x 31x 31x 31x 31x               6x     6x   6x       6x   6x             6x       6x   6x 6x 6x   6x                     5x   5x   5x 5x 5x 5x 5x     5x   5x       5x             4x  
"use strict";
 
const Crypt = require('./Crypt');
const crypto = require('crypto');
 
/**
 * Use crypto.pbkdf2
 * 
 * implementation adapted from https://gist.github.com/skeggse/52672ddee97c8efec269
 */
class PBKDF2 extends Crypt
{
 
  /**
   * @param {Object} [options={}]
   * @param {String} [options.hashDigest=sha512] see https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_digest_callback
   * @param {Number} [options.hashBytes=512] see https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_digest_callback
   * @param {Number} [options.saltBytes=16] see https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_digest_callback
   * @param {Number} [options.iterations=10000] see https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_digest_callback
   */
  constructor(options = {})
  {
    super();
    this.hashDigest = options.hashDigest || 'sha512';
    this.hashBytes = options.hashBytes || 512;
    this.saltBytes = options.saltBytes || 16;
    this.iterations = options.iterations || 10000;
  }
 
  /**
   * @override
   */
  hashImplementation(password)
  {
    return new Promise((resolve, reject) =>
    {
      // generate a salt for pbkdf2
      crypto.randomBytes(this.saltBytes, (err, salt) =>
      {
        Iif (err)
        {
          return reject(err);
        }
        crypto.pbkdf2(password, salt, this.iterations, this.hashBytes, this.hashDigest, (err, hash) =>
        {
          Iif (err)
          {
            console.log('XXX', err);
 
            return reject(err);
          }
 
          let combined = new Buffer(hash.length + salt.length + 8);
 
          // include the size of the salt so that we can, during verification,
          // figure out how much of the hash is salt
          combined.writeUInt32BE(salt.length, 0, true);
          // similarly, include the iteration count
          combined.writeUInt32BE(this.iterations, 4, true);
          salt.copy(combined, 8);
          hash.copy(combined, salt.length + 8);
 
          resolve(combined.toString('hex'));
        });
      });
    });
  }
 
  /**
   * @override
   */
  verifyImplementation(password, oldhash)
  {
    return new Promise((resolve, reject) =>
    {
      let combined = new Buffer(oldhash, 'hex');
      // extract the salt and hash from the combined buffer
      const saltBytes = combined.readUInt32BE(0);
      const hashBytes = combined.length - saltBytes - 8;
      const iterations = combined.readUInt32BE(4);
      const salt = combined.slice(8, saltBytes + 8);
      const hash = combined.toString('binary', saltBytes + 8);
      // verify the salt and hash against the password
 
      crypto.pbkdf2(password, salt, iterations, hashBytes, this.hashDigest, (err, verify) =>
      {
        Iif (err)
        {
          return reject(err);
        }
        resolve(verify.toString('binary') === hash);
      });
    });
  }
 
}
 
module.exports = PBKDF2;