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