node/lib/internal/crypto/hkdf.js
ZiJian Liu b00bb01db9 lib: refactor to use validateCallback
PR-URL: https://github.com/nodejs/node/pull/36609
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
2020-12-29 15:48:57 +00:00

183 lines
3.5 KiB
JavaScript

'use strict';
const {
FunctionPrototypeCall,
Promise,
Uint8Array,
} = primordials;
const {
HKDFJob,
kCryptoJobAsync,
kCryptoJobSync,
} = internalBinding('crypto');
const {
validateCallback,
validateInteger,
validateString,
validateUint32,
} = require('internal/validators');
const { kMaxLength } = require('buffer');
const {
getArrayBufferOrView,
lazyDOMException,
normalizeHashName,
toBuf,
validateByteSource,
kKeyObject,
} = require('internal/crypto/util');
const {
createSecretKey,
isKeyObject,
} = require('internal/crypto/keys');
const {
isAnyArrayBuffer,
isArrayBufferView,
} = require('internal/util/types');
const {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_OUT_OF_RANGE,
ERR_MISSING_OPTION,
},
hideStackFrames,
} = require('internal/errors');
const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
key = prepareKey(key);
salt = toBuf(salt);
info = toBuf(info);
validateString(hash, 'digest');
validateByteSource(salt, 'salt');
validateByteSource(info, 'info');
validateInteger(length, 'length', 0, kMaxLength);
if (info.byteLength > 1024) {
throw ERR_OUT_OF_RANGE(
'info',
'must not contain more than 1024 bytes',
info.byteLength);
}
return {
hash,
key,
salt,
info,
length,
};
});
function prepareKey(key) {
if (isKeyObject(key))
return key;
// TODO(@jasnell): createSecretKey should allow using an ArrayBuffer
if (isAnyArrayBuffer(key))
return createSecretKey(new Uint8Array(key));
key = toBuf(key);
if (!isArrayBufferView(key)) {
throw new ERR_INVALID_ARG_TYPE(
'key',
[
'string',
'SecretKeyObject',
'ArrayBuffer',
'TypedArray',
'DataView',
'Buffer'
],
key);
}
return createSecretKey(key);
}
function hkdf(hash, key, salt, info, length, callback) {
({
hash,
key,
salt,
info,
length,
} = validateParameters(hash, key, salt, info, length));
validateCallback(callback);
const job = new HKDFJob(kCryptoJobAsync, hash, key, salt, info, length);
job.ondone = (error, bits) => {
if (error) return FunctionPrototypeCall(callback, job, error);
FunctionPrototypeCall(callback, job, null, bits);
};
job.run();
}
function hkdfSync(hash, key, salt, info, length) {
({
hash,
key,
salt,
info,
length,
} = validateParameters(hash, key, salt, info, length));
const job = new HKDFJob(kCryptoJobSync, hash, key, salt, info, length);
const [err, bits] = job.run();
if (err !== undefined)
throw err;
return bits;
}
async function hkdfDeriveBits(algorithm, baseKey, length) {
validateUint32(length, 'length');
const { hash } = algorithm;
const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt');
const info = getArrayBufferOrView(algorithm.info, 'algorithm.info');
if (hash === undefined)
throw new ERR_MISSING_OPTION('algorithm.hash');
let byteLength = 512 / 8;
if (length !== undefined) {
if (length === 0)
throw lazyDOMException('length cannot be zero', 'OperationError');
if (length % 8) {
throw lazyDOMException(
'length must be a multiple of 8',
'OperationError');
}
byteLength = length / 8;
}
return new Promise((resolve, reject) => {
hkdf(
normalizeHashName(hash.name),
baseKey[kKeyObject],
salt,
info,
byteLength,
(err, bits) => {
if (err) return reject(err);
resolve(bits);
});
});
}
module.exports = {
hkdf,
hkdfSync,
hkdfDeriveBits,
};