• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  FunctionPrototypeCall,
5} = primordials;
6
7const { Buffer } = require('buffer');
8
9const {
10  PBKDF2Job,
11  kCryptoJobAsync,
12  kCryptoJobSync,
13} = internalBinding('crypto');
14
15const {
16  validateFunction,
17  validateInt32,
18  validateString,
19} = require('internal/validators');
20
21const {
22  getArrayBufferOrView,
23  getDefaultEncoding,
24  normalizeHashName,
25  kKeyObject,
26} = require('internal/crypto/util');
27
28const {
29  lazyDOMException,
30  promisify,
31} = require('internal/util');
32
33function pbkdf2(password, salt, iterations, keylen, digest, callback) {
34  if (typeof digest === 'function') {
35    callback = digest;
36    digest = undefined;
37  }
38
39  ({ password, salt, iterations, keylen, digest } =
40    check(password, salt, iterations, keylen, digest));
41
42  validateFunction(callback, 'callback');
43
44  const job = new PBKDF2Job(
45    kCryptoJobAsync,
46    password,
47    salt,
48    iterations,
49    keylen,
50    digest);
51
52  const encoding = getDefaultEncoding();
53  job.ondone = (err, result) => {
54    if (err !== undefined)
55      return FunctionPrototypeCall(callback, job, err);
56    const buf = Buffer.from(result);
57    if (encoding === 'buffer')
58      return FunctionPrototypeCall(callback, job, null, buf);
59    FunctionPrototypeCall(callback, job, null, buf.toString(encoding));
60  };
61
62  job.run();
63}
64
65function pbkdf2Sync(password, salt, iterations, keylen, digest) {
66  ({ password, salt, iterations, keylen, digest } =
67    check(password, salt, iterations, keylen, digest));
68
69  const job = new PBKDF2Job(
70    kCryptoJobSync,
71    password,
72    salt,
73    iterations,
74    keylen,
75    digest);
76
77  const { 0: err, 1: result } = job.run();
78  if (err !== undefined)
79    throw err;
80
81  const buf = Buffer.from(result);
82  const encoding = getDefaultEncoding();
83  return encoding === 'buffer' ? buf : buf.toString(encoding);
84}
85
86function check(password, salt, iterations, keylen, digest) {
87  validateString(digest, 'digest');
88
89  password = getArrayBufferOrView(password, 'password');
90  salt = getArrayBufferOrView(salt, 'salt');
91  // OpenSSL uses a signed int to represent these values, so we are restricted
92  // to the 31-bit range here (which is plenty).
93  validateInt32(iterations, 'iterations', 1);
94  validateInt32(keylen, 'keylen', 0);
95
96  return { password, salt, iterations, keylen, digest };
97}
98
99const pbkdf2Promise = promisify(pbkdf2);
100async function pbkdf2DeriveBits(algorithm, baseKey, length) {
101  const { iterations, hash, salt } = algorithm;
102  if (iterations === 0)
103    throw lazyDOMException(
104      'iterations cannot be zero',
105      'OperationError');
106
107  const raw = baseKey[kKeyObject].export();
108
109  if (length === 0)
110    throw lazyDOMException('length cannot be zero', 'OperationError');
111  if (length === null)
112    throw lazyDOMException('length cannot be null', 'OperationError');
113  if (length % 8) {
114    throw lazyDOMException(
115      'length must be a multiple of 8',
116      'OperationError');
117  }
118
119  let result;
120  try {
121    result = await pbkdf2Promise(
122      raw, salt, iterations, length / 8, normalizeHashName(hash.name),
123    );
124  } catch (err) {
125    throw lazyDOMException(
126      'The operation failed for an operation-specific reason',
127      { name: 'OperationError', cause: err });
128  }
129
130  return result.buffer;
131}
132
133module.exports = {
134  pbkdf2,
135  pbkdf2Sync,
136  pbkdf2DeriveBits,
137};
138