mirror of
https://github.com/nodejs/node.git
synced 2025-05-11 16:21:27 +00:00

WebCryptoAPI functions' arguments are now coersed and validated as per their WebIDL definitions like in other Web Crypto API implementations. This further improves interoperability with other implementations of Web Crypto API. PR-URL: https://github.com/nodejs/node/pull/46067 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
138 lines
3.2 KiB
JavaScript
138 lines
3.2 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
FunctionPrototypeCall,
|
|
} = primordials;
|
|
|
|
const { Buffer } = require('buffer');
|
|
|
|
const {
|
|
PBKDF2Job,
|
|
kCryptoJobAsync,
|
|
kCryptoJobSync,
|
|
} = internalBinding('crypto');
|
|
|
|
const {
|
|
validateFunction,
|
|
validateInt32,
|
|
validateString,
|
|
} = require('internal/validators');
|
|
|
|
const {
|
|
getArrayBufferOrView,
|
|
getDefaultEncoding,
|
|
normalizeHashName,
|
|
kKeyObject,
|
|
} = require('internal/crypto/util');
|
|
|
|
const {
|
|
lazyDOMException,
|
|
promisify,
|
|
} = require('internal/util');
|
|
|
|
function pbkdf2(password, salt, iterations, keylen, digest, callback) {
|
|
if (typeof digest === 'function') {
|
|
callback = digest;
|
|
digest = undefined;
|
|
}
|
|
|
|
({ password, salt, iterations, keylen, digest } =
|
|
check(password, salt, iterations, keylen, digest));
|
|
|
|
validateFunction(callback, 'callback');
|
|
|
|
const job = new PBKDF2Job(
|
|
kCryptoJobAsync,
|
|
password,
|
|
salt,
|
|
iterations,
|
|
keylen,
|
|
digest);
|
|
|
|
const encoding = getDefaultEncoding();
|
|
job.ondone = (err, result) => {
|
|
if (err !== undefined)
|
|
return FunctionPrototypeCall(callback, job, err);
|
|
const buf = Buffer.from(result);
|
|
if (encoding === 'buffer')
|
|
return FunctionPrototypeCall(callback, job, null, buf);
|
|
FunctionPrototypeCall(callback, job, null, buf.toString(encoding));
|
|
};
|
|
|
|
job.run();
|
|
}
|
|
|
|
function pbkdf2Sync(password, salt, iterations, keylen, digest) {
|
|
({ password, salt, iterations, keylen, digest } =
|
|
check(password, salt, iterations, keylen, digest));
|
|
|
|
const job = new PBKDF2Job(
|
|
kCryptoJobSync,
|
|
password,
|
|
salt,
|
|
iterations,
|
|
keylen,
|
|
digest);
|
|
|
|
const { 0: err, 1: result } = job.run();
|
|
if (err !== undefined)
|
|
throw err;
|
|
|
|
const buf = Buffer.from(result);
|
|
const encoding = getDefaultEncoding();
|
|
return encoding === 'buffer' ? buf : buf.toString(encoding);
|
|
}
|
|
|
|
function check(password, salt, iterations, keylen, digest) {
|
|
validateString(digest, 'digest');
|
|
|
|
password = getArrayBufferOrView(password, 'password');
|
|
salt = getArrayBufferOrView(salt, 'salt');
|
|
// OpenSSL uses a signed int to represent these values, so we are restricted
|
|
// to the 31-bit range here (which is plenty).
|
|
validateInt32(iterations, 'iterations', 1);
|
|
validateInt32(keylen, 'keylen', 0);
|
|
|
|
return { password, salt, iterations, keylen, digest };
|
|
}
|
|
|
|
const pbkdf2Promise = promisify(pbkdf2);
|
|
async function pbkdf2DeriveBits(algorithm, baseKey, length) {
|
|
const { iterations, hash, salt } = algorithm;
|
|
if (iterations === 0)
|
|
throw lazyDOMException(
|
|
'iterations cannot be zero',
|
|
'OperationError');
|
|
|
|
const raw = baseKey[kKeyObject].export();
|
|
|
|
if (length === 0)
|
|
throw lazyDOMException('length cannot be zero', 'OperationError');
|
|
if (length === null)
|
|
throw lazyDOMException('length cannot be null', 'OperationError');
|
|
if (length % 8) {
|
|
throw lazyDOMException(
|
|
'length must be a multiple of 8',
|
|
'OperationError');
|
|
}
|
|
|
|
let result;
|
|
try {
|
|
result = await pbkdf2Promise(
|
|
raw, salt, iterations, length / 8, normalizeHashName(hash.name),
|
|
);
|
|
} catch (err) {
|
|
throw lazyDOMException(
|
|
'The operation failed for an operation-specific reason',
|
|
{ name: 'OperationError', cause: err });
|
|
}
|
|
|
|
return result.buffer;
|
|
}
|
|
|
|
module.exports = {
|
|
pbkdf2,
|
|
pbkdf2Sync,
|
|
pbkdf2DeriveBits,
|
|
};
|