summaryrefslogtreecommitdiff
path: root/lib/internal/crypto/pbkdf2.js
blob: 2fc211a87d7635f390804c47fc0c6bccf9e6633e (plain)
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
'use strict';

const errors = require('internal/errors');
const {
  getDefaultEncoding,
  toBuf
} = require('internal/crypto/util');
const { isArrayBufferView } = require('internal/util/types');
const {
  PBKDF2
} = process.binding('crypto');
const {
  INT_MAX
} = process.binding('constants').crypto;

function pbkdf2(password, salt, iterations, keylen, digest, callback) {
  if (typeof digest === 'function') {
    callback = digest;
    digest = undefined;
  }

  if (typeof callback !== 'function')
    throw new errors.TypeError('ERR_INVALID_CALLBACK');

  return _pbkdf2(password, salt, iterations, keylen, digest, callback);
}

function pbkdf2Sync(password, salt, iterations, keylen, digest) {
  return _pbkdf2(password, salt, iterations, keylen, digest);
}

function _pbkdf2(password, salt, iterations, keylen, digest, callback) {

  if (digest !== null && typeof digest !== 'string')
    throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'digest',
                               ['string', 'null']);

  password = toBuf(password);
  salt = toBuf(salt);

  if (!isArrayBufferView(password)) {
    throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'password',
                               ['string', 'Buffer', 'TypedArray']);
  }

  if (!isArrayBufferView(salt)) {
    throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'salt',
                               ['string', 'Buffer', 'TypedArray']);
  }

  if (typeof iterations !== 'number')
    throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'iterations', 'number');

  if (iterations < 0)
    throw new errors.RangeError('ERR_OUT_OF_RANGE', 'iterations');

  if (typeof keylen !== 'number')
    throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'keylen', 'number');

  if (keylen < 0 ||
      !Number.isFinite(keylen) ||
      keylen > INT_MAX) {
    throw new errors.RangeError('ERR_OUT_OF_RANGE', 'keylen');
  }

  const encoding = getDefaultEncoding();

  if (encoding === 'buffer') {
    const ret = PBKDF2(password, salt, iterations, keylen, digest, callback);
    if (ret === -1)
      throw new errors.TypeError('ERR_CRYPTO_INVALID_DIGEST', digest);
    return ret;
  }

  // at this point, we need to handle encodings.
  if (callback) {
    function next(er, ret) {
      if (ret)
        ret = ret.toString(encoding);
      callback(er, ret);
    }
    if (PBKDF2(password, salt, iterations, keylen, digest, next) === -1)
      throw new errors.TypeError('ERR_CRYPTO_INVALID_DIGEST', digest);
  } else {
    const ret = PBKDF2(password, salt, iterations, keylen, digest);
    if (ret === -1)
      throw new errors.TypeError('ERR_CRYPTO_INVALID_DIGEST', digest);
    return ret.toString(encoding);
  }
}

module.exports = {
  pbkdf2,
  pbkdf2Sync
};