• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const { AsyncWrap, Providers } = internalBinding('async_wrap');
4const { Buffer } = require('buffer');
5const { scrypt: _scrypt } = internalBinding('crypto');
6const { validateInteger, validateUint32 } = require('internal/validators');
7const {
8  ERR_CRYPTO_SCRYPT_INVALID_PARAMETER,
9  ERR_CRYPTO_SCRYPT_NOT_SUPPORTED,
10  ERR_INVALID_CALLBACK
11} = require('internal/errors').codes;
12const {
13  getDefaultEncoding,
14  getArrayBufferView,
15} = require('internal/crypto/util');
16
17const defaults = {
18  N: 16384,
19  r: 8,
20  p: 1,
21  maxmem: 32 << 20,  // 32 MB, matches SCRYPT_MAX_MEM.
22};
23
24function scrypt(password, salt, keylen, options, callback = defaults) {
25  if (callback === defaults) {
26    callback = options;
27    options = defaults;
28  }
29
30  options = check(password, salt, keylen, options);
31  const { N, r, p, maxmem } = options;
32  ({ password, salt, keylen } = options);
33
34  if (typeof callback !== 'function')
35    throw new ERR_INVALID_CALLBACK(callback);
36
37  const encoding = getDefaultEncoding();
38  const keybuf = Buffer.alloc(keylen);
39
40  const wrap = new AsyncWrap(Providers.SCRYPTREQUEST);
41  wrap.ondone = (ex) => {  // Retains keybuf while request is in flight.
42    if (ex) return callback.call(wrap, ex);
43    if (encoding === 'buffer') return callback.call(wrap, null, keybuf);
44    callback.call(wrap, null, keybuf.toString(encoding));
45  };
46
47  handleError(_scrypt(keybuf, password, salt, N, r, p, maxmem, wrap));
48}
49
50function scryptSync(password, salt, keylen, options = defaults) {
51  options = check(password, salt, keylen, options);
52  const { N, r, p, maxmem } = options;
53  ({ password, salt, keylen } = options);
54  const keybuf = Buffer.alloc(keylen);
55  handleError(_scrypt(keybuf, password, salt, N, r, p, maxmem));
56  const encoding = getDefaultEncoding();
57  if (encoding === 'buffer') return keybuf;
58  return keybuf.toString(encoding);
59}
60
61function handleError(ex) {
62  if (ex === undefined)
63    return;
64
65  if (ex === null)
66    throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();  // Bad N, r, p, or maxmem.
67
68  throw ex;  // Scrypt operation failed, exception object contains details.
69}
70
71function check(password, salt, keylen, options) {
72  if (_scrypt === undefined)
73    throw new ERR_CRYPTO_SCRYPT_NOT_SUPPORTED();
74
75  password = getArrayBufferView(password, 'password');
76  salt = getArrayBufferView(salt, 'salt');
77  validateUint32(keylen, 'keylen');
78
79  let { N, r, p, maxmem } = defaults;
80  if (options && options !== defaults) {
81    let has_N, has_r, has_p;
82    if (has_N = (options.N !== undefined)) {
83      N = options.N;
84      validateUint32(N, 'N');
85    }
86    if (options.cost !== undefined) {
87      if (has_N) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
88      N = options.cost;
89      validateUint32(N, 'cost');
90    }
91    if (has_r = (options.r !== undefined)) {
92      r = options.r;
93      validateUint32(r, 'r');
94    }
95    if (options.blockSize !== undefined) {
96      if (has_r) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
97      r = options.blockSize;
98      validateUint32(r, 'blockSize');
99    }
100    if (has_p = (options.p !== undefined)) {
101      p = options.p;
102      validateUint32(p, 'p');
103    }
104    if (options.parallelization !== undefined) {
105      if (has_p) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
106      p = options.parallelization;
107      validateUint32(p, 'parallelization');
108    }
109    if (options.maxmem !== undefined) {
110      maxmem = options.maxmem;
111      validateInteger(maxmem, 'maxmem', 0);
112    }
113    if (N === 0) N = defaults.N;
114    if (r === 0) r = defaults.r;
115    if (p === 0) p = defaults.p;
116    if (maxmem === 0) maxmem = defaults.maxmem;
117  }
118
119  return { password, salt, keylen, N, r, p, maxmem };
120}
121
122module.exports = { scrypt, scryptSync };
123