mirror of
https://github.com/nodejs/node.git
synced 2025-04-28 13:40:37 +00:00
src: refactor SubtleCrypto algorithm and length validations
PR-URL: https://github.com/nodejs/node/pull/57273 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jason Zhang <xzha4350@gmail.com> Reviewed-By: Mattias Buelens <mattias@buelens.com>
This commit is contained in:
parent
52ac44888d
commit
6fdd4e6dcf
@ -58,7 +58,6 @@ const {
|
||||
generateKey: _generateKey,
|
||||
} = require('internal/crypto/keygen');
|
||||
|
||||
const kMaxCounterLength = 128;
|
||||
const kTagLengths = [32, 64, 96, 104, 112, 120, 128];
|
||||
const generateKey = promisify(_generateKey);
|
||||
|
||||
@ -109,15 +108,19 @@ function getVariant(name, length) {
|
||||
}
|
||||
}
|
||||
|
||||
function asyncAesCtrCipher(mode, key, data, { counter, length }) {
|
||||
validateByteLength(counter, 'algorithm.counter', 16);
|
||||
function validateAesCtrAlgorithm(algorithm) {
|
||||
validateByteLength(algorithm.counter, 'algorithm.counter', 16);
|
||||
// The length must specify an integer between 1 and 128. While
|
||||
// there is no default, this should typically be 64.
|
||||
if (length === 0 || length > kMaxCounterLength) {
|
||||
if (algorithm.length === 0 || algorithm.length > 128) {
|
||||
throw lazyDOMException(
|
||||
'AES-CTR algorithm.length must be between 1 and 128',
|
||||
'OperationError');
|
||||
}
|
||||
}
|
||||
|
||||
function asyncAesCtrCipher(mode, key, data, algorithm) {
|
||||
validateAesCtrAlgorithm(algorithm);
|
||||
|
||||
return jobPromise(() => new AESCipherJob(
|
||||
kCryptoJobAsync,
|
||||
@ -125,19 +128,23 @@ function asyncAesCtrCipher(mode, key, data, { counter, length }) {
|
||||
key[kKeyObject][kHandle],
|
||||
data,
|
||||
getVariant('AES-CTR', key.algorithm.length),
|
||||
counter,
|
||||
length));
|
||||
algorithm.counter,
|
||||
algorithm.length));
|
||||
}
|
||||
|
||||
function asyncAesCbcCipher(mode, key, data, { iv }) {
|
||||
validateByteLength(iv, 'algorithm.iv', 16);
|
||||
function validateAesCbcAlgorithm(algorithm) {
|
||||
validateByteLength(algorithm.iv, 'algorithm.iv', 16);
|
||||
}
|
||||
|
||||
function asyncAesCbcCipher(mode, key, data, algorithm) {
|
||||
validateAesCbcAlgorithm(algorithm);
|
||||
return jobPromise(() => new AESCipherJob(
|
||||
kCryptoJobAsync,
|
||||
mode,
|
||||
key[kKeyObject][kHandle],
|
||||
data,
|
||||
getVariant('AES-CBC', key.algorithm.length),
|
||||
iv));
|
||||
algorithm.iv));
|
||||
}
|
||||
|
||||
function asyncAesKwCipher(mode, key, data) {
|
||||
@ -149,24 +156,25 @@ function asyncAesKwCipher(mode, key, data) {
|
||||
getVariant('AES-KW', key.algorithm.length)));
|
||||
}
|
||||
|
||||
function asyncAesGcmCipher(
|
||||
mode,
|
||||
key,
|
||||
data,
|
||||
{ iv, additionalData, tagLength = 128 }) {
|
||||
if (!ArrayPrototypeIncludes(kTagLengths, tagLength)) {
|
||||
return PromiseReject(lazyDOMException(
|
||||
`${tagLength} is not a valid AES-GCM tag length`,
|
||||
'OperationError'));
|
||||
function validateAesGcmAlgorithm(algorithm) {
|
||||
if (!ArrayPrototypeIncludes(kTagLengths, algorithm.tagLength)) {
|
||||
throw lazyDOMException(
|
||||
`${algorithm.tagLength} is not a valid AES-GCM tag length`,
|
||||
'OperationError');
|
||||
}
|
||||
|
||||
validateMaxBufferLength(iv, 'algorithm.iv');
|
||||
validateMaxBufferLength(algorithm.iv, 'algorithm.iv');
|
||||
|
||||
if (additionalData !== undefined) {
|
||||
validateMaxBufferLength(additionalData, 'algorithm.additionalData');
|
||||
if (algorithm.additionalData !== undefined) {
|
||||
validateMaxBufferLength(algorithm.additionalData, 'algorithm.additionalData');
|
||||
}
|
||||
}
|
||||
|
||||
const tagByteLength = MathFloor(tagLength / 8);
|
||||
function asyncAesGcmCipher(mode, key, data, algorithm) {
|
||||
algorithm.tagLength ??= 128;
|
||||
validateAesGcmAlgorithm(algorithm);
|
||||
|
||||
const tagByteLength = MathFloor(algorithm.tagLength / 8);
|
||||
let tag;
|
||||
switch (mode) {
|
||||
case kWebCryptoCipherDecrypt: {
|
||||
@ -198,9 +206,9 @@ function asyncAesGcmCipher(
|
||||
key[kKeyObject][kHandle],
|
||||
data,
|
||||
getVariant('AES-GCM', key.algorithm.length),
|
||||
iv,
|
||||
algorithm.iv,
|
||||
tag,
|
||||
additionalData));
|
||||
algorithm.additionalData));
|
||||
}
|
||||
|
||||
function aesCipher(mode, key, data, algorithm) {
|
||||
@ -212,13 +220,17 @@ function aesCipher(mode, key, data, algorithm) {
|
||||
}
|
||||
}
|
||||
|
||||
async function aesGenerateKey(algorithm, extractable, keyUsages) {
|
||||
const { name, length } = algorithm;
|
||||
if (!ArrayPrototypeIncludes(kAesKeyLengths, length)) {
|
||||
function validateAesGenerateKeyAlgorithm(algorithm) {
|
||||
if (!ArrayPrototypeIncludes(kAesKeyLengths, algorithm.length)) {
|
||||
throw lazyDOMException(
|
||||
'AES key length must be 128, 192, or 256 bits',
|
||||
'OperationError');
|
||||
}
|
||||
}
|
||||
|
||||
async function aesGenerateKey(algorithm, extractable, keyUsages) {
|
||||
validateAesGenerateKeyAlgorithm(algorithm);
|
||||
const { name, length } = algorithm;
|
||||
|
||||
const checkUsages = ['wrapKey', 'unwrapKey'];
|
||||
if (name !== 'AES-KW')
|
||||
|
@ -329,18 +329,21 @@ function cfrgImportKey(
|
||||
extractable);
|
||||
}
|
||||
|
||||
function eddsaSignVerify(key, data, { name, context }, signature) {
|
||||
function validateEdDSASignVerifyAlgorithm(algorithm) {
|
||||
if (algorithm.name === 'Ed448' && algorithm.context?.byteLength) {
|
||||
throw lazyDOMException(
|
||||
'Non zero-length context is not yet supported.', 'NotSupportedError');
|
||||
}
|
||||
}
|
||||
|
||||
function eddsaSignVerify(key, data, algorithm, signature) {
|
||||
validateEdDSASignVerifyAlgorithm(algorithm);
|
||||
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
|
||||
const type = mode === kSignJobModeSign ? 'private' : 'public';
|
||||
|
||||
if (key.type !== type)
|
||||
throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
|
||||
|
||||
if (name === 'Ed448' && context?.byteLength) {
|
||||
throw lazyDOMException(
|
||||
'Non zero-length context is not yet supported.', 'NotSupportedError');
|
||||
}
|
||||
|
||||
return jobPromise(() => new SignJob(
|
||||
kCryptoJobAsync,
|
||||
mode,
|
||||
|
@ -298,28 +298,28 @@ function diffieHellman(options) {
|
||||
|
||||
let masks;
|
||||
|
||||
// The ecdhDeriveBits function is part of the Web Crypto API and serves both
|
||||
// deriveKeys and deriveBits functions.
|
||||
async function ecdhDeriveBits(algorithm, baseKey, length) {
|
||||
const { 'public': key } = algorithm;
|
||||
|
||||
if (key.type !== 'public') {
|
||||
function validateEcdhDeriveBitsAlgorithmAndLength(algorithm, length) {
|
||||
if (algorithm.public.type !== 'public') {
|
||||
throw lazyDOMException(
|
||||
'algorithm.public must be a public key', 'InvalidAccessError');
|
||||
}
|
||||
|
||||
if (algorithm.name !== algorithm.public.algorithm.name) {
|
||||
throw lazyDOMException(`algorithm.public must be an ${algorithm.name} key`, 'InvalidAccessError');
|
||||
}
|
||||
}
|
||||
|
||||
// The ecdhDeriveBits function is part of the Web Crypto API and serves both
|
||||
// deriveKeys and deriveBits functions.
|
||||
async function ecdhDeriveBits(algorithm, baseKey, length) {
|
||||
validateEcdhDeriveBitsAlgorithmAndLength(algorithm, length);
|
||||
const { 'public': key } = algorithm;
|
||||
|
||||
if (baseKey.type !== 'private') {
|
||||
throw lazyDOMException(
|
||||
'baseKey must be a private key', 'InvalidAccessError');
|
||||
}
|
||||
|
||||
if (
|
||||
key.algorithm.name !== 'ECDH' &&
|
||||
key.algorithm.name !== 'X25519' &&
|
||||
key.algorithm.name !== 'X448'
|
||||
) {
|
||||
throw lazyDOMException('Keys must be ECDH, X25519, or X448 keys', 'InvalidAccessError');
|
||||
}
|
||||
|
||||
if (key.algorithm.name !== baseKey.algorithm.name) {
|
||||
throw lazyDOMException(
|
||||
'The public and private keys must be of the same type',
|
||||
|
@ -1,8 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayPrototypeIncludes,
|
||||
ObjectKeys,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
SafeSet,
|
||||
} = primordials;
|
||||
|
||||
@ -77,14 +76,17 @@ function createECPublicKeyRaw(namedCurve, keyData) {
|
||||
return new PublicKeyObject(handle);
|
||||
}
|
||||
|
||||
async function ecGenerateKey(algorithm, extractable, keyUsages) {
|
||||
const { name, namedCurve } = algorithm;
|
||||
|
||||
if (!ArrayPrototypeIncludes(ObjectKeys(kNamedCurveAliases), namedCurve)) {
|
||||
function validateEcKeyAlgorithm(algorithm) {
|
||||
if (!ObjectPrototypeHasOwnProperty(kNamedCurveAliases, algorithm.namedCurve)) {
|
||||
throw lazyDOMException(
|
||||
'Unrecognized namedCurve',
|
||||
'NotSupportedError');
|
||||
}
|
||||
}
|
||||
|
||||
async function ecGenerateKey(algorithm, extractable, keyUsages) {
|
||||
validateEcKeyAlgorithm(algorithm);
|
||||
const { name, namedCurve } = algorithm;
|
||||
|
||||
const usageSet = new SafeSet(keyUsages);
|
||||
switch (name) {
|
||||
@ -154,16 +156,11 @@ function ecImportKey(
|
||||
keyData,
|
||||
algorithm,
|
||||
extractable,
|
||||
keyUsages) {
|
||||
|
||||
keyUsages,
|
||||
) {
|
||||
validateEcKeyAlgorithm(algorithm);
|
||||
const { name, namedCurve } = algorithm;
|
||||
|
||||
if (!ArrayPrototypeIncludes(ObjectKeys(kNamedCurveAliases), namedCurve)) {
|
||||
throw lazyDOMException(
|
||||
'Unrecognized namedCurve',
|
||||
'NotSupportedError');
|
||||
}
|
||||
|
||||
let keyObject;
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
switch (format) {
|
||||
|
@ -138,11 +138,7 @@ function hkdfSync(hash, key, salt, info, length) {
|
||||
}
|
||||
|
||||
const hkdfPromise = promisify(hkdf);
|
||||
async function hkdfDeriveBits(algorithm, baseKey, length) {
|
||||
const { hash, salt, info } = algorithm;
|
||||
|
||||
if (length === 0)
|
||||
return new ArrayBuffer(0);
|
||||
function validateHkdfDeriveBitsAlgorithmAndLength(algorithm, length) {
|
||||
if (length === null)
|
||||
throw lazyDOMException('length cannot be null', 'OperationError');
|
||||
if (length % 8) {
|
||||
@ -150,6 +146,14 @@ async function hkdfDeriveBits(algorithm, baseKey, length) {
|
||||
'length must be a multiple of 8',
|
||||
'OperationError');
|
||||
}
|
||||
}
|
||||
|
||||
async function hkdfDeriveBits(algorithm, baseKey, length) {
|
||||
validateHkdfDeriveBitsAlgorithmAndLength(algorithm, length);
|
||||
const { hash, salt, info } = algorithm;
|
||||
|
||||
if (length === 0)
|
||||
return new ArrayBuffer(0);
|
||||
|
||||
try {
|
||||
return await hkdfPromise(
|
||||
|
@ -895,12 +895,14 @@ function isCryptoKey(obj) {
|
||||
}
|
||||
|
||||
function importGenericSecretKey(
|
||||
{ name, length },
|
||||
algorithm,
|
||||
format,
|
||||
keyData,
|
||||
extractable,
|
||||
keyUsages) {
|
||||
keyUsages,
|
||||
) {
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
const { name } = algorithm;
|
||||
if (extractable)
|
||||
throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError');
|
||||
|
||||
@ -910,47 +912,22 @@ function importGenericSecretKey(
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
let keyObject;
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
|
||||
throw lazyDOMException(
|
||||
`Unsupported key usage for a ${name} key`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
const checkLength = keyData.symmetricKeySize * 8;
|
||||
|
||||
// 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 (length !== undefined && length !== checkLength) {
|
||||
throw lazyDOMException('Invalid key length', 'DataError');
|
||||
}
|
||||
return new InternalCryptoKey(keyData, { name }, keyUsages, false);
|
||||
keyObject = keyData;
|
||||
break;
|
||||
}
|
||||
case 'raw': {
|
||||
if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
|
||||
throw lazyDOMException(
|
||||
`Unsupported key usage for a ${name} key`,
|
||||
'SyntaxError');
|
||||
}
|
||||
|
||||
const checkLength = keyData.byteLength * 8;
|
||||
|
||||
// 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 (length !== undefined && length !== checkLength) {
|
||||
throw lazyDOMException('Invalid key length', 'DataError');
|
||||
}
|
||||
|
||||
const keyObject = createSecretKey(keyData);
|
||||
return new InternalCryptoKey(keyObject, { name }, keyUsages, false);
|
||||
keyObject = createSecretKey(keyData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyObject) {
|
||||
return new InternalCryptoKey(keyObject, { name }, keyUsages, false);
|
||||
}
|
||||
|
||||
throw lazyDOMException(
|
||||
`Unable to import ${name} key with format ${format}`,
|
||||
'NotSupportedError');
|
||||
|
@ -18,7 +18,6 @@ const {
|
||||
hasAnyNotIn,
|
||||
jobPromise,
|
||||
normalizeHashName,
|
||||
validateBitLength,
|
||||
validateKeyOps,
|
||||
kHandle,
|
||||
kKeyObject,
|
||||
@ -41,15 +40,30 @@ const {
|
||||
|
||||
const generateKey = promisify(_generateKey);
|
||||
|
||||
function validateHmacGenerateKeyAlgorithm(algorithm) {
|
||||
if (algorithm.length !== undefined) {
|
||||
if (algorithm.length === 0)
|
||||
throw lazyDOMException(
|
||||
'Zero-length key is not supported',
|
||||
'OperationError');
|
||||
|
||||
// The Web Crypto spec allows for key lengths that are not multiples of 8. We don't.
|
||||
if (algorithm.length % 8) {
|
||||
throw lazyDOMException(
|
||||
'Unsupported algorithm.length',
|
||||
'NotSupportedError');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function hmacGenerateKey(algorithm, extractable, keyUsages) {
|
||||
validateHmacGenerateKeyAlgorithm(algorithm);
|
||||
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(
|
||||
@ -82,12 +96,27 @@ function getAlgorithmName(hash) {
|
||||
}
|
||||
}
|
||||
|
||||
function validateHmacImportKeyAlgorithm(algorithm) {
|
||||
if (algorithm.length !== undefined) {
|
||||
if (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.
|
||||
if (algorithm.length % 8) {
|
||||
throw lazyDOMException('Unsupported algorithm.length', 'NotSupportedError');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hmacImportKey(
|
||||
format,
|
||||
keyData,
|
||||
algorithm,
|
||||
extractable,
|
||||
keyUsages) {
|
||||
keyUsages,
|
||||
) {
|
||||
validateHmacImportKeyAlgorithm(algorithm);
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
if (hasAnyNotIn(usagesSet, ['sign', 'verify'])) {
|
||||
throw lazyDOMException(
|
||||
@ -97,38 +126,10 @@ function hmacImportKey(
|
||||
let keyObject;
|
||||
switch (format) {
|
||||
case 'KeyObject': {
|
||||
const checkLength = keyData.symmetricKeySize * 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 = 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;
|
||||
}
|
||||
@ -178,6 +179,14 @@ function hmacImportKey(
|
||||
|
||||
const { length } = keyObject[kHandle].keyDetail({});
|
||||
|
||||
if (length === 0)
|
||||
throw lazyDOMException('Zero-length key is not supported', 'DataError');
|
||||
|
||||
if (algorithm.length !== undefined &&
|
||||
algorithm.length !== length) {
|
||||
throw lazyDOMException('Invalid key length', 'DataError');
|
||||
}
|
||||
|
||||
return new InternalCryptoKey(
|
||||
keyObject, {
|
||||
name: 'HMAC',
|
||||
|
@ -92,22 +92,28 @@ function check(password, salt, iterations, keylen, digest) {
|
||||
}
|
||||
|
||||
const pbkdf2Promise = promisify(pbkdf2);
|
||||
async function pbkdf2DeriveBits(algorithm, baseKey, length) {
|
||||
const { iterations, hash, salt } = algorithm;
|
||||
if (iterations === 0)
|
||||
function validatePbkdf2DeriveBitsAlgorithmAndLength(algorithm, length) {
|
||||
if (algorithm.iterations === 0)
|
||||
throw lazyDOMException(
|
||||
'iterations cannot be zero',
|
||||
'OperationError');
|
||||
|
||||
if (length === 0)
|
||||
return new ArrayBuffer(0);
|
||||
if (length === null)
|
||||
throw lazyDOMException('length cannot be null', 'OperationError');
|
||||
|
||||
if (length % 8) {
|
||||
throw lazyDOMException(
|
||||
'length must be a multiple of 8',
|
||||
'OperationError');
|
||||
}
|
||||
}
|
||||
|
||||
async function pbkdf2DeriveBits(algorithm, baseKey, length) {
|
||||
validatePbkdf2DeriveBitsAlgorithmAndLength(algorithm, length);
|
||||
const { iterations, hash, salt } = algorithm;
|
||||
|
||||
if (length === 0)
|
||||
return new ArrayBuffer(0);
|
||||
|
||||
let result;
|
||||
try {
|
||||
|
@ -85,16 +85,21 @@ function verifyAcceptableRsaKeyUse(name, isPublic, usages) {
|
||||
}
|
||||
}
|
||||
|
||||
function rsaOaepCipher(mode, key, data, { label }) {
|
||||
function validateRsaOaepAlgorithm(algorithm) {
|
||||
if (algorithm.label !== undefined) {
|
||||
validateMaxBufferLength(algorithm.label, 'algorithm.label');
|
||||
}
|
||||
}
|
||||
|
||||
function rsaOaepCipher(mode, key, data, algorithm) {
|
||||
validateRsaOaepAlgorithm(algorithm);
|
||||
|
||||
const type = mode === kWebCryptoCipherEncrypt ? 'public' : 'private';
|
||||
if (key.type !== type) {
|
||||
throw lazyDOMException(
|
||||
'The requested operation is not valid for the provided key',
|
||||
'InvalidAccessError');
|
||||
}
|
||||
if (label !== undefined) {
|
||||
validateMaxBufferLength(label, 'algorithm.label');
|
||||
}
|
||||
|
||||
return jobPromise(() => new RSACipherJob(
|
||||
kCryptoJobAsync,
|
||||
@ -103,14 +108,26 @@ function rsaOaepCipher(mode, key, data, { label }) {
|
||||
data,
|
||||
kKeyVariantRSA_OAEP,
|
||||
normalizeHashName(key.algorithm.hash.name),
|
||||
label));
|
||||
algorithm.label));
|
||||
}
|
||||
|
||||
function validateRsaKeyGenerateAlgorithm(algorithm) {
|
||||
const publicExponentConverted = bigIntArrayToUnsignedInt(algorithm.publicExponent);
|
||||
if (publicExponentConverted === undefined) {
|
||||
throw lazyDOMException(
|
||||
'The publicExponent must be equivalent to an unsigned 32-bit value',
|
||||
'OperationError');
|
||||
}
|
||||
|
||||
return publicExponentConverted;
|
||||
}
|
||||
|
||||
async function rsaKeyGenerate(
|
||||
algorithm,
|
||||
extractable,
|
||||
keyUsages) {
|
||||
|
||||
keyUsages,
|
||||
) {
|
||||
const publicExponentConverted = validateRsaKeyGenerateAlgorithm(algorithm);
|
||||
const {
|
||||
name,
|
||||
modulusLength,
|
||||
@ -120,13 +137,6 @@ async function rsaKeyGenerate(
|
||||
|
||||
const usageSet = new SafeSet(keyUsages);
|
||||
|
||||
const publicExponentConverted = bigIntArrayToUnsignedInt(publicExponent);
|
||||
if (publicExponentConverted === undefined) {
|
||||
throw lazyDOMException(
|
||||
'The publicExponent must be equivalent to an unsigned 32-bit value',
|
||||
'OperationError');
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case 'RSA-OAEP':
|
||||
if (hasAnyNotIn(usageSet,
|
||||
@ -319,7 +329,7 @@ function rsaImportKey(
|
||||
}, keyUsages, extractable);
|
||||
}
|
||||
|
||||
async function rsaSignVerify(key, data, { saltLength }, signature) {
|
||||
function rsaSignVerify(key, data, { saltLength }, signature) {
|
||||
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
|
||||
const type = mode === kSignJobModeSign ? 'private' : 'public';
|
||||
|
||||
|
@ -50,8 +50,6 @@ const {
|
||||
ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED,
|
||||
ERR_CRYPTO_ENGINE_UNKNOWN,
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
ERR_OUT_OF_RANGE,
|
||||
},
|
||||
hideStackFrames,
|
||||
} = require('internal/errors');
|
||||
@ -418,20 +416,6 @@ function hasAnyNotIn(set, checks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function validateBitLength(length, name, required = false) {
|
||||
if (length !== undefined || required) {
|
||||
validateNumber(length, name);
|
||||
if (length < 0)
|
||||
throw new ERR_OUT_OF_RANGE(name, '> 0');
|
||||
if (length % 8) {
|
||||
throw new ERR_INVALID_ARG_VALUE(
|
||||
name,
|
||||
length,
|
||||
'must be a multiple of 8');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateByteLength(buf, name, target) {
|
||||
if (buf.byteLength !== target) {
|
||||
throw lazyDOMException(
|
||||
@ -617,7 +601,6 @@ module.exports = {
|
||||
normalizeAlgorithm,
|
||||
normalizeHashName,
|
||||
hasAnyNotIn,
|
||||
validateBitLength,
|
||||
validateByteLength,
|
||||
validateByteSource,
|
||||
validateKeyOps,
|
||||
|
@ -182,7 +182,7 @@ async function prepareKeys() {
|
||||
},
|
||||
keys.X448.privateKey,
|
||||
8 * keys.X448.size),
|
||||
{ message: 'The public and private keys must be of the same type' });
|
||||
{ message: 'algorithm.public must be an X448 key' });
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -202,7 +202,7 @@ async function prepareKeys() {
|
||||
public: keys['P-384'].publicKey
|
||||
},
|
||||
keys['P-521'].privateKey,
|
||||
8 * keys['P-521'].size),
|
||||
8 * keys['P-384'].size),
|
||||
{ message: /Named curve mismatch/ });
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ async function prepareKeys() {
|
||||
name: 'ECDH',
|
||||
public: publicKey
|
||||
}, keys['P-521'].privateKey, null), {
|
||||
message: /Keys must be ECDH, X25519, or X448 keys/
|
||||
message: 'algorithm.public must be an ECDH key'
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ async function prepareKeys() {
|
||||
},
|
||||
keys.X448.privateKey,
|
||||
...otherArgs),
|
||||
{ message: 'The public and private keys must be of the same type' });
|
||||
{ message: 'algorithm.public must be an X448 key' });
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -174,7 +174,7 @@ async function prepareKeys() {
|
||||
},
|
||||
keys['P-521'].privateKey,
|
||||
...otherArgs),
|
||||
{ message: /Keys must be ECDH, X25519, or X448 keys/ });
|
||||
{ message: 'algorithm.public must be an ECDH key' });
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -37,6 +37,20 @@ const { subtle } = globalThis.crypto;
|
||||
assert.strictEqual(
|
||||
Buffer.from(plaintext).toString('hex'),
|
||||
Buffer.from(buf).toString('hex'));
|
||||
|
||||
await assert.rejects(() => subtle.encrypt({
|
||||
name: 'RSA-OAEP',
|
||||
}, privateKey, buf), {
|
||||
name: 'InvalidAccessError',
|
||||
message: 'The requested operation is not valid for the provided key'
|
||||
});
|
||||
|
||||
await assert.rejects(() => subtle.decrypt({
|
||||
name: 'RSA-OAEP',
|
||||
}, publicKey, ciphertext), {
|
||||
name: 'InvalidAccessError',
|
||||
message: 'The requested operation is not valid for the provided key'
|
||||
});
|
||||
}
|
||||
|
||||
test().then(common.mustCall());
|
||||
|
@ -36,6 +36,15 @@ const { subtle } = globalThis.crypto;
|
||||
}, false, ['sign', 'verify']), {
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.importKey('raw', keyData, {
|
||||
name: 'HMAC',
|
||||
hash: 'SHA-256',
|
||||
length: 384,
|
||||
}, false, ['sign', 'verify']), {
|
||||
name: 'DataError',
|
||||
message: 'Invalid key length'
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.importKey('raw', keyData, {
|
||||
name: 'HMAC',
|
||||
@ -59,8 +68,8 @@ const { subtle } = globalThis.crypto;
|
||||
hash: 'SHA-256',
|
||||
length: 1
|
||||
}, false, ['sign', 'verify']), {
|
||||
name: 'DataError',
|
||||
message: 'Invalid key length'
|
||||
name: 'NotSupportedError',
|
||||
message: 'Unsupported algorithm.length'
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.importKey('jwk', null, {
|
||||
|
@ -345,6 +345,14 @@ const vectors = {
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||
}));
|
||||
|
||||
await assert.rejects(
|
||||
subtle.generateKey(
|
||||
{ name, modulusLength, publicExponent: new Uint8Array([1, 1, 1, 1, 1]), hash }, true, usages),
|
||||
{
|
||||
message: /The publicExponent must be equivalent to an unsigned 32-bit value/,
|
||||
name: 'OperationError',
|
||||
});
|
||||
|
||||
await Promise.all([true, 1].map((hash) => {
|
||||
return assert.rejects(subtle.generateKey({
|
||||
name,
|
||||
@ -494,8 +502,6 @@ const vectors = {
|
||||
|
||||
const tests = kTests.map((args) => test(...args));
|
||||
|
||||
// Test bad parameters
|
||||
|
||||
Promise.all(tests).then(common.mustCall());
|
||||
}
|
||||
|
||||
@ -675,7 +681,5 @@ assert.throws(() => new CryptoKey(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
|
||||
|
||||
const tests = kTests.map((args) => test(...args));
|
||||
|
||||
// Test bad parameters
|
||||
|
||||
Promise.all(tests).then(common.mustCall());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user