mirror of
https://github.com/nodejs/node.git
synced 2025-05-03 02:06:12 +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>
184 lines
4.5 KiB
JavaScript
184 lines
4.5 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayFrom,
|
|
SafeSet,
|
|
} = primordials;
|
|
|
|
const {
|
|
HmacJob,
|
|
KeyObjectHandle,
|
|
kCryptoJobAsync,
|
|
kSignJobModeSign,
|
|
kSignJobModeVerify,
|
|
} = internalBinding('crypto');
|
|
|
|
const {
|
|
getBlockSize,
|
|
hasAnyNotIn,
|
|
jobPromise,
|
|
normalizeHashName,
|
|
validateBitLength,
|
|
validateKeyOps,
|
|
kHandle,
|
|
kKeyObject,
|
|
} = require('internal/crypto/util');
|
|
|
|
const {
|
|
lazyDOMException,
|
|
promisify,
|
|
} = require('internal/util');
|
|
|
|
const {
|
|
generateKey: _generateKey,
|
|
} = require('internal/crypto/keygen');
|
|
|
|
const {
|
|
InternalCryptoKey,
|
|
SecretKeyObject,
|
|
createSecretKey,
|
|
} = require('internal/crypto/keys');
|
|
|
|
const generateKey = promisify(_generateKey);
|
|
|
|
async function hmacGenerateKey(algorithm, extractable, keyUsages) {
|
|
const { hash, name } = algorithm;
|
|
let { length } = algorithm;
|
|
|
|
if (length === undefined)
|
|
length = getBlockSize(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');
|
|
}
|
|
|
|
const key = await generateKey('hmac', { length }).catch((err) => {
|
|
throw lazyDOMException(
|
|
'The operation failed for an operation-specific reason',
|
|
{ name: 'OperationError', cause: err });
|
|
});
|
|
|
|
return new InternalCryptoKey(
|
|
key,
|
|
{ name, length, hash: { name: hash.name } },
|
|
ArrayFrom(usageSet),
|
|
extractable);
|
|
}
|
|
|
|
function getAlgorithmName(hash) {
|
|
switch (hash) {
|
|
case 'SHA-1': // Fall through
|
|
case 'SHA-256': // Fall through
|
|
case 'SHA-384': // Fall through
|
|
case 'SHA-512': // Fall through
|
|
return `HS${hash.slice(4)}`;
|
|
default:
|
|
throw lazyDOMException('Unsupported digest algorithm', 'DataError');
|
|
}
|
|
}
|
|
|
|
async function hmacImportKey(
|
|
format,
|
|
keyData,
|
|
algorithm,
|
|
extractable,
|
|
keyUsages) {
|
|
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 '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.kty)
|
|
throw lazyDOMException('Invalid keyData', 'DataError');
|
|
|
|
if (keyData.kty !== 'oct')
|
|
throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
|
|
|
|
if (usagesSet.size > 0 &&
|
|
keyData.use !== undefined &&
|
|
keyData.use !== 'sig') {
|
|
throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
|
|
}
|
|
|
|
validateKeyOps(keyData.key_ops, usagesSet);
|
|
|
|
if (keyData.ext !== undefined &&
|
|
keyData.ext === false &&
|
|
extractable === true) {
|
|
throw lazyDOMException(
|
|
'JWK "ext" Parameter and extractable mismatch',
|
|
'DataError');
|
|
}
|
|
|
|
if (keyData.alg !== undefined) {
|
|
if (keyData.alg !== getAlgorithmName(algorithm.hash.name))
|
|
throw lazyDOMException(
|
|
'JWK "alg" does not match the requested 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,
|
|
};
|