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