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