• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  FunctionPrototypeCall,
5} = primordials;
6
7const { Buffer } = require('buffer');
8
9const {
10  ScryptJob,
11  kCryptoJobAsync,
12  kCryptoJobSync,
13} = internalBinding('crypto');
14
15const {
16  validateFunction,
17  validateInteger,
18  validateInt32,
19  validateUint32,
20} = require('internal/validators');
21
22const {
23  codes: {
24    ERR_CRYPTO_SCRYPT_INVALID_PARAMETER,
25    ERR_CRYPTO_SCRYPT_NOT_SUPPORTED,
26  },
27} = require('internal/errors');
28
29const {
30  getArrayBufferOrView,
31  getDefaultEncoding,
32} = require('internal/crypto/util');
33
34const defaults = {
35  N: 16384,
36  r: 8,
37  p: 1,
38  maxmem: 32 << 20,  // 32 MiB, matches SCRYPT_MAX_MEM.
39};
40
41function scrypt(password, salt, keylen, options, callback = defaults) {
42  if (callback === defaults) {
43    callback = options;
44    options = defaults;
45  }
46
47  options = check(password, salt, keylen, options);
48  const { N, r, p, maxmem } = options;
49  ({ password, salt, keylen } = options);
50
51  validateFunction(callback, 'callback');
52
53  const job = new ScryptJob(
54    kCryptoJobAsync, password, salt, N, r, p, maxmem, keylen);
55
56  const encoding = getDefaultEncoding();
57  job.ondone = (error, result) => {
58    if (error !== undefined)
59      return FunctionPrototypeCall(callback, job, error);
60    const buf = Buffer.from(result);
61    if (encoding === 'buffer')
62      return FunctionPrototypeCall(callback, job, null, buf);
63    FunctionPrototypeCall(callback, job, null, buf.toString(encoding));
64  };
65
66  job.run();
67}
68
69function scryptSync(password, salt, keylen, options = defaults) {
70  options = check(password, salt, keylen, options);
71  const { N, r, p, maxmem } = options;
72  ({ password, salt, keylen } = options);
73  const job = new ScryptJob(
74    kCryptoJobSync, password, salt, N, r, p, maxmem, keylen);
75  const { 0: err, 1: result } = job.run();
76
77  if (err !== undefined)
78    throw err;
79
80  const buf = Buffer.from(result);
81  const encoding = getDefaultEncoding();
82  return encoding === 'buffer' ? buf : buf.toString(encoding);
83}
84
85function check(password, salt, keylen, options) {
86  if (ScryptJob === undefined)
87    throw new ERR_CRYPTO_SCRYPT_NOT_SUPPORTED();
88
89  password = getArrayBufferOrView(password, 'password');
90  salt = getArrayBufferOrView(salt, 'salt');
91  validateInt32(keylen, 'keylen', 0);
92
93  let { N, r, p, maxmem } = defaults;
94  if (options && options !== defaults) {
95    const has_N = options.N !== undefined;
96    if (has_N) {
97      N = options.N;
98      validateUint32(N, 'N');
99    }
100    if (options.cost !== undefined) {
101      if (has_N) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
102      N = options.cost;
103      validateUint32(N, 'cost');
104    }
105    const has_r = (options.r !== undefined);
106    if (has_r) {
107      r = options.r;
108      validateUint32(r, 'r');
109    }
110    if (options.blockSize !== undefined) {
111      if (has_r) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
112      r = options.blockSize;
113      validateUint32(r, 'blockSize');
114    }
115    const has_p = options.p !== undefined;
116    if (has_p) {
117      p = options.p;
118      validateUint32(p, 'p');
119    }
120    if (options.parallelization !== undefined) {
121      if (has_p) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
122      p = options.parallelization;
123      validateUint32(p, 'parallelization');
124    }
125    if (options.maxmem !== undefined) {
126      maxmem = options.maxmem;
127      validateInteger(maxmem, 'maxmem', 0);
128    }
129    if (N === 0) N = defaults.N;
130    if (r === 0) r = defaults.r;
131    if (p === 0) p = defaults.p;
132    if (maxmem === 0) maxmem = defaults.maxmem;
133  }
134
135  return { password, salt, keylen, N, r, p, maxmem };
136}
137
138module.exports = {
139  scrypt,
140  scryptSync,
141};
142