mirror of
https://github.com/nodejs/node.git
synced 2025-05-02 03:31:35 +00:00

PR-URL: https://github.com/nodejs/node/pull/37433 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
212 lines
5.5 KiB
JavaScript
212 lines
5.5 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayFrom,
|
|
Promise,
|
|
SafeSet,
|
|
} = primordials;
|
|
|
|
const {
|
|
HmacJob,
|
|
KeyObjectHandle,
|
|
kCryptoJobAsync,
|
|
kSignJobModeSign,
|
|
kSignJobModeVerify,
|
|
} = internalBinding('crypto');
|
|
|
|
const {
|
|
getHashLength,
|
|
hasAnyNotIn,
|
|
jobPromise,
|
|
lazyDOMException,
|
|
normalizeHashName,
|
|
validateBitLength,
|
|
validateKeyOps,
|
|
kHandle,
|
|
kKeyObject,
|
|
} = require('internal/crypto/util');
|
|
|
|
const {
|
|
codes: {
|
|
ERR_MISSING_OPTION,
|
|
ERR_INVALID_ARG_TYPE,
|
|
}
|
|
} = require('internal/errors');
|
|
|
|
const {
|
|
generateKey,
|
|
} = require('internal/crypto/keygen');
|
|
|
|
const {
|
|
InternalCryptoKey,
|
|
SecretKeyObject,
|
|
createSecretKey,
|
|
isKeyObject,
|
|
} = require('internal/crypto/keys');
|
|
|
|
async function hmacGenerateKey(algorithm, extractable, keyUsages) {
|
|
const { hash, name } = algorithm;
|
|
let { length } = algorithm;
|
|
if (hash === undefined)
|
|
throw new ERR_MISSING_OPTION('algorithm.hash');
|
|
|
|
if (length === undefined)
|
|
length = getHashLength(hash.name);
|
|
|
|
validateBitLength(length, 'algorithm.length', true);
|
|
|
|
const usageSet = new SafeSet(keyUsages);
|
|
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
|
|
throw lazyDOMException(
|
|
'Unsupported key usage for an HMAC key',
|
|
'SyntaxError');
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
generateKey('hmac', { length }, (err, key) => {
|
|
if (err) {
|
|
return reject(lazyDOMException(
|
|
'The operation failed for an operation-specific reason',
|
|
'OperationError'));
|
|
}
|
|
|
|
resolve(new InternalCryptoKey(
|
|
key,
|
|
{ name, length, hash: { name: hash.name } },
|
|
ArrayFrom(usageSet),
|
|
extractable));
|
|
});
|
|
});
|
|
}
|
|
|
|
async function hmacImportKey(
|
|
format,
|
|
keyData,
|
|
algorithm,
|
|
extractable,
|
|
keyUsages) {
|
|
const { hash } = algorithm;
|
|
if (hash === undefined)
|
|
throw new ERR_MISSING_OPTION('algorithm.hash');
|
|
|
|
const usagesSet = new SafeSet(keyUsages);
|
|
if (hasAnyNotIn(usagesSet, ['sign', 'verify'])) {
|
|
throw lazyDOMException(
|
|
'Unsupported key usage for an HMAC key',
|
|
'SyntaxError');
|
|
}
|
|
let keyObject;
|
|
switch (format) {
|
|
case 'node.keyObject': {
|
|
if (!isKeyObject(keyData))
|
|
throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData);
|
|
|
|
if (keyData.type !== 'secret') {
|
|
throw lazyDOMException(
|
|
`Unable to import HMAC key with format ${format}`);
|
|
}
|
|
|
|
keyObject = keyData;
|
|
break;
|
|
}
|
|
case 'raw': {
|
|
const checkLength = keyData.byteLength * 8;
|
|
|
|
if (checkLength === 0 || algorithm.length === 0)
|
|
throw lazyDOMException('Zero-length key is not supported', 'DataError');
|
|
|
|
// The Web Crypto spec allows for key lengths that are not multiples of
|
|
// 8. We don't. Our check here is stricter than that defined by the spec
|
|
// in that we require that algorithm.length match keyData.length * 8 if
|
|
// algorithm.length is specified.
|
|
if (algorithm.length !== undefined &&
|
|
algorithm.length !== checkLength) {
|
|
throw lazyDOMException('Invalid key length', 'DataError');
|
|
}
|
|
|
|
keyObject = createSecretKey(keyData);
|
|
break;
|
|
}
|
|
case 'jwk': {
|
|
if (keyData == null || typeof keyData !== 'object')
|
|
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
|
|
|
if (keyData.kty !== 'oct')
|
|
throw lazyDOMException('Invalid key type', 'DataError');
|
|
|
|
if (usagesSet.size > 0 &&
|
|
keyData.use !== undefined &&
|
|
keyData.use !== 'sig') {
|
|
throw lazyDOMException('Invalid use type', 'DataError');
|
|
}
|
|
|
|
validateKeyOps(keyData.key_ops, usagesSet);
|
|
|
|
if (keyData.ext !== undefined &&
|
|
keyData.ext === false &&
|
|
extractable === true) {
|
|
throw lazyDOMException('JWK is not extractable', 'DataError');
|
|
}
|
|
|
|
if (keyData.alg !== undefined) {
|
|
if (typeof keyData.alg !== 'string')
|
|
throw lazyDOMException('Invalid alg', 'DataError');
|
|
switch (keyData.alg) {
|
|
case 'HS1':
|
|
if (algorithm.hash.name !== 'SHA-1')
|
|
throw lazyDOMException('Digest algorithm mismatch', 'DataError');
|
|
break;
|
|
case 'HS256':
|
|
if (algorithm.hash.name !== 'SHA-256')
|
|
throw lazyDOMException('Digest algorithm mismatch', 'DataError');
|
|
break;
|
|
case 'HS384':
|
|
if (algorithm.hash.name !== 'SHA-384')
|
|
throw lazyDOMException('Digest algorithm mismatch', 'DataError');
|
|
break;
|
|
case 'HS512':
|
|
if (algorithm.hash.name !== 'SHA-512')
|
|
throw lazyDOMException('Digest algorithm mismatch', 'DataError');
|
|
break;
|
|
default:
|
|
throw lazyDOMException('Unsupported digest algorithm', 'DataError');
|
|
}
|
|
}
|
|
|
|
const handle = new KeyObjectHandle();
|
|
handle.initJwk(keyData);
|
|
keyObject = new SecretKeyObject(handle);
|
|
break;
|
|
}
|
|
default:
|
|
throw lazyDOMException(`Unable to import HMAC key with format ${format}`);
|
|
}
|
|
|
|
const { length } = keyObject[kHandle].keyDetail({});
|
|
|
|
return new InternalCryptoKey(
|
|
keyObject, {
|
|
name: 'HMAC',
|
|
hash: algorithm.hash,
|
|
length,
|
|
},
|
|
keyUsages,
|
|
extractable);
|
|
}
|
|
|
|
function hmacSignVerify(key, data, algorithm, signature) {
|
|
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
|
|
return jobPromise(new HmacJob(
|
|
kCryptoJobAsync,
|
|
mode,
|
|
normalizeHashName(key.algorithm.hash.name),
|
|
key[kKeyObject][kHandle],
|
|
data,
|
|
signature));
|
|
}
|
|
|
|
module.exports = {
|
|
hmacImportKey,
|
|
hmacGenerateKey,
|
|
hmacSignVerify,
|
|
};
|