mirror of
https://github.com/nodejs/node.git
synced 2025-04-30 23:56:58 +00:00
crypto: use WebIDL converters in WebCryptoAPI
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>
This commit is contained in:
parent
66b1356ebc
commit
3ef38c4bd7
@ -2,6 +2,10 @@
|
||||
|
||||
<!-- YAML
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/46067
|
||||
description: Arguments are now coersed and validated as per their WebIDL
|
||||
definitions like in other Web Crypto API implementations.
|
||||
- version: v19.0.0
|
||||
pr-url: https://github.com/nodejs/node/pull/44897
|
||||
description: No longer experimental except for the `Ed25519`, `Ed448`,
|
||||
|
@ -32,7 +32,6 @@ const {
|
||||
} = internalBinding('crypto');
|
||||
|
||||
const {
|
||||
getArrayBufferOrView,
|
||||
hasAnyNotIn,
|
||||
jobPromise,
|
||||
validateByteLength,
|
||||
@ -112,13 +111,10 @@ function getVariant(name, length) {
|
||||
}
|
||||
|
||||
function asyncAesCtrCipher(mode, key, data, { counter, length }) {
|
||||
counter = getArrayBufferOrView(counter, 'algorithm.counter');
|
||||
validateByteLength(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 (typeof length !== 'number' ||
|
||||
length <= 0 ||
|
||||
length > kMaxCounterLength) {
|
||||
if (length === 0 || length > kMaxCounterLength) {
|
||||
throw lazyDOMException(
|
||||
'AES-CTR algorithm.length must be between 1 and 128',
|
||||
'OperationError');
|
||||
@ -135,7 +131,6 @@ function asyncAesCtrCipher(mode, key, data, { counter, length }) {
|
||||
}
|
||||
|
||||
function asyncAesCbcCipher(mode, key, data, { iv }) {
|
||||
iv = getArrayBufferOrView(iv, 'algorithm.iv');
|
||||
validateByteLength(iv, 'algorithm.iv', 16);
|
||||
return jobPromise(() => new AESCipherJob(
|
||||
kCryptoJobAsync,
|
||||
@ -166,12 +161,9 @@ function asyncAesGcmCipher(
|
||||
'OperationError'));
|
||||
}
|
||||
|
||||
iv = getArrayBufferOrView(iv, 'algorithm.iv');
|
||||
validateMaxBufferLength(iv, 'algorithm.iv');
|
||||
|
||||
if (additionalData !== undefined) {
|
||||
additionalData =
|
||||
getArrayBufferOrView(additionalData, 'algorithm.additionalData');
|
||||
validateMaxBufferLength(additionalData, 'algorithm.additionalData');
|
||||
}
|
||||
|
||||
@ -281,16 +273,16 @@ async function aesImportKey(
|
||||
break;
|
||||
}
|
||||
case 'jwk': {
|
||||
if (keyData == null || typeof keyData !== 'object')
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
if (!keyData.kty)
|
||||
throw lazyDOMException('Invalid keyData', 'DataError');
|
||||
|
||||
if (keyData.kty !== 'oct')
|
||||
throw lazyDOMException('Invalid key type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
|
||||
|
||||
if (usagesSet.size > 0 &&
|
||||
keyData.use !== undefined &&
|
||||
keyData.use !== 'enc') {
|
||||
throw lazyDOMException('Invalid use type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
|
||||
}
|
||||
|
||||
validateKeyOps(keyData.key_ops, usagesSet);
|
||||
@ -298,7 +290,9 @@ async function aesImportKey(
|
||||
if (keyData.ext !== undefined &&
|
||||
keyData.ext === false &&
|
||||
extractable === true) {
|
||||
throw lazyDOMException('JWK is not extractable', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "ext" Parameter and extractable mismatch',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
const handle = new KeyObjectHandle();
|
||||
@ -308,10 +302,10 @@ async function aesImportKey(
|
||||
validateKeyLength(length);
|
||||
|
||||
if (keyData.alg !== undefined) {
|
||||
if (typeof keyData.alg !== 'string')
|
||||
throw lazyDOMException('Invalid alg', 'DataError');
|
||||
if (keyData.alg !== getAlgorithmName(algorithm.name, length))
|
||||
throw lazyDOMException('Algorithm mismatch', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "alg" does not match the requested algorithm',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
keyObject = new SecretKeyObject(handle);
|
||||
|
@ -18,7 +18,6 @@ const {
|
||||
} = internalBinding('crypto');
|
||||
|
||||
const {
|
||||
getArrayBufferOrView,
|
||||
getUsagesUnion,
|
||||
hasAnyNotIn,
|
||||
jobPromise,
|
||||
@ -73,7 +72,6 @@ function verifyAcceptableCfrgKeyUse(name, isPublic, usages) {
|
||||
|
||||
function createCFRGRawKey(name, keyData, isPublic) {
|
||||
const handle = new KeyObjectHandle();
|
||||
keyData = getArrayBufferOrView(keyData, 'keyData');
|
||||
|
||||
switch (name) {
|
||||
case 'Ed25519':
|
||||
@ -237,12 +235,13 @@ async function cfrgImportKey(
|
||||
break;
|
||||
}
|
||||
case 'jwk': {
|
||||
if (keyData == null || typeof keyData !== 'object')
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
if (!keyData.kty)
|
||||
throw lazyDOMException('Invalid keyData', 'DataError');
|
||||
if (keyData.kty !== 'OKP')
|
||||
throw lazyDOMException('Invalid key type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
|
||||
if (keyData.crv !== name)
|
||||
throw lazyDOMException('Subtype mismatch', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "crv" Parameter and algorithm name mismatch', 'DataError');
|
||||
const isPublic = keyData.d === undefined;
|
||||
|
||||
if (usagesSet.size > 0 && keyData.use !== undefined) {
|
||||
@ -260,7 +259,7 @@ async function cfrgImportKey(
|
||||
break;
|
||||
}
|
||||
if (keyData.use !== checkUse)
|
||||
throw lazyDOMException('Invalid use type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
|
||||
}
|
||||
|
||||
validateKeyOps(keyData.key_ops, usagesSet);
|
||||
@ -268,22 +267,24 @@ async function cfrgImportKey(
|
||||
if (keyData.ext !== undefined &&
|
||||
keyData.ext === false &&
|
||||
extractable === true) {
|
||||
throw lazyDOMException('JWK is not extractable', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "ext" Parameter and extractable mismatch',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
if (keyData.alg !== undefined) {
|
||||
if (typeof keyData.alg !== 'string')
|
||||
throw lazyDOMException('Invalid alg', 'DataError');
|
||||
if (
|
||||
(name === 'Ed25519' || name === 'Ed448') &&
|
||||
keyData.alg !== 'EdDSA'
|
||||
) {
|
||||
throw lazyDOMException('Invalid alg', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "alg" does not match the requested algorithm',
|
||||
'DataError');
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPublic && typeof keyData.x !== 'string') {
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK', 'DataError');
|
||||
}
|
||||
|
||||
verifyAcceptableCfrgKeyUse(
|
||||
@ -305,7 +306,7 @@ async function cfrgImportKey(
|
||||
false);
|
||||
|
||||
if (!createPublicKey(keyObject).equals(publicKeyObject)) {
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK', 'DataError');
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -336,13 +337,9 @@ function eddsaSignVerify(key, data, { name, context }, signature) {
|
||||
if (key.type !== type)
|
||||
throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
|
||||
|
||||
if (name === 'Ed448' && context !== undefined) {
|
||||
context =
|
||||
getArrayBufferOrView(context, 'algorithm.context');
|
||||
if (context.byteLength !== 0) {
|
||||
throw lazyDOMException(
|
||||
'Non zero-length context is not yet supported.', 'NotSupportedError');
|
||||
}
|
||||
if (name === 'Ed448' && context?.byteLength) {
|
||||
throw lazyDOMException(
|
||||
'Non zero-length context is not yet supported.', 'NotSupportedError');
|
||||
}
|
||||
|
||||
return jobPromise(() => new SignJob(
|
||||
|
@ -34,7 +34,6 @@ const {
|
||||
validateInt32,
|
||||
validateObject,
|
||||
validateString,
|
||||
validateUint32,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
@ -48,7 +47,6 @@ const {
|
||||
|
||||
const {
|
||||
KeyObject,
|
||||
isCryptoKey,
|
||||
} = require('internal/crypto/keys');
|
||||
|
||||
const {
|
||||
@ -320,13 +318,6 @@ function diffieHellman(options) {
|
||||
async function ecdhDeriveBits(algorithm, baseKey, length) {
|
||||
const { 'public': key } = algorithm;
|
||||
|
||||
// Null means that we're not asking for a specific number of bits, just
|
||||
// give us everything that is generated.
|
||||
if (length !== null)
|
||||
validateUint32(length, 'length');
|
||||
if (!isCryptoKey(key))
|
||||
throw new ERR_INVALID_ARG_TYPE('algorithm.public', 'CryptoKey', key);
|
||||
|
||||
if (key.type !== 'public') {
|
||||
throw lazyDOMException(
|
||||
'algorithm.public must be a public key', 'InvalidAccessError');
|
||||
|
@ -18,13 +18,6 @@ const {
|
||||
} = internalBinding('crypto');
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_MISSING_OPTION,
|
||||
}
|
||||
} = require('internal/errors');
|
||||
|
||||
const {
|
||||
getArrayBufferOrView,
|
||||
getUsagesUnion,
|
||||
hasAnyNotIn,
|
||||
jobPromise,
|
||||
@ -76,7 +69,6 @@ function verifyAcceptableEcKeyUse(name, isPublic, usages) {
|
||||
|
||||
function createECPublicKeyRaw(namedCurve, keyData) {
|
||||
const handle = new KeyObjectHandle();
|
||||
keyData = getArrayBufferOrView(keyData, 'keyData');
|
||||
|
||||
if (!handle.initECRaw(kNamedCurveAliases[namedCurve], keyData)) {
|
||||
throw lazyDOMException('Invalid keyData', 'DataError');
|
||||
@ -204,12 +196,14 @@ async function ecImportKey(
|
||||
break;
|
||||
}
|
||||
case 'jwk': {
|
||||
if (keyData == null || typeof keyData !== 'object')
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
if (!keyData.kty)
|
||||
throw lazyDOMException('Invalid keyData', 'DataError');
|
||||
if (keyData.kty !== 'EC')
|
||||
throw lazyDOMException('Invalid key type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
|
||||
if (keyData.crv !== namedCurve)
|
||||
throw lazyDOMException('Named curve mismatch', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "crv" does not match the requested algorithm',
|
||||
'DataError');
|
||||
|
||||
verifyAcceptableEcKeyUse(
|
||||
name,
|
||||
@ -217,10 +211,9 @@ async function ecImportKey(
|
||||
usagesSet);
|
||||
|
||||
if (usagesSet.size > 0 && keyData.use !== undefined) {
|
||||
if (algorithm.name === 'ECDSA' && keyData.use !== 'sig')
|
||||
throw lazyDOMException('Invalid use type', 'DataError');
|
||||
if (algorithm.name === 'ECDH' && keyData.use !== 'enc')
|
||||
throw lazyDOMException('Invalid use type', 'DataError');
|
||||
const checkUse = name === 'ECDH' ? 'enc' : 'sig';
|
||||
if (keyData.use !== checkUse)
|
||||
throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
|
||||
}
|
||||
|
||||
validateKeyOps(keyData.key_ops, usagesSet);
|
||||
@ -228,12 +221,12 @@ async function ecImportKey(
|
||||
if (keyData.ext !== undefined &&
|
||||
keyData.ext === false &&
|
||||
extractable === true) {
|
||||
throw lazyDOMException('JWK is not extractable', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "ext" Parameter and extractable mismatch',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
if (algorithm.name === 'ECDSA' && keyData.alg !== undefined) {
|
||||
if (typeof keyData.alg !== 'string')
|
||||
throw lazyDOMException('Invalid alg', 'DataError');
|
||||
let algNamedCurve;
|
||||
switch (keyData.alg) {
|
||||
case 'ES256': algNamedCurve = 'P-256'; break;
|
||||
@ -241,13 +234,15 @@ async function ecImportKey(
|
||||
case 'ES512': algNamedCurve = 'P-521'; break;
|
||||
}
|
||||
if (algNamedCurve !== namedCurve)
|
||||
throw lazyDOMException('Named curve mismatch', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "alg" does not match the requested algorithm',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
const handle = new KeyObjectHandle();
|
||||
const type = handle.initJwk(keyData, namedCurve);
|
||||
if (type === undefined)
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK', 'DataError');
|
||||
keyObject = type === kKeyTypePrivate ?
|
||||
new PrivateKeyObject(handle) :
|
||||
new PublicKeyObject(handle);
|
||||
@ -289,8 +284,6 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
|
||||
if (key.type !== type)
|
||||
throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
|
||||
|
||||
if (hash === undefined)
|
||||
throw new ERR_MISSING_OPTION('algorithm.hash');
|
||||
const hashname = normalizeHashName(hash.name);
|
||||
|
||||
return jobPromise(() => new SignJob(
|
||||
|
@ -14,11 +14,9 @@ const {
|
||||
} = internalBinding('crypto');
|
||||
|
||||
const {
|
||||
getArrayBufferOrView,
|
||||
getDefaultEncoding,
|
||||
getStringOption,
|
||||
jobPromise,
|
||||
normalizeAlgorithm,
|
||||
normalizeHashName,
|
||||
validateMaxBufferLength,
|
||||
kHandle,
|
||||
@ -168,13 +166,8 @@ Hmac.prototype._transform = Hash.prototype._transform;
|
||||
// Implementation for WebCrypto subtle.digest()
|
||||
|
||||
async function asyncDigest(algorithm, data) {
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
data = getArrayBufferOrView(data, 'data');
|
||||
validateMaxBufferLength(data, 'data');
|
||||
|
||||
if (algorithm.length !== undefined)
|
||||
validateUint32(algorithm.length, 'algorithm.length');
|
||||
|
||||
switch (algorithm.name) {
|
||||
case 'SHA-1':
|
||||
// Fall through
|
||||
@ -186,11 +179,10 @@ async function asyncDigest(algorithm, data) {
|
||||
return jobPromise(() => new HashJob(
|
||||
kCryptoJobAsync,
|
||||
normalizeHashName(algorithm.name),
|
||||
data,
|
||||
algorithm.length));
|
||||
data));
|
||||
}
|
||||
|
||||
throw lazyDOMException('Unrecognized name.', 'NotSupportedError');
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -19,7 +19,6 @@ const {
|
||||
const { kMaxLength } = require('buffer');
|
||||
|
||||
const {
|
||||
getArrayBufferOrView,
|
||||
normalizeHashName,
|
||||
toBuf,
|
||||
validateByteSource,
|
||||
@ -45,7 +44,6 @@ const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_OUT_OF_RANGE,
|
||||
ERR_MISSING_OPTION,
|
||||
},
|
||||
hideStackFrames,
|
||||
} = require('internal/errors');
|
||||
@ -140,11 +138,7 @@ function hkdfSync(hash, key, salt, info, length) {
|
||||
|
||||
const hkdfPromise = promisify(hkdf);
|
||||
async function hkdfDeriveBits(algorithm, baseKey, 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');
|
||||
const { hash, salt, info } = algorithm;
|
||||
|
||||
if (length === 0)
|
||||
throw lazyDOMException('length cannot be zero', 'OperationError');
|
||||
|
@ -29,12 +29,6 @@ const {
|
||||
promisify,
|
||||
} = require('internal/util');
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_MISSING_OPTION,
|
||||
}
|
||||
} = require('internal/errors');
|
||||
|
||||
const {
|
||||
generateKey: _generateKey,
|
||||
} = require('internal/crypto/keygen');
|
||||
@ -50,8 +44,6 @@ const generateKey = promisify(_generateKey);
|
||||
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 = getBlockSize(hash.name);
|
||||
@ -78,16 +70,24 @@ async function hmacGenerateKey(algorithm, extractable, keyUsages) {
|
||||
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 { hash } = algorithm;
|
||||
if (hash === undefined)
|
||||
throw new ERR_MISSING_OPTION('algorithm.hash');
|
||||
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
if (hasAnyNotIn(usagesSet, ['sign', 'verify'])) {
|
||||
throw lazyDOMException(
|
||||
@ -115,16 +115,16 @@ async function hmacImportKey(
|
||||
break;
|
||||
}
|
||||
case 'jwk': {
|
||||
if (keyData == null || typeof keyData !== 'object')
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
if (!keyData.kty)
|
||||
throw lazyDOMException('Invalid keyData', 'DataError');
|
||||
|
||||
if (keyData.kty !== 'oct')
|
||||
throw lazyDOMException('Invalid key type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
|
||||
|
||||
if (usagesSet.size > 0 &&
|
||||
keyData.use !== undefined &&
|
||||
keyData.use !== 'sig') {
|
||||
throw lazyDOMException('Invalid use type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
|
||||
}
|
||||
|
||||
validateKeyOps(keyData.key_ops, usagesSet);
|
||||
@ -132,32 +132,16 @@ async function hmacImportKey(
|
||||
if (keyData.ext !== undefined &&
|
||||
keyData.ext === false &&
|
||||
extractable === true) {
|
||||
throw lazyDOMException('JWK is not extractable', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "ext" Parameter and extractable mismatch',
|
||||
'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');
|
||||
}
|
||||
if (keyData.alg !== getAlgorithmName(algorithm.hash.name))
|
||||
throw lazyDOMException(
|
||||
'JWK "alg" does not match the requested algorithm',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
const handle = new KeyObjectHandle();
|
||||
|
@ -15,12 +15,9 @@ const {
|
||||
const {
|
||||
validateFunction,
|
||||
validateInt32,
|
||||
validateInteger,
|
||||
validateString,
|
||||
} = require('internal/validators');
|
||||
|
||||
const { ERR_MISSING_OPTION } = require('internal/errors').codes;
|
||||
|
||||
const {
|
||||
getArrayBufferOrView,
|
||||
getDefaultEncoding,
|
||||
@ -101,19 +98,12 @@ function check(password, salt, iterations, keylen, digest) {
|
||||
|
||||
const pbkdf2Promise = promisify(pbkdf2);
|
||||
async function pbkdf2DeriveBits(algorithm, baseKey, length) {
|
||||
const { iterations } = algorithm;
|
||||
let { hash } = algorithm;
|
||||
const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt');
|
||||
if (hash === undefined)
|
||||
throw new ERR_MISSING_OPTION('algorithm.hash');
|
||||
validateInteger(iterations, 'algorithm.iterations');
|
||||
const { iterations, hash, salt } = algorithm;
|
||||
if (iterations === 0)
|
||||
throw lazyDOMException(
|
||||
'iterations cannot be zero',
|
||||
'OperationError');
|
||||
|
||||
hash = normalizeHashName(hash.name);
|
||||
|
||||
const raw = baseKey[kKeyObject].export();
|
||||
|
||||
if (length === 0)
|
||||
@ -128,7 +118,9 @@ async function pbkdf2DeriveBits(algorithm, baseKey, length) {
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await pbkdf2Promise(raw, salt, iterations, length / 8, hash);
|
||||
result = await pbkdf2Promise(
|
||||
raw, salt, iterations, length / 8, normalizeHashName(hash.name),
|
||||
);
|
||||
} catch (err) {
|
||||
throw lazyDOMException(
|
||||
'The operation failed for an operation-specific reason',
|
||||
|
@ -40,7 +40,6 @@ const { Buffer, kMaxLength } = require('buffer');
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_MISSING_ARGS,
|
||||
ERR_OUT_OF_RANGE,
|
||||
ERR_OPERATION_FAILED,
|
||||
}
|
||||
@ -316,8 +315,6 @@ function onJobDone(buf, callback, error) {
|
||||
// not allowed to exceed 65536 bytes, and can only
|
||||
// be an integer-type TypedArray.
|
||||
function getRandomValues(data) {
|
||||
if (arguments.length < 1)
|
||||
throw new ERR_MISSING_ARGS('typedArray');
|
||||
if (!isTypedArray(data) ||
|
||||
isFloat32Array(data) ||
|
||||
isFloat64Array(data)) {
|
||||
|
@ -21,21 +21,12 @@ const {
|
||||
RSA_PKCS1_PSS_PADDING,
|
||||
} = internalBinding('crypto');
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_MISSING_OPTION,
|
||||
}
|
||||
} = require('internal/errors');
|
||||
|
||||
const {
|
||||
validateInt32,
|
||||
validateUint32,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
bigIntArrayToUnsignedInt,
|
||||
getArrayBufferOrView,
|
||||
getUsagesUnion,
|
||||
hasAnyNotIn,
|
||||
jobPromise,
|
||||
@ -51,10 +42,6 @@ const {
|
||||
promisify,
|
||||
} = require('internal/util');
|
||||
|
||||
const {
|
||||
isUint8Array,
|
||||
} = require('internal/util/types');
|
||||
|
||||
const {
|
||||
InternalCryptoKey,
|
||||
PrivateKeyObject,
|
||||
@ -104,7 +91,6 @@ function rsaOaepCipher(mode, key, data, { label }) {
|
||||
'InvalidAccessError');
|
||||
}
|
||||
if (label !== undefined) {
|
||||
label = getArrayBufferOrView(label, 'algorithm.label');
|
||||
validateMaxBufferLength(label, 'algorithm.label');
|
||||
}
|
||||
|
||||
@ -130,16 +116,6 @@ async function rsaKeyGenerate(
|
||||
hash
|
||||
} = algorithm;
|
||||
|
||||
if (hash === undefined)
|
||||
throw new ERR_MISSING_OPTION('algorithm.hash');
|
||||
validateUint32(modulusLength, 'algorithm.modulusLength');
|
||||
if (!isUint8Array(publicExponent)) {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
'algorithm.publicExponent',
|
||||
'Uint8Array',
|
||||
publicExponent);
|
||||
}
|
||||
|
||||
const usageSet = new SafeSet(keyUsages);
|
||||
|
||||
const publicExponentConverted = bigIntArrayToUnsignedInt(publicExponent);
|
||||
@ -228,10 +204,6 @@ async function rsaImportKey(
|
||||
algorithm,
|
||||
extractable,
|
||||
keyUsages) {
|
||||
const { hash } = algorithm;
|
||||
if (hash === undefined)
|
||||
throw new ERR_MISSING_OPTION('algorithm.hash');
|
||||
|
||||
const usagesSet = new SafeSet(keyUsages);
|
||||
let keyObject;
|
||||
switch (format) {
|
||||
@ -264,21 +236,21 @@ async function rsaImportKey(
|
||||
break;
|
||||
}
|
||||
case 'jwk': {
|
||||
if (keyData == null || typeof keyData !== 'object')
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
if (!keyData.kty)
|
||||
throw lazyDOMException('Invalid keyData', 'DataError');
|
||||
|
||||
if (keyData.kty !== 'RSA')
|
||||
throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
|
||||
|
||||
verifyAcceptableRsaKeyUse(
|
||||
algorithm.name,
|
||||
keyData.d === undefined,
|
||||
usagesSet);
|
||||
|
||||
if (keyData.kty !== 'RSA')
|
||||
throw lazyDOMException('Invalid key type', 'DataError');
|
||||
|
||||
if (usagesSet.size > 0 && keyData.use !== undefined) {
|
||||
const checkUse = algorithm.name === 'RSA-OAEP' ? 'enc' : 'sig';
|
||||
if (keyData.use !== checkUse)
|
||||
throw lazyDOMException('Invalid use type', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
|
||||
}
|
||||
|
||||
validateKeyOps(keyData.key_ops, usagesSet);
|
||||
@ -286,22 +258,24 @@ async function rsaImportKey(
|
||||
if (keyData.ext !== undefined &&
|
||||
keyData.ext === false &&
|
||||
extractable === true) {
|
||||
throw lazyDOMException('JWK is not extractable', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "ext" Parameter and extractable mismatch',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
if (keyData.alg !== undefined) {
|
||||
if (typeof keyData.alg !== 'string')
|
||||
throw lazyDOMException('Invalid alg', 'DataError');
|
||||
const hash =
|
||||
normalizeHashName(keyData.alg, normalizeHashName.kContextWebCrypto);
|
||||
if (hash !== algorithm.hash.name)
|
||||
throw lazyDOMException('Hash mismatch', 'DataError');
|
||||
throw lazyDOMException(
|
||||
'JWK "alg" does not match the requested algorithm',
|
||||
'DataError');
|
||||
}
|
||||
|
||||
const handle = new KeyObjectHandle();
|
||||
const type = handle.initJwk(keyData);
|
||||
if (type === undefined)
|
||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||
throw lazyDOMException('Invalid JWK', 'DataError');
|
||||
|
||||
keyObject = type === kKeyTypePrivate ?
|
||||
new PrivateKeyObject(handle) :
|
||||
|
@ -1,15 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayBufferIsView,
|
||||
ArrayBufferPrototypeGetByteLength,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypePush,
|
||||
BigInt,
|
||||
DataViewPrototypeGetBuffer,
|
||||
DataViewPrototypeGetByteLength,
|
||||
DataViewPrototypeGetByteOffset,
|
||||
FunctionPrototypeBind,
|
||||
Number,
|
||||
ObjectKeys,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
Promise,
|
||||
StringPrototypeToLowerCase,
|
||||
StringPrototypeToUpperCase,
|
||||
Symbol,
|
||||
TypedArrayPrototypeGetBuffer,
|
||||
TypedArrayPrototypeGetByteLength,
|
||||
TypedArrayPrototypeGetByteOffset,
|
||||
TypedArrayPrototypeSlice,
|
||||
Uint8Array,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
@ -55,6 +66,7 @@ const {
|
||||
} = require('internal/util');
|
||||
|
||||
const {
|
||||
isDataView,
|
||||
isArrayBufferView,
|
||||
isAnyArrayBuffer,
|
||||
} = require('internal/util/types');
|
||||
@ -138,40 +150,6 @@ const kNamedCurveAliases = {
|
||||
|
||||
const kAesKeyLengths = [128, 192, 256];
|
||||
|
||||
// These are the only algorithms we currently support
|
||||
// via the Web Crypto API
|
||||
const kAlgorithms = {
|
||||
'rsassa-pkcs1-v1_5': 'RSASSA-PKCS1-v1_5',
|
||||
'rsa-pss': 'RSA-PSS',
|
||||
'rsa-oaep': 'RSA-OAEP',
|
||||
'ecdsa': 'ECDSA',
|
||||
'ecdh': 'ECDH',
|
||||
'aes-ctr': 'AES-CTR',
|
||||
'aes-cbc': 'AES-CBC',
|
||||
'aes-gcm': 'AES-GCM',
|
||||
'aes-kw': 'AES-KW',
|
||||
'hmac': 'HMAC',
|
||||
'sha-1': 'SHA-1',
|
||||
'sha-256': 'SHA-256',
|
||||
'sha-384': 'SHA-384',
|
||||
'sha-512': 'SHA-512',
|
||||
'hkdf': 'HKDF',
|
||||
'pbkdf2': 'PBKDF2',
|
||||
'ed25519': 'Ed25519',
|
||||
'ed448': 'Ed448',
|
||||
'x25519': 'X25519',
|
||||
'x448': 'X448',
|
||||
};
|
||||
const kAlgorithmsKeys = ObjectKeys(kAlgorithms);
|
||||
|
||||
// These are the only export and import formats we currently
|
||||
// support via the Web Crypto API
|
||||
const kExportFormats = [
|
||||
'raw',
|
||||
'pkcs8',
|
||||
'spki',
|
||||
'jwk'];
|
||||
|
||||
// These are the only hash algorithms we currently support via
|
||||
// the Web Crypto API.
|
||||
const kHashTypes = [
|
||||
@ -181,6 +159,119 @@ const kHashTypes = [
|
||||
'SHA-512',
|
||||
];
|
||||
|
||||
const kSupportedAlgorithms = {
|
||||
'digest': {
|
||||
'SHA-1': null,
|
||||
'SHA-256': null,
|
||||
'SHA-384': null,
|
||||
'SHA-512': null,
|
||||
},
|
||||
'generateKey': {
|
||||
'RSASSA-PKCS1-v1_5': 'RsaHashedKeyGenParams',
|
||||
'RSA-PSS': 'RsaHashedKeyGenParams',
|
||||
'RSA-OAEP': 'RsaHashedKeyGenParams',
|
||||
'ECDSA': 'EcKeyGenParams',
|
||||
'ECDH': 'EcKeyGenParams',
|
||||
'AES-CTR': 'AesKeyGenParams',
|
||||
'AES-CBC': 'AesKeyGenParams',
|
||||
'AES-GCM': 'AesKeyGenParams',
|
||||
'AES-KW': 'AesKeyGenParams',
|
||||
'HMAC': 'HmacKeyGenParams',
|
||||
'X25519': null,
|
||||
'Ed25519': null,
|
||||
'X448': null,
|
||||
'Ed448': null,
|
||||
},
|
||||
'sign': {
|
||||
'RSASSA-PKCS1-v1_5': null,
|
||||
'RSA-PSS': 'RsaPssParams',
|
||||
'ECDSA': 'EcdsaParams',
|
||||
'HMAC': null,
|
||||
'Ed25519': null,
|
||||
'Ed448': 'Ed448Params',
|
||||
},
|
||||
'verify': {
|
||||
'RSASSA-PKCS1-v1_5': null,
|
||||
'RSA-PSS': 'RsaPssParams',
|
||||
'ECDSA': 'EcdsaParams',
|
||||
'HMAC': null,
|
||||
'Ed25519': null,
|
||||
'Ed448': 'Ed448Params',
|
||||
},
|
||||
'importKey': {
|
||||
'RSASSA-PKCS1-v1_5': 'RsaHashedImportParams',
|
||||
'RSA-PSS': 'RsaHashedImportParams',
|
||||
'RSA-OAEP': 'RsaHashedImportParams',
|
||||
'ECDSA': 'EcKeyImportParams',
|
||||
'ECDH': 'EcKeyImportParams',
|
||||
'HMAC': 'HmacImportParams',
|
||||
'HKDF': null,
|
||||
'PBKDF2': null,
|
||||
'AES-CTR': null,
|
||||
'AES-CBC': null,
|
||||
'AES-GCM': null,
|
||||
'AES-KW': null,
|
||||
'Ed25519': null,
|
||||
'X25519': null,
|
||||
'Ed448': null,
|
||||
'X448': null,
|
||||
},
|
||||
'deriveBits': {
|
||||
'HKDF': 'HkdfParams',
|
||||
'PBKDF2': 'Pbkdf2Params',
|
||||
'ECDH': 'EcdhKeyDeriveParams',
|
||||
'X25519': 'EcdhKeyDeriveParams',
|
||||
'X448': 'EcdhKeyDeriveParams',
|
||||
},
|
||||
'encrypt': {
|
||||
'RSA-OAEP': 'RsaOaepParams',
|
||||
'AES-CBC': 'AesCbcParams',
|
||||
'AES-GCM': 'AesGcmParams',
|
||||
'AES-CTR': 'AesCtrParams',
|
||||
},
|
||||
'decrypt': {
|
||||
'RSA-OAEP': 'RsaOaepParams',
|
||||
'AES-CBC': 'AesCbcParams',
|
||||
'AES-GCM': 'AesGcmParams',
|
||||
'AES-CTR': 'AesCtrParams',
|
||||
},
|
||||
'get key length': {
|
||||
'AES-CBC': 'AesDerivedKeyParams',
|
||||
'AES-CTR': 'AesDerivedKeyParams',
|
||||
'AES-GCM': 'AesDerivedKeyParams',
|
||||
'AES-KW': 'AesDerivedKeyParams',
|
||||
'HMAC': 'HmacImportParams',
|
||||
'HKDF': null,
|
||||
'PBKDF2': null,
|
||||
},
|
||||
'wrapKey': {
|
||||
'AES-KW': null,
|
||||
},
|
||||
'unwrapKey': {
|
||||
'AES-KW': null,
|
||||
},
|
||||
};
|
||||
|
||||
const simpleAlgorithmDictionaries = {
|
||||
AesGcmParams: { iv: 'BufferSource', additionalData: 'BufferSource' },
|
||||
RsaHashedKeyGenParams: { hash: 'HashAlgorithmIdentifier' },
|
||||
EcKeyGenParams: {},
|
||||
HmacKeyGenParams: { hash: 'HashAlgorithmIdentifier' },
|
||||
RsaPssParams: {},
|
||||
EcdsaParams: { hash: 'HashAlgorithmIdentifier' },
|
||||
HmacImportParams: { hash: 'HashAlgorithmIdentifier' },
|
||||
HkdfParams: {
|
||||
hash: 'HashAlgorithmIdentifier',
|
||||
salt: 'BufferSource',
|
||||
info: 'BufferSource',
|
||||
},
|
||||
Ed448Params: { context: 'BufferSource' },
|
||||
Pbkdf2Params: { hash: 'HashAlgorithmIdentifier', salt: 'BufferSource' },
|
||||
RsaOaepParams: { label: 'BufferSource' },
|
||||
RsaHashedImportParams: { hash: 'HashAlgorithmIdentifier' },
|
||||
EcKeyImportParams: {},
|
||||
};
|
||||
|
||||
function validateMaxBufferLength(data, name) {
|
||||
if (data.byteLength > kMaxBufferLength) {
|
||||
throw lazyDOMException(
|
||||
@ -189,36 +280,99 @@ function validateMaxBufferLength(data, name) {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeAlgorithm(algorithm) {
|
||||
if (algorithm != null) {
|
||||
if (typeof algorithm === 'string')
|
||||
algorithm = { name: algorithm };
|
||||
let webidl;
|
||||
|
||||
if (typeof algorithm === 'object') {
|
||||
const { name } = algorithm;
|
||||
if (typeof name !== 'string' ||
|
||||
!ArrayPrototypeIncludes(
|
||||
kAlgorithmsKeys,
|
||||
StringPrototypeToLowerCase(name))) {
|
||||
throw lazyDOMException('Unrecognized name.', 'NotSupportedError');
|
||||
}
|
||||
let { hash } = algorithm;
|
||||
if (hash !== undefined) {
|
||||
hash = normalizeAlgorithm(hash);
|
||||
if (!ArrayPrototypeIncludes(kHashTypes, hash.name))
|
||||
throw lazyDOMException('Unrecognized name.', 'NotSupportedError');
|
||||
}
|
||||
const normalized = {
|
||||
...algorithm,
|
||||
name: kAlgorithms[StringPrototypeToLowerCase(name)],
|
||||
};
|
||||
if (hash) {
|
||||
normalized.hash = hash;
|
||||
}
|
||||
return normalized;
|
||||
// https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
|
||||
// adapted for Node.js from Deno's implementation
|
||||
// https://github.com/denoland/deno/blob/v1.29.1/ext/crypto/00_crypto.js#L195
|
||||
function normalizeAlgorithm(algorithm, op) {
|
||||
if (typeof algorithm === 'string')
|
||||
return normalizeAlgorithm({ name: algorithm }, op);
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
|
||||
// 1.
|
||||
const registeredAlgorithms = kSupportedAlgorithms[op];
|
||||
// 2. 3.
|
||||
const initialAlg = webidl.converters.Algorithm(algorithm, {
|
||||
prefix: 'Failed to normalize algorithm',
|
||||
context: 'passed algorithm',
|
||||
});
|
||||
// 4.
|
||||
let algName = initialAlg.name;
|
||||
|
||||
// 5.
|
||||
let desiredType;
|
||||
for (const key in registeredAlgorithms) {
|
||||
if (!ObjectPrototypeHasOwnProperty(registeredAlgorithms, key)) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
StringPrototypeToUpperCase(key) === StringPrototypeToUpperCase(algName)
|
||||
) {
|
||||
algName = key;
|
||||
desiredType = registeredAlgorithms[key];
|
||||
}
|
||||
}
|
||||
throw lazyDOMException('Unrecognized name.', 'NotSupportedError');
|
||||
if (desiredType === undefined)
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
|
||||
// Fast path everything below if the registered dictionary is null
|
||||
if (desiredType === null)
|
||||
return { name: algName };
|
||||
|
||||
// 6.
|
||||
const normalizedAlgorithm = webidl.converters[desiredType](algorithm, {
|
||||
prefix: 'Failed to normalize algorithm',
|
||||
context: 'passed algorithm',
|
||||
});
|
||||
// 7.
|
||||
normalizedAlgorithm.name = algName;
|
||||
|
||||
// 9.
|
||||
const dict = simpleAlgorithmDictionaries[desiredType];
|
||||
// 10.
|
||||
const dictKeys = dict ? ObjectKeys(dict) : [];
|
||||
for (let i = 0; i < dictKeys.length; i++) {
|
||||
const member = dictKeys[i];
|
||||
if (!ObjectPrototypeHasOwnProperty(dict, member))
|
||||
continue;
|
||||
const idlType = dict[member];
|
||||
const idlValue = normalizedAlgorithm[member];
|
||||
// 3.
|
||||
if (idlType === 'BufferSource' && idlValue) {
|
||||
const isView = ArrayBufferIsView(idlValue);
|
||||
normalizedAlgorithm[member] = TypedArrayPrototypeSlice(
|
||||
new Uint8Array(
|
||||
isView ? getDataViewOrTypedArrayBuffer(idlValue) : idlValue,
|
||||
isView ? getDataViewOrTypedArrayByteOffset(idlValue) : 0,
|
||||
isView ? getDataViewOrTypedArrayByteLength(idlValue) : ArrayBufferPrototypeGetByteLength(idlValue),
|
||||
),
|
||||
);
|
||||
} else if (idlType === 'HashAlgorithmIdentifier') {
|
||||
normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, 'digest');
|
||||
} else if (idlType === 'AlgorithmIdentifier') {
|
||||
// This extension point is not used by any supported algorithm (yet?)
|
||||
throw lazyDOMException('Not implemented.', 'NotSupportedError');
|
||||
}
|
||||
}
|
||||
|
||||
return normalizedAlgorithm;
|
||||
}
|
||||
|
||||
function getDataViewOrTypedArrayBuffer(V) {
|
||||
return isDataView(V) ?
|
||||
DataViewPrototypeGetBuffer(V) : TypedArrayPrototypeGetBuffer(V);
|
||||
}
|
||||
|
||||
function getDataViewOrTypedArrayByteOffset(V) {
|
||||
return isDataView(V) ?
|
||||
DataViewPrototypeGetByteOffset(V) : TypedArrayPrototypeGetByteOffset(V);
|
||||
}
|
||||
|
||||
function getDataViewOrTypedArrayByteLength(V) {
|
||||
return isDataView(V) ?
|
||||
DataViewPrototypeGetByteLength(V) : TypedArrayPrototypeGetByteLength(V);
|
||||
}
|
||||
|
||||
function hasAnyNotIn(set, checks) {
|
||||
@ -402,6 +556,7 @@ module.exports = {
|
||||
getArrayBufferOrView,
|
||||
getCiphers,
|
||||
getCurves,
|
||||
getDataViewOrTypedArrayBuffer,
|
||||
getDefaultEncoding,
|
||||
getHashes,
|
||||
kHandle,
|
||||
@ -413,7 +568,6 @@ module.exports = {
|
||||
kHashTypes,
|
||||
kNamedCurveAliases,
|
||||
kAesKeyLengths,
|
||||
kExportFormats,
|
||||
normalizeAlgorithm,
|
||||
normalizeHashName,
|
||||
hasAnyNotIn,
|
||||
|
@ -10,7 +10,6 @@ const {
|
||||
ReflectConstruct,
|
||||
SafeSet,
|
||||
SymbolToStringTag,
|
||||
StringPrototypeRepeat,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
@ -21,14 +20,6 @@ const {
|
||||
kWebCryptoCipherDecrypt,
|
||||
} = internalBinding('crypto');
|
||||
|
||||
const {
|
||||
validateArray,
|
||||
validateBoolean,
|
||||
validateObject,
|
||||
validateOneOf,
|
||||
validateString,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
getOptionValue,
|
||||
} = require('internal/options');
|
||||
@ -38,7 +29,6 @@ const { TextDecoder, TextEncoder } = require('internal/encoding');
|
||||
const {
|
||||
codes: {
|
||||
ERR_ILLEGAL_CONSTRUCTOR,
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_INVALID_THIS,
|
||||
}
|
||||
} = require('internal/errors');
|
||||
@ -47,7 +37,6 @@ const {
|
||||
CryptoKey,
|
||||
InternalCryptoKey,
|
||||
createSecretKey,
|
||||
isCryptoKey,
|
||||
} = require('internal/crypto/keys');
|
||||
|
||||
const {
|
||||
@ -55,13 +44,11 @@ const {
|
||||
} = require('internal/crypto/hash');
|
||||
|
||||
const {
|
||||
getArrayBufferOrView,
|
||||
getBlockSize,
|
||||
hasAnyNotIn,
|
||||
normalizeAlgorithm,
|
||||
normalizeHashName,
|
||||
validateMaxBufferLength,
|
||||
kExportFormats,
|
||||
kHandle,
|
||||
kKeyObject,
|
||||
} = require('internal/crypto/util');
|
||||
@ -76,9 +63,26 @@ const {
|
||||
randomUUID: _randomUUID,
|
||||
} = require('internal/crypto/random');
|
||||
|
||||
let webidl;
|
||||
|
||||
async function digest(algorithm, data) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
return ReflectApply(asyncDigest, this, arguments);
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'digest' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
data = webidl.converters.BufferSource(data, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
|
||||
algorithm = normalizeAlgorithm(algorithm, 'digest');
|
||||
|
||||
return ReflectApply(asyncDigest, this, [algorithm, data]);
|
||||
}
|
||||
|
||||
function randomUUID() {
|
||||
@ -91,9 +95,24 @@ async function generateKey(
|
||||
extractable,
|
||||
keyUsages) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
validateBoolean(extractable, 'extractable');
|
||||
validateArray(keyUsages, 'keyUsages');
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'generateKey' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 3, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
extractable = webidl.converters.boolean(extractable, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
|
||||
algorithm = normalizeAlgorithm(algorithm, 'generateKey');
|
||||
let result;
|
||||
let resultType;
|
||||
switch (algorithm.name) {
|
||||
@ -141,7 +160,7 @@ async function generateKey(
|
||||
.aesGenerateKey(algorithm, extractable, keyUsages);
|
||||
break;
|
||||
default:
|
||||
throw lazyDOMException('Unrecognized name.');
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
if (
|
||||
@ -160,9 +179,26 @@ async function generateKey(
|
||||
|
||||
async function deriveBits(algorithm, baseKey, length) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
if (!isCryptoKey(baseKey))
|
||||
throw new ERR_INVALID_ARG_TYPE('baseKey', 'CryptoKey', baseKey);
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'deriveBits' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 3, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
baseKey = webidl.converters.CryptoKey(baseKey, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
if (length !== null) {
|
||||
length = webidl.converters['unsigned long'](length, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
}
|
||||
|
||||
algorithm = normalizeAlgorithm(algorithm, 'deriveBits');
|
||||
if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveBits')) {
|
||||
throw lazyDOMException(
|
||||
'baseKey does not have deriveBits usage',
|
||||
@ -185,7 +221,7 @@ async function deriveBits(algorithm, baseKey, length) {
|
||||
return require('internal/crypto/pbkdf2')
|
||||
.pbkdf2DeriveBits(algorithm, baseKey, length);
|
||||
}
|
||||
throw lazyDOMException('Unrecognized name.');
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
function getKeyLength({ name, length, hash }) {
|
||||
@ -221,10 +257,33 @@ async function deriveKey(
|
||||
extractable,
|
||||
keyUsages) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm);
|
||||
if (!isCryptoKey(baseKey))
|
||||
throw new ERR_INVALID_ARG_TYPE('baseKey', 'CryptoKey', baseKey);
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'deriveKey' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 5, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
baseKey = webidl.converters.CryptoKey(baseKey, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
derivedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(derivedKeyAlgorithm, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
extractable = webidl.converters.boolean(extractable, {
|
||||
prefix,
|
||||
context: '4th argument',
|
||||
});
|
||||
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
|
||||
prefix,
|
||||
context: '5th argument',
|
||||
});
|
||||
|
||||
algorithm = normalizeAlgorithm(algorithm, 'deriveBits');
|
||||
derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm, 'importKey');
|
||||
if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveKey')) {
|
||||
throw lazyDOMException(
|
||||
'baseKey does not have deriveKey usage',
|
||||
@ -232,13 +291,8 @@ async function deriveKey(
|
||||
}
|
||||
if (baseKey.algorithm.name !== algorithm.name)
|
||||
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
|
||||
validateObject(derivedKeyAlgorithm, 'derivedKeyAlgorithm', {
|
||||
allowArray: true, allowFunction: true,
|
||||
});
|
||||
validateBoolean(extractable, 'extractable');
|
||||
validateArray(keyUsages, 'keyUsages');
|
||||
|
||||
const length = getKeyLength(derivedKeyAlgorithm);
|
||||
const length = getKeyLength(normalizeAlgorithm(arguments[2], 'get key length'));
|
||||
let bits;
|
||||
switch (algorithm.name) {
|
||||
case 'X25519':
|
||||
@ -258,7 +312,7 @@ async function deriveKey(
|
||||
.pbkdf2DeriveBits(algorithm, baseKey, length);
|
||||
break;
|
||||
default:
|
||||
throw lazyDOMException('Unrecognized name.');
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
return ReflectApply(
|
||||
@ -446,10 +500,18 @@ async function exportKeyJWK(key) {
|
||||
|
||||
async function exportKey(format, key) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
validateString(format, 'format');
|
||||
validateOneOf(format, 'format', kExportFormats);
|
||||
if (!isCryptoKey(key))
|
||||
throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'exportKey' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||
format = webidl.converters.KeyFormat(format, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
key = webidl.converters.CryptoKey(key, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
|
||||
if (!key.extractable)
|
||||
throw lazyDOMException('key is not extractable', 'InvalidAccessException');
|
||||
@ -515,13 +577,33 @@ async function importKey(
|
||||
extractable,
|
||||
keyUsages) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
validateString(format, 'format');
|
||||
validateOneOf(format, 'format', kExportFormats);
|
||||
if (format !== 'jwk')
|
||||
keyData = getArrayBufferOrView(keyData, 'keyData');
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
validateBoolean(extractable, 'extractable');
|
||||
validateArray(keyUsages, 'keyUsages');
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'importKey' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 4, { prefix });
|
||||
format = webidl.converters.KeyFormat(format, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
const type = format === 'jwk' ? 'JsonWebKey' : 'BufferSource';
|
||||
keyData = webidl.converters[type](keyData, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
extractable = webidl.converters.boolean(extractable, {
|
||||
prefix,
|
||||
context: '4th argument',
|
||||
});
|
||||
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
|
||||
prefix,
|
||||
context: '5th argument',
|
||||
});
|
||||
|
||||
algorithm = normalizeAlgorithm(algorithm, 'importKey');
|
||||
switch (algorithm.name) {
|
||||
case 'RSASSA-PKCS1-v1_5':
|
||||
// Fall through
|
||||
@ -567,22 +649,43 @@ async function importKey(
|
||||
keyUsages);
|
||||
}
|
||||
|
||||
throw lazyDOMException('Unrecognized name.', 'NotSupportedError');
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
// subtle.wrapKey() is essentially a subtle.exportKey() followed
|
||||
// by a subtle.encrypt().
|
||||
async function wrapKey(format, key, wrappingKey, algorithm) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'wrapKey' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 4, { prefix });
|
||||
format = webidl.converters.KeyFormat(format, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
key = webidl.converters.CryptoKey(key, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
wrappingKey = webidl.converters.CryptoKey(wrappingKey, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '4th argument',
|
||||
});
|
||||
|
||||
try {
|
||||
algorithm = normalizeAlgorithm(algorithm, 'wrapKey');
|
||||
} catch {
|
||||
algorithm = normalizeAlgorithm(algorithm, 'encrypt');
|
||||
}
|
||||
let keyData = await ReflectApply(exportKey, this, [format, key]);
|
||||
|
||||
if (format === 'jwk') {
|
||||
if (keyData == null || typeof keyData !== 'object')
|
||||
throw lazyDOMException('Invalid exported JWK key', 'DataError');
|
||||
const ec = new TextEncoder();
|
||||
const raw = JSONStringify(keyData);
|
||||
keyData = ec.encode(raw + StringPrototypeRepeat(' ', 8 - (raw.length % 8)));
|
||||
keyData = new TextEncoder().encode(JSONStringify(keyData));
|
||||
}
|
||||
|
||||
return cipherOrWrap(
|
||||
@ -604,8 +707,48 @@ async function unwrapKey(
|
||||
extractable,
|
||||
keyUsages) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
wrappedKey = getArrayBufferOrView(wrappedKey, 'wrappedKey');
|
||||
unwrapAlgo = normalizeAlgorithm(unwrapAlgo);
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'unwrapKey' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 7, { prefix });
|
||||
format = webidl.converters.KeyFormat(format, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
wrappedKey = webidl.converters.BufferSource(wrappedKey, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
unwrappingKey = webidl.converters.CryptoKey(unwrappingKey, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
unwrapAlgo = webidl.converters.AlgorithmIdentifier(unwrapAlgo, {
|
||||
prefix,
|
||||
context: '4th argument',
|
||||
});
|
||||
unwrappedKeyAlgo = webidl.converters.AlgorithmIdentifier(
|
||||
unwrappedKeyAlgo,
|
||||
{
|
||||
prefix,
|
||||
context: '5th argument',
|
||||
},
|
||||
);
|
||||
extractable = webidl.converters.boolean(extractable, {
|
||||
prefix,
|
||||
context: '6th argument',
|
||||
});
|
||||
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
|
||||
prefix,
|
||||
context: '7th argument',
|
||||
});
|
||||
|
||||
try {
|
||||
unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'unwrapKey');
|
||||
} catch {
|
||||
unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'decrypt');
|
||||
}
|
||||
|
||||
let keyData = await cipherOrWrap(
|
||||
kWebCryptoCipherDecrypt,
|
||||
unwrapAlgo,
|
||||
@ -621,7 +764,7 @@ async function unwrapKey(
|
||||
try {
|
||||
keyData = JSONParse(dec.decode(keyData));
|
||||
} catch {
|
||||
throw lazyDOMException('Invalid imported JWK key', 'DataError');
|
||||
throw lazyDOMException('Invalid wrapped JWK key', 'DataError');
|
||||
}
|
||||
}
|
||||
|
||||
@ -633,15 +776,11 @@ async function unwrapKey(
|
||||
}
|
||||
|
||||
function signVerify(algorithm, key, data, signature) {
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
if (!isCryptoKey(key))
|
||||
throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
|
||||
data = getArrayBufferOrView(data, 'data');
|
||||
let usage = 'sign';
|
||||
if (signature !== undefined) {
|
||||
signature = getArrayBufferOrView(signature, 'signature');
|
||||
usage = 'verify';
|
||||
}
|
||||
algorithm = normalizeAlgorithm(algorithm, usage);
|
||||
|
||||
if (!ArrayPrototypeIncludes(key.usages, usage) ||
|
||||
algorithm.name !== key.algorithm.name) {
|
||||
@ -669,26 +808,61 @@ function signVerify(algorithm, key, data, signature) {
|
||||
return require('internal/crypto/mac')
|
||||
.hmacSignVerify(key, data, algorithm, signature);
|
||||
}
|
||||
throw lazyDOMException('Unrecognized named.', 'NotSupportedError');
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
async function sign(algorithm, key, data) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'sign' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 3, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
key = webidl.converters.CryptoKey(key, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
data = webidl.converters.BufferSource(data, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
|
||||
return signVerify(algorithm, key, data);
|
||||
}
|
||||
|
||||
async function verify(algorithm, key, signature, data) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'verify' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 4, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
key = webidl.converters.CryptoKey(key, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
signature = webidl.converters.BufferSource(signature, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
data = webidl.converters.BufferSource(data, {
|
||||
prefix,
|
||||
context: '4th argument',
|
||||
});
|
||||
|
||||
return signVerify(algorithm, key, data, signature);
|
||||
}
|
||||
|
||||
async function cipherOrWrap(mode, algorithm, key, data, op) {
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
// We use a Node.js style error here instead of a DOMException because
|
||||
// the WebCrypto spec is not specific what kind of error is to be thrown
|
||||
// in this case. Both Firefox and Chrome throw simple TypeErrors here.
|
||||
if (!isCryptoKey(key))
|
||||
throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
|
||||
// The key algorithm and cipher algorithm must match, and the
|
||||
// key must have the proper usage.
|
||||
if (key.algorithm.name !== algorithm.name ||
|
||||
@ -698,10 +872,6 @@ async function cipherOrWrap(mode, algorithm, key, data, op) {
|
||||
'InvalidAccessError');
|
||||
}
|
||||
|
||||
// For the Web Crypto API, the input data can be any ArrayBuffer,
|
||||
// TypedArray, or DataView.
|
||||
data = getArrayBufferOrView(data, 'data');
|
||||
|
||||
// While WebCrypto allows for larger input buffer sizes, we limit
|
||||
// those to sizes that can fit within uint32_t because of limitations
|
||||
// in the OpenSSL API.
|
||||
@ -724,16 +894,52 @@ async function cipherOrWrap(mode, algorithm, key, data, op) {
|
||||
.aesCipher(mode, key, data, algorithm);
|
||||
}
|
||||
}
|
||||
throw lazyDOMException('Unrecognized name.', 'NotSupportedError');
|
||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||
}
|
||||
|
||||
async function encrypt(algorithm, key, data) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'encrypt' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 3, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
key = webidl.converters.CryptoKey(key, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
data = webidl.converters.BufferSource(data, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
|
||||
algorithm = normalizeAlgorithm(algorithm, 'encrypt');
|
||||
return cipherOrWrap(kWebCryptoCipherEncrypt, algorithm, key, data, 'encrypt');
|
||||
}
|
||||
|
||||
async function decrypt(algorithm, key, data) {
|
||||
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'decrypt' on 'SubtleCrypto'";
|
||||
webidl.requiredArguments(arguments.length, 3, { prefix });
|
||||
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||
prefix,
|
||||
context: '1st argument',
|
||||
});
|
||||
key = webidl.converters.CryptoKey(key, {
|
||||
prefix,
|
||||
context: '2nd argument',
|
||||
});
|
||||
data = webidl.converters.BufferSource(data, {
|
||||
prefix,
|
||||
context: '3rd argument',
|
||||
});
|
||||
|
||||
algorithm = normalizeAlgorithm(algorithm, 'decrypt');
|
||||
return cipherOrWrap(kWebCryptoCipherDecrypt, algorithm, key, data, 'decrypt');
|
||||
}
|
||||
|
||||
@ -761,6 +967,11 @@ const crypto = ReflectConstruct(function() {}, [], Crypto);
|
||||
|
||||
function getRandomValues(array) {
|
||||
if (this !== crypto) throw new ERR_INVALID_THIS('Crypto');
|
||||
|
||||
webidl ??= require('internal/crypto/webidl');
|
||||
const prefix = "Failed to execute 'getRandomValues' on 'Crypto'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
|
||||
return ReflectApply(_getRandomValues, this, arguments);
|
||||
}
|
||||
|
||||
|
707
lib/internal/crypto/webidl.js
Normal file
707
lib/internal/crypto/webidl.js
Normal file
@ -0,0 +1,707 @@
|
||||
'use strict';
|
||||
|
||||
// Adapted from the following sources
|
||||
// - https://github.com/jsdom/webidl-conversions
|
||||
// Copyright Domenic Denicola. Licensed under BSD-2-Clause License.
|
||||
// Original license at https://github.com/jsdom/webidl-conversions/blob/master/LICENSE.md.
|
||||
// - https://github.com/denoland/deno
|
||||
// Copyright Deno authors. Licensed under MIT License.
|
||||
// Original license at https://github.com/denoland/deno/blob/main/LICENSE.md.
|
||||
// Changes include using primordials and stripping the code down to only what
|
||||
// WebCryptoAPI needs.
|
||||
|
||||
const {
|
||||
ArrayBufferIsView,
|
||||
ArrayBufferPrototype,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSort,
|
||||
MathPow,
|
||||
MathTrunc,
|
||||
Number,
|
||||
NumberIsFinite,
|
||||
ObjectAssign,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
SafeArrayIterator,
|
||||
SafeSet,
|
||||
String,
|
||||
SymbolIterator,
|
||||
TypedArrayPrototypeGetBuffer,
|
||||
TypedArrayPrototypeGetSymbolToStringTag,
|
||||
TypeError,
|
||||
globalThis: {
|
||||
SharedArrayBuffer,
|
||||
},
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
kEmptyObject,
|
||||
setOwnProperty,
|
||||
} = require('internal/util');
|
||||
const { CryptoKey } = require('internal/crypto/webcrypto');
|
||||
const { getDataViewOrTypedArrayBuffer } = require('internal/crypto/util');
|
||||
|
||||
function codedTypeError(message, errorProperties = kEmptyObject) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const err = new TypeError(message);
|
||||
ObjectAssign(err, errorProperties);
|
||||
return err;
|
||||
}
|
||||
|
||||
function makeException(message, opts = kEmptyObject) {
|
||||
const prefix = opts.prefix ? opts.prefix + ': ' : '';
|
||||
const context = opts.context?.length === 0 ?
|
||||
'' : (opts.context ?? 'Value') + ' ';
|
||||
return codedTypeError(
|
||||
`${prefix}${context}${message}`,
|
||||
{ code: opts.code || 'ERR_INVALID_ARG_TYPE' },
|
||||
);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-tonumber
|
||||
function toNumber(value, opts = kEmptyObject) {
|
||||
switch (typeof value) {
|
||||
case 'number':
|
||||
return value;
|
||||
case 'bigint':
|
||||
throw makeException(
|
||||
'is a BigInt and cannot be converted to a number.',
|
||||
opts);
|
||||
case 'symbol':
|
||||
throw makeException(
|
||||
'is a Symbol and cannot be converted to a number.',
|
||||
opts);
|
||||
default:
|
||||
return Number(value);
|
||||
}
|
||||
}
|
||||
|
||||
function type(V) {
|
||||
if (V === null)
|
||||
return 'Null';
|
||||
|
||||
switch (typeof V) {
|
||||
case 'undefined':
|
||||
return 'Undefined';
|
||||
case 'boolean':
|
||||
return 'Boolean';
|
||||
case 'number':
|
||||
return 'Number';
|
||||
case 'string':
|
||||
return 'String';
|
||||
case 'symbol':
|
||||
return 'Symbol';
|
||||
case 'bigint':
|
||||
return 'BigInt';
|
||||
case 'object': // Fall through
|
||||
case 'function': // Fall through
|
||||
default:
|
||||
// Per ES spec, typeof returns an implemention-defined value that is not
|
||||
// any of the existing ones for uncallable non-standard exotic objects.
|
||||
// Yet Type() which the Web IDL spec depends on returns Object for such
|
||||
// cases. So treat the default case as an object.
|
||||
return 'Object';
|
||||
}
|
||||
}
|
||||
|
||||
const integerPart = MathTrunc;
|
||||
|
||||
// This was updated to only consider bitlength up to 32 used by WebCryptoAPI
|
||||
function createIntegerConversion(bitLength) {
|
||||
const lowerBound = 0;
|
||||
const upperBound = MathPow(2, bitLength) - 1;
|
||||
|
||||
const twoToTheBitLength = MathPow(2, bitLength);
|
||||
|
||||
return (V, opts = kEmptyObject) => {
|
||||
let x = toNumber(V, opts);
|
||||
|
||||
if (opts.enforceRange) {
|
||||
if (!NumberIsFinite(x)) {
|
||||
throw makeException(
|
||||
'is not a finite number.',
|
||||
opts);
|
||||
}
|
||||
|
||||
x = integerPart(x);
|
||||
|
||||
if (x < lowerBound || x > upperBound) {
|
||||
throw makeException(
|
||||
`is outside the expected range of ${lowerBound} to ${upperBound}.`,
|
||||
{ __proto__: null, ...opts, code: 'ERR_OUT_OF_RANGE' },
|
||||
);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
if (!NumberIsFinite(x) || x === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
x = integerPart(x);
|
||||
|
||||
if (x >= lowerBound && x <= upperBound) {
|
||||
return x;
|
||||
}
|
||||
|
||||
x = x % twoToTheBitLength;
|
||||
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
const converters = {};
|
||||
|
||||
converters.boolean = (val) => !!val;
|
||||
converters.octet = createIntegerConversion(8);
|
||||
converters['unsigned short'] = createIntegerConversion(16);
|
||||
converters['unsigned long'] = createIntegerConversion(32);
|
||||
|
||||
converters.DOMString = function(V, opts = kEmptyObject) {
|
||||
if (typeof V === 'string') {
|
||||
return V;
|
||||
} else if (typeof V === 'symbol') {
|
||||
throw makeException(
|
||||
'is a Symbol and cannot be converted to a string.',
|
||||
opts);
|
||||
}
|
||||
|
||||
return String(V);
|
||||
};
|
||||
|
||||
converters.object = (V, opts) => {
|
||||
if (type(V) !== 'Object') {
|
||||
throw makeException(
|
||||
'is not an object.',
|
||||
opts);
|
||||
}
|
||||
|
||||
return V;
|
||||
};
|
||||
|
||||
function isNonSharedArrayBuffer(V) {
|
||||
return ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V);
|
||||
}
|
||||
|
||||
function isSharedArrayBuffer(V) {
|
||||
return ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V);
|
||||
}
|
||||
|
||||
converters.Uint8Array = (V, opts = kEmptyObject) => {
|
||||
if (!ArrayBufferIsView(V) ||
|
||||
TypedArrayPrototypeGetSymbolToStringTag(V) !== 'Uint8Array') {
|
||||
throw makeException(
|
||||
'is not an Uint8Array object.',
|
||||
opts);
|
||||
}
|
||||
if (isSharedArrayBuffer(TypedArrayPrototypeGetBuffer(V))) {
|
||||
throw makeException(
|
||||
'is a view on a SharedArrayBuffer, which is not allowed.',
|
||||
opts);
|
||||
}
|
||||
|
||||
return V;
|
||||
};
|
||||
|
||||
converters.BufferSource = (V, opts = kEmptyObject) => {
|
||||
if (ArrayBufferIsView(V)) {
|
||||
if (isSharedArrayBuffer(getDataViewOrTypedArrayBuffer(V))) {
|
||||
throw makeException(
|
||||
'is a view on a SharedArrayBuffer, which is not allowed.',
|
||||
opts);
|
||||
}
|
||||
|
||||
return V;
|
||||
}
|
||||
|
||||
if (!isNonSharedArrayBuffer(V)) {
|
||||
throw makeException(
|
||||
'is not instance of ArrayBuffer, Buffer, TypedArray, or DataView.',
|
||||
opts);
|
||||
}
|
||||
|
||||
return V;
|
||||
};
|
||||
|
||||
converters['sequence<DOMString>'] = createSequenceConverter(
|
||||
converters.DOMString);
|
||||
|
||||
function requiredArguments(length, required, opts = kEmptyObject) {
|
||||
if (length < required) {
|
||||
throw makeException(
|
||||
`${required} argument${
|
||||
required === 1 ? '' : 's'
|
||||
} required, but only ${length} present.`,
|
||||
{ __proto__: null, ...opts, context: '', code: 'ERR_MISSING_ARGS' });
|
||||
}
|
||||
}
|
||||
|
||||
function createDictionaryConverter(name, dictionaries) {
|
||||
let hasRequiredKey = false;
|
||||
const allMembers = [];
|
||||
for (let i = 0; i < dictionaries.length; i++) {
|
||||
const member = dictionaries[i];
|
||||
if (member.required) {
|
||||
hasRequiredKey = true;
|
||||
}
|
||||
ArrayPrototypePush(allMembers, member);
|
||||
}
|
||||
ArrayPrototypeSort(allMembers, (a, b) => {
|
||||
if (a.key === b.key) {
|
||||
return 0;
|
||||
}
|
||||
return a.key < b.key ? -1 : 1;
|
||||
});
|
||||
|
||||
return function(V, opts = kEmptyObject) {
|
||||
const typeV = type(V);
|
||||
switch (typeV) {
|
||||
case 'Undefined':
|
||||
case 'Null':
|
||||
case 'Object':
|
||||
break;
|
||||
default:
|
||||
throw makeException(
|
||||
'can not be converted to a dictionary',
|
||||
opts);
|
||||
}
|
||||
const esDict = V;
|
||||
const idlDict = {};
|
||||
|
||||
// Fast path null and undefined.
|
||||
if (V == null && !hasRequiredKey) {
|
||||
return idlDict;
|
||||
}
|
||||
|
||||
for (const member of new SafeArrayIterator(allMembers)) {
|
||||
const key = member.key;
|
||||
|
||||
let esMemberValue;
|
||||
if (typeV === 'Undefined' || typeV === 'Null') {
|
||||
esMemberValue = undefined;
|
||||
} else {
|
||||
esMemberValue = esDict[key];
|
||||
}
|
||||
|
||||
if (esMemberValue !== undefined) {
|
||||
const context = `'${key}' of '${name}'${
|
||||
opts.context ? ` (${opts.context})` : ''
|
||||
}`;
|
||||
const converter = member.converter;
|
||||
const idlMemberValue = converter(esMemberValue, {
|
||||
__proto__: null,
|
||||
...opts,
|
||||
context,
|
||||
});
|
||||
setOwnProperty(idlDict, key, idlMemberValue);
|
||||
} else if (member.required) {
|
||||
throw makeException(
|
||||
`can not be converted to '${name}' because '${key}' is required in '${name}'.`,
|
||||
{ __proto__: null, ...opts, code: 'ERR_MISSING_OPTION' });
|
||||
}
|
||||
}
|
||||
|
||||
return idlDict;
|
||||
};
|
||||
}
|
||||
|
||||
function createEnumConverter(name, values) {
|
||||
const E = new SafeSet(values);
|
||||
|
||||
return function(V, opts = kEmptyObject) {
|
||||
const S = String(V);
|
||||
|
||||
if (!E.has(S)) {
|
||||
throw makeException(
|
||||
`value '${S}' is not a valid enum value of type ${name}.`,
|
||||
{ __proto__: null, ...opts, code: 'ERR_INVALID_ARG_VALUE' });
|
||||
}
|
||||
|
||||
return S;
|
||||
};
|
||||
}
|
||||
|
||||
function createSequenceConverter(converter) {
|
||||
return function(V, opts = kEmptyObject) {
|
||||
if (type(V) !== 'Object') {
|
||||
throw makeException(
|
||||
'can not be converted to sequence.',
|
||||
opts);
|
||||
}
|
||||
const iter = V?.[SymbolIterator]?.();
|
||||
if (iter === undefined) {
|
||||
throw makeException(
|
||||
'can not be converted to sequence.',
|
||||
opts);
|
||||
}
|
||||
const array = [];
|
||||
while (true) {
|
||||
const res = iter?.next?.();
|
||||
if (res === undefined) {
|
||||
throw makeException(
|
||||
'can not be converted to sequence.',
|
||||
opts);
|
||||
}
|
||||
if (res.done === true) break;
|
||||
const val = converter(res.value, {
|
||||
__proto__: null,
|
||||
...opts,
|
||||
context: `${opts.context}, index ${array.length}`,
|
||||
});
|
||||
ArrayPrototypePush(array, val);
|
||||
}
|
||||
return array;
|
||||
};
|
||||
}
|
||||
|
||||
function createInterfaceConverter(name, prototype) {
|
||||
return (V, opts) => {
|
||||
if (!ObjectPrototypeIsPrototypeOf(prototype, V)) {
|
||||
throw makeException(
|
||||
`is not of type ${name}.`,
|
||||
opts);
|
||||
}
|
||||
return V;
|
||||
};
|
||||
}
|
||||
|
||||
converters.AlgorithmIdentifier = (V, opts) => {
|
||||
// Union for (object or DOMString)
|
||||
if (type(V) === 'Object') {
|
||||
return converters.object(V, opts);
|
||||
}
|
||||
return converters.DOMString(V, opts);
|
||||
};
|
||||
|
||||
converters.KeyFormat = createEnumConverter('KeyFormat', [
|
||||
'raw',
|
||||
'pkcs8',
|
||||
'spki',
|
||||
'jwk',
|
||||
]);
|
||||
|
||||
converters.KeyUsage = createEnumConverter('KeyUsage', [
|
||||
'encrypt',
|
||||
'decrypt',
|
||||
'sign',
|
||||
'verify',
|
||||
'deriveKey',
|
||||
'deriveBits',
|
||||
'wrapKey',
|
||||
'unwrapKey',
|
||||
]);
|
||||
|
||||
converters['sequence<KeyUsage>'] = createSequenceConverter(converters.KeyUsage);
|
||||
|
||||
converters.HashAlgorithmIdentifier = converters.AlgorithmIdentifier;
|
||||
|
||||
const dictAlgorithm = [
|
||||
{
|
||||
key: 'name',
|
||||
converter: converters.DOMString,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
converters.Algorithm = createDictionaryConverter(
|
||||
'Algorithm', dictAlgorithm);
|
||||
|
||||
converters.BigInteger = converters.Uint8Array;
|
||||
|
||||
const dictRsaKeyGenParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'modulusLength',
|
||||
converter: (V, opts) =>
|
||||
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'publicExponent',
|
||||
converter: converters.BigInteger,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
converters.RsaKeyGenParams = createDictionaryConverter(
|
||||
'RsaKeyGenParams', dictRsaKeyGenParams);
|
||||
|
||||
converters.RsaHashedKeyGenParams = createDictionaryConverter(
|
||||
'RsaHashedKeyGenParams', [
|
||||
...new SafeArrayIterator(dictRsaKeyGenParams),
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.RsaHashedImportParams = createDictionaryConverter(
|
||||
'RsaHashedImportParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.NamedCurve = converters.DOMString;
|
||||
|
||||
converters.EcKeyImportParams = createDictionaryConverter(
|
||||
'EcKeyImportParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'namedCurve',
|
||||
converter: converters.NamedCurve,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.EcKeyGenParams = createDictionaryConverter(
|
||||
'EcKeyGenParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'namedCurve',
|
||||
converter: converters.NamedCurve,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.AesKeyGenParams = createDictionaryConverter(
|
||||
'AesKeyGenParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'length',
|
||||
converter: (V, opts) =>
|
||||
converters['unsigned short'](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.HmacKeyGenParams = createDictionaryConverter(
|
||||
'HmacKeyGenParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'length',
|
||||
converter: (V, opts) =>
|
||||
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||
},
|
||||
]);
|
||||
|
||||
converters.RsaPssParams = createDictionaryConverter(
|
||||
'RsaPssParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'saltLength',
|
||||
converter: (V, opts) =>
|
||||
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.RsaOaepParams = createDictionaryConverter(
|
||||
'RsaOaepParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'label',
|
||||
converter: converters.BufferSource,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.EcdsaParams = createDictionaryConverter(
|
||||
'EcdsaParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.HmacImportParams = createDictionaryConverter(
|
||||
'HmacImportParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'length',
|
||||
converter: (V, opts) =>
|
||||
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||
},
|
||||
]);
|
||||
|
||||
const simpleDomStringKey = (key) => ({ key, converter: converters.DOMString });
|
||||
|
||||
converters.RsaOtherPrimesInfo = createDictionaryConverter(
|
||||
'RsaOtherPrimesInfo', [
|
||||
simpleDomStringKey('r'),
|
||||
simpleDomStringKey('d'),
|
||||
simpleDomStringKey('t'),
|
||||
]);
|
||||
converters['sequence<RsaOtherPrimesInfo>'] = createSequenceConverter(
|
||||
converters.RsaOtherPrimesInfo);
|
||||
|
||||
converters.JsonWebKey = createDictionaryConverter(
|
||||
'JsonWebKey', [
|
||||
simpleDomStringKey('kty'),
|
||||
simpleDomStringKey('use'),
|
||||
{
|
||||
key: 'key_ops',
|
||||
converter: converters['sequence<DOMString>'],
|
||||
},
|
||||
simpleDomStringKey('alg'),
|
||||
{
|
||||
key: 'ext',
|
||||
converter: converters.boolean,
|
||||
},
|
||||
simpleDomStringKey('crv'),
|
||||
simpleDomStringKey('x'),
|
||||
simpleDomStringKey('y'),
|
||||
simpleDomStringKey('d'),
|
||||
simpleDomStringKey('n'),
|
||||
simpleDomStringKey('e'),
|
||||
simpleDomStringKey('p'),
|
||||
simpleDomStringKey('q'),
|
||||
simpleDomStringKey('dp'),
|
||||
simpleDomStringKey('dq'),
|
||||
simpleDomStringKey('qi'),
|
||||
{
|
||||
key: 'oth',
|
||||
converter: converters['sequence<RsaOtherPrimesInfo>'],
|
||||
},
|
||||
simpleDomStringKey('k'),
|
||||
]);
|
||||
|
||||
converters.HkdfParams = createDictionaryConverter(
|
||||
'HkdfParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'salt',
|
||||
converter: converters.BufferSource,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'info',
|
||||
converter: converters.BufferSource,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.Pbkdf2Params = createDictionaryConverter(
|
||||
'Pbkdf2Params', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'iterations',
|
||||
converter: (V, opts) =>
|
||||
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'salt',
|
||||
converter: converters.BufferSource,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.AesDerivedKeyParams = createDictionaryConverter(
|
||||
'AesDerivedKeyParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'length',
|
||||
converter: (V, opts) =>
|
||||
converters['unsigned short'](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.AesCbcParams = createDictionaryConverter(
|
||||
'AesCbcParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'iv',
|
||||
converter: converters.BufferSource,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.AesGcmParams = createDictionaryConverter(
|
||||
'AesGcmParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'iv',
|
||||
converter: converters.BufferSource,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'tagLength',
|
||||
converter: (V, opts) =>
|
||||
converters.octet(V, { ...opts, enforceRange: true }),
|
||||
},
|
||||
{
|
||||
key: 'additionalData',
|
||||
converter: converters.BufferSource,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.AesCtrParams = createDictionaryConverter(
|
||||
'AesCtrParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'counter',
|
||||
converter: converters.BufferSource,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'length',
|
||||
converter: (V, opts) =>
|
||||
converters.octet(V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.CryptoKey = createInterfaceConverter(
|
||||
'CryptoKey', CryptoKey.prototype);
|
||||
|
||||
converters.EcdhKeyDeriveParams = createDictionaryConverter(
|
||||
'EcdhKeyDeriveParams', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'public',
|
||||
converter: converters.CryptoKey,
|
||||
required: true,
|
||||
},
|
||||
]);
|
||||
|
||||
converters.Ed448Params = createDictionaryConverter(
|
||||
'Ed448Params', [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: 'context',
|
||||
converter: converters.BufferSource,
|
||||
required: false,
|
||||
},
|
||||
]);
|
||||
|
||||
module.exports = {
|
||||
converters,
|
||||
requiredArguments,
|
||||
};
|
2
test/fixtures/crypto/aes_gcm.js
vendored
2
test/fixtures/crypto/aes_gcm.js
vendored
@ -115,7 +115,7 @@ module.exports = function() {
|
||||
|
||||
const failing = [];
|
||||
kKeyLengths.forEach((keyLength) => {
|
||||
[24, 48, 72, 95, 129, 256].forEach((badTagLength) => {
|
||||
[24, 48, 72, 95, 129].forEach((badTagLength) => {
|
||||
failing.push({
|
||||
keyBuffer: kKeyBytes[keyLength],
|
||||
algorithm: {
|
||||
|
@ -25,7 +25,7 @@ const sig = '13691a79fb55a0417e4d6699a32f91ad29283fa2c1439865cc0632931f4f48dc';
|
||||
async function doSig(key) {
|
||||
const signature = await subtle.sign({
|
||||
name: 'HMAC'
|
||||
}, key, 'some data');
|
||||
}, key, Buffer.from('some data'));
|
||||
assert.strictEqual(Buffer.from(signature).toString('hex'), sig);
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ async function prepareKeys() {
|
||||
{ name: 'X448' },
|
||||
keys.X448.privateKey,
|
||||
8 * keys.X448.size),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||
{ code: 'ERR_MISSING_OPTION' });
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -165,7 +165,7 @@ async function prepareKeys() {
|
||||
{ name: 'ECDH' },
|
||||
keys['P-384'].privateKey,
|
||||
8 * keys['P-384'].size),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||
{ code: 'ERR_MISSING_OPTION' });
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -296,7 +296,8 @@ async function testDeriveBitsBadHash(
|
||||
...algorithm,
|
||||
hash: hash.substring(0, 3) + hash.substring(4)
|
||||
}, baseKeys[size], 256), {
|
||||
message: /Unrecognized name/
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
assert.rejects(
|
||||
subtle.deriveBits(
|
||||
@ -305,7 +306,8 @@ async function testDeriveBitsBadHash(
|
||||
hash: 'PBKDF2'
|
||||
},
|
||||
baseKeys[size], 256), {
|
||||
message: /Unrecognized name/
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@ -343,7 +345,7 @@ async function testDeriveBitsMissingSalt(
|
||||
|
||||
return assert.rejects(
|
||||
subtle.deriveBits(algorithm, baseKeys[size], 0), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
}
|
||||
|
||||
@ -361,7 +363,7 @@ async function testDeriveBitsMissingInfo(
|
||||
|
||||
return assert.rejects(
|
||||
subtle.deriveBits(algorithm, baseKeys[size], 0), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
}
|
||||
|
||||
@ -435,7 +437,10 @@ async function testDeriveKeyBadHash(
|
||||
keyType,
|
||||
true,
|
||||
usages),
|
||||
{ message: /Unrecognized name/ }),
|
||||
{
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
assert.rejects(
|
||||
subtle.deriveKey(
|
||||
{
|
||||
@ -446,7 +451,10 @@ async function testDeriveKeyBadHash(
|
||||
keyType,
|
||||
true,
|
||||
usages),
|
||||
{ message: /Unrecognized name/ }),
|
||||
{
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ async function prepareKeys() {
|
||||
{ name: 'X448' },
|
||||
keys.X448.privateKey,
|
||||
...otherArgs),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||
{ code: 'ERR_MISSING_OPTION' });
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -127,7 +127,7 @@ async function prepareKeys() {
|
||||
{ name: 'ECDH' },
|
||||
keys['P-384'].privateKey,
|
||||
...otherArgs),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||
{ code: 'ERR_MISSING_OPTION' });
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -66,11 +66,17 @@ const kData = (new TextEncoder()).encode('hello');
|
||||
}));
|
||||
})().then(common.mustCall());
|
||||
|
||||
Promise.all([1, [], {}, null, undefined].map((i) =>
|
||||
assert.rejects(subtle.digest(i), { message: /Unrecognized name/ })
|
||||
Promise.all([1, null, undefined].map((i) =>
|
||||
assert.rejects(subtle.digest(i, Buffer.alloc(0)), {
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
})
|
||||
)).then(common.mustCall());
|
||||
|
||||
assert.rejects(subtle.digest(''), { message: /Unrecognized name/ }).then(common.mustCall());
|
||||
assert.rejects(subtle.digest('', Buffer.alloc(0)), {
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}).then(common.mustCall());
|
||||
|
||||
Promise.all([1, [], {}, null, undefined].map((i) =>
|
||||
assert.rejects(subtle.digest('SHA-256', i), {
|
||||
@ -78,14 +84,6 @@ Promise.all([1, [], {}, null, undefined].map((i) =>
|
||||
})
|
||||
)).then(common.mustCall());
|
||||
|
||||
// If there is a mismatch between length and the expected digest size for
|
||||
// the selected algorithm, we fail. The length is a Node.js specific
|
||||
// addition to the API, and is added as a support for future additional
|
||||
// hash algorithms that support variable digest output lengths.
|
||||
assert.rejects(subtle.digest({ name: 'SHA-512', length: 510 }, kData), {
|
||||
name: 'OperationError',
|
||||
}).then(common.mustCall());
|
||||
|
||||
const kSourceData = {
|
||||
empty: '',
|
||||
short: '156eea7cc14c56cb94db030a4a9d95ff',
|
||||
|
@ -260,6 +260,38 @@ async function testImportJwk({ name, publicUsages, privateUsages }, extractable)
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const invalidUse = name.startsWith('X') ? 'sig' : 'enc';
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ ...jwk, use: invalidUse },
|
||||
{ name },
|
||||
extractable,
|
||||
privateUsages),
|
||||
{ message: 'Invalid JWK "use" Parameter' });
|
||||
}
|
||||
|
||||
if (name.startsWith('Ed')) {
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ kty: jwk.kty, x: jwk.x, crv: jwk.crv, alg: 'foo' },
|
||||
{ name },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: 'JWK "alg" does not match the requested algorithm' });
|
||||
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ ...jwk, alg: 'foo' },
|
||||
{ name },
|
||||
extractable,
|
||||
privateUsages),
|
||||
{ message: 'JWK "alg" does not match the requested algorithm' });
|
||||
}
|
||||
|
||||
for (const crv of [undefined, name === 'Ed25519' ? 'Ed448' : 'Ed25519']) {
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
@ -268,16 +300,16 @@ async function testImportJwk({ name, publicUsages, privateUsages }, extractable)
|
||||
{ name },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: /Subtype mismatch/ });
|
||||
{ message: 'JWK "crv" Parameter and algorithm name mismatch' });
|
||||
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ kty: jwk.kty, d: jwk.d, x: jwk.x, y: jwk.y, crv },
|
||||
{ ...jwk, crv },
|
||||
{ name },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: /Subtype mismatch/ });
|
||||
{ message: 'JWK "crv" Parameter and algorithm name mismatch' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,6 +261,38 @@ async function testImportJwk(
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const invalidUse = name === 'ECDH' ? 'sig' : 'enc';
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ ...jwk, use: invalidUse },
|
||||
{ name, namedCurve },
|
||||
extractable,
|
||||
privateUsages),
|
||||
{ message: 'Invalid JWK "use" Parameter' });
|
||||
}
|
||||
|
||||
if (name === 'ECDSA') {
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ kty: jwk.kty, x: jwk.x, y: jwk.y, crv: jwk.crv, alg: jwk.crv === 'P-256' ? 'ES384' : 'ES256' },
|
||||
{ name, namedCurve },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: 'JWK "alg" does not match the requested algorithm' });
|
||||
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ ...jwk, alg: jwk.crv === 'P-256' ? 'ES384' : 'ES256' },
|
||||
{ name, namedCurve },
|
||||
extractable,
|
||||
privateUsages),
|
||||
{ message: 'JWK "alg" does not match the requested algorithm' });
|
||||
}
|
||||
|
||||
for (const crv of [undefined, namedCurve === 'P-256' ? 'P-384' : 'P-256']) {
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
@ -269,16 +301,16 @@ async function testImportJwk(
|
||||
{ name, namedCurve },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: /Named curve mismatch/ });
|
||||
{ message: 'JWK "crv" does not match the requested algorithm' });
|
||||
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ kty: jwk.kty, d: jwk.d, x: jwk.x, y: jwk.y, crv },
|
||||
{ ...jwk, crv },
|
||||
{ name, namedCurve },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: /Named curve mismatch/ });
|
||||
privateUsages),
|
||||
{ message: 'JWK "crv" does not match the requested algorithm' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,6 +444,57 @@ async function testImportJwk(
|
||||
message: /key is not extractable/
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const invalidUse = name === 'RSA-OAEP' ? 'sig' : 'enc';
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ kty: jwk.kty, n: jwk.n, e: jwk.e, use: invalidUse },
|
||||
{ name, hash },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: 'Invalid JWK "use" Parameter' });
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ ...jwk, use: invalidUse },
|
||||
{ name, hash },
|
||||
extractable,
|
||||
privateUsages),
|
||||
{ message: 'Invalid JWK "use" Parameter' });
|
||||
}
|
||||
|
||||
{
|
||||
let invalidAlg = name === 'RSA-OAEP' ? name : name === 'RSA-PSS' ? 'PS' : 'RS';
|
||||
switch (name) {
|
||||
case 'RSA-OAEP':
|
||||
if (hash === 'SHA-1')
|
||||
invalidAlg += '-256';
|
||||
break;
|
||||
default:
|
||||
if (hash === 'SHA-256')
|
||||
invalidAlg += '384';
|
||||
else
|
||||
invalidAlg += '256';
|
||||
}
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ kty: jwk.kty, n: jwk.n, e: jwk.e, alg: invalidAlg },
|
||||
{ name, hash },
|
||||
extractable,
|
||||
publicUsages),
|
||||
{ message: 'JWK "alg" does not match the requested algorithm' });
|
||||
await assert.rejects(
|
||||
subtle.importKey(
|
||||
'jwk',
|
||||
{ ...jwk, alg: invalidAlg },
|
||||
{ name, hash },
|
||||
extractable,
|
||||
privateUsages),
|
||||
{ message: 'JWK "alg" does not match the requested algorithm' });
|
||||
}
|
||||
}
|
||||
|
||||
// combinations to test
|
||||
|
@ -14,19 +14,17 @@ const { subtle } = globalThis.crypto;
|
||||
await Promise.all([1, null, undefined, {}, []].map((format) =>
|
||||
assert.rejects(
|
||||
subtle.importKey(format, keyData, {}, false, ['wrapKey']), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
})
|
||||
));
|
||||
await assert.rejects(
|
||||
subtle.importKey('not valid', keyData, {}, false, ['wrapKey']), {
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
});
|
||||
await Promise.all([1, null, undefined, {}, []].map((keyData) =>
|
||||
assert.rejects(
|
||||
subtle.importKey('raw', keyData, {}, false, ['deriveBits']), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
})
|
||||
));
|
||||
await assert.rejects(
|
||||
subtle.importKey('raw', 1, {}, false, ['deriveBits']), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.importKey('raw', keyData, {
|
||||
name: 'HMAC'
|
||||
@ -65,7 +63,7 @@ const { subtle } = globalThis.crypto;
|
||||
hash: 'SHA-256',
|
||||
}, false, ['sign', 'verify']), {
|
||||
name: 'DataError',
|
||||
message: 'Invalid JWK keyData'
|
||||
message: 'Invalid keyData'
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -163,8 +163,9 @@ const vectors = {
|
||||
return assert.rejects(
|
||||
// The extractable and usages values are invalid here also,
|
||||
// but the unrecognized algorithm name should be caught first.
|
||||
subtle.generateKey(algorithm, 7, ['zebra']), {
|
||||
message: /Unrecognized name/
|
||||
subtle.generateKey(algorithm, 7, []), {
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
});
|
||||
}
|
||||
|
||||
@ -299,12 +300,12 @@ const vectors = {
|
||||
// Missing parameters
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name, publicExponent, hash }, true, usages), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name, modulusLength, hash }, true, usages), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
@ -312,7 +313,7 @@ const vectors = {
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
|
||||
await Promise.all(['', true, {}].map((modulusLength) => {
|
||||
await Promise.all([{}].map((modulusLength) => {
|
||||
return assert.rejects(subtle.generateKey({
|
||||
name,
|
||||
modulusLength,
|
||||
@ -338,25 +339,15 @@ const vectors = {
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||
}));
|
||||
|
||||
await Promise.all([true, {}, 1, []].map((hash) => {
|
||||
await Promise.all([true, 1].map((hash) => {
|
||||
return assert.rejects(subtle.generateKey({
|
||||
name,
|
||||
modulusLength,
|
||||
publicExponent,
|
||||
hash
|
||||
}, true, usages), {
|
||||
message: /Unrecognized name/
|
||||
});
|
||||
}));
|
||||
|
||||
await Promise.all(['', {}, 1, []].map((extractable) => {
|
||||
return assert.rejects(subtle.generateKey({
|
||||
name,
|
||||
modulusLength,
|
||||
publicExponent,
|
||||
hash
|
||||
}, extractable, usages), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
});
|
||||
}));
|
||||
|
||||
@ -449,12 +440,17 @@ const vectors = {
|
||||
assert.strictEqual(privateKey.algorithm.namedCurve, namedCurve);
|
||||
|
||||
// Invalid parameters
|
||||
[1, true, {}, [], undefined, null].forEach(async (namedCurve) => {
|
||||
[1, true, {}, [], null].forEach(async (namedCurve) => {
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name, namedCurve }, true, privateUsages), {
|
||||
name: 'NotSupportedError'
|
||||
});
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name, namedCurve: undefined }, true, privateUsages), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
}
|
||||
|
||||
const kTests = [
|
||||
@ -509,19 +505,18 @@ const vectors = {
|
||||
assert.strictEqual(key.algorithm.length, length);
|
||||
|
||||
// Invalid parameters
|
||||
[1, 100, 257].forEach(async (length) => {
|
||||
[1, 100, 257, '', false, null].forEach(async (length) => {
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name, length }, true, usages), {
|
||||
name: 'OperationError'
|
||||
});
|
||||
});
|
||||
|
||||
['', {}, [], false, null, undefined].forEach(async (length) => {
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name, length }, true, usages), {
|
||||
name: 'OperationError',
|
||||
});
|
||||
});
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name, length: undefined }, true, usages), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION'
|
||||
});
|
||||
}
|
||||
|
||||
const kTests = [
|
||||
@ -568,17 +563,11 @@ const vectors = {
|
||||
assert.strictEqual(key.algorithm.length, length);
|
||||
assert.strictEqual(key.algorithm.hash.name, hash);
|
||||
|
||||
['', {}, [], false, null].forEach(async (length) => {
|
||||
[1, false, null].forEach(async (hash) => {
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name: 'HMAC', length, hash }, true, usages), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
[1, {}, [], false, null].forEach(async (hash) => {
|
||||
await assert.rejects(
|
||||
subtle.generateKey({ name: 'HMAC', length, hash }, true, usages), {
|
||||
message: /Unrecognized name/
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -135,7 +135,8 @@ async function testVerify({ name,
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name, hash: 'sha256' }, publicKey, signature, copy), {
|
||||
message: /Unrecognized name/
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -91,16 +91,14 @@ async function testVerify({ hash,
|
||||
// Test failure when wrong hash is used
|
||||
{
|
||||
const otherhash = hash === 'SHA-1' ? 'SHA-256' : 'SHA-1';
|
||||
assert(!(await subtle.verify({
|
||||
name,
|
||||
hash: otherhash
|
||||
}, key, signature, copy)));
|
||||
const keyWithOtherHash = await subtle.importKey(
|
||||
'raw',
|
||||
keyBuffer,
|
||||
{ name, hash: otherhash },
|
||||
false,
|
||||
['verify']);
|
||||
assert(!(await subtle.verify({ name }, keyWithOtherHash, signature, plaintext)));
|
||||
}
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify({ name, hash: 'sha256' }, key, signature, copy), {
|
||||
message: /Unrecognized name/
|
||||
});
|
||||
}
|
||||
|
||||
async function testSign({ hash,
|
||||
@ -156,7 +154,6 @@ async function testSign({ hash,
|
||||
subtle.generateKey({ name }, false, ['sign', 'verify']), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: 'algorithm.hash is required'
|
||||
});
|
||||
|
||||
// Test failure when no sign usage
|
||||
|
@ -112,19 +112,14 @@ async function testVerify({
|
||||
// Test failure when wrong hash is used
|
||||
{
|
||||
const otherhash = hash === 'SHA-1' ? 'SHA-256' : 'SHA-1';
|
||||
assert(!(await subtle.verify({
|
||||
...algorithm,
|
||||
hash: otherhash
|
||||
}, publicKey, signature, copy)));
|
||||
const keyWithOtherHash = await subtle.importKey(
|
||||
'spki',
|
||||
publicKeyBuffer,
|
||||
{ name: algorithm.name, hash: otherhash },
|
||||
false,
|
||||
['verify']);
|
||||
assert(!(await subtle.verify(algorithm, keyWithOtherHash, signature, plaintext)));
|
||||
}
|
||||
|
||||
await assert.rejects(
|
||||
subtle.verify(
|
||||
{ ...algorithm, hash: 'sha256' },
|
||||
publicKey,
|
||||
signature,
|
||||
copy),
|
||||
{ message: /Unrecognized name/ });
|
||||
}
|
||||
|
||||
async function testSign({
|
||||
|
@ -11,15 +11,9 @@ const {
|
||||
normalizeAlgorithm,
|
||||
} = require('internal/crypto/util');
|
||||
|
||||
{
|
||||
// Check that normalizeAlgorithm does not add an undefined hash property.
|
||||
assert.strictEqual('hash' in normalizeAlgorithm({ name: 'ECDH' }), false);
|
||||
assert.strictEqual('hash' in normalizeAlgorithm('ECDH'), false);
|
||||
}
|
||||
|
||||
{
|
||||
// Check that normalizeAlgorithm does not mutate object inputs.
|
||||
const algorithm = { name: 'ECDH', hash: 'SHA-256' };
|
||||
assert.strictEqual(normalizeAlgorithm(algorithm) !== algorithm, true);
|
||||
assert.deepStrictEqual(algorithm, { name: 'ECDH', hash: 'SHA-256' });
|
||||
const algorithm = { name: 'ECDSA', hash: 'SHA-256' };
|
||||
assert.strictEqual(normalizeAlgorithm(algorithm, 'sign') !== algorithm, true);
|
||||
assert.deepStrictEqual(algorithm, { name: 'ECDSA', hash: 'SHA-256' });
|
||||
}
|
||||
|
518
test/parallel/test-webcrypto-webidl.js
Normal file
518
test/parallel/test-webcrypto-webidl.js
Normal file
@ -0,0 +1,518 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const webidl = require('internal/crypto/webidl');
|
||||
const { subtle } = globalThis.crypto;
|
||||
const { generateKeySync } = require('crypto');
|
||||
|
||||
const { converters } = webidl;
|
||||
const prefix = "Failed to execute 'fn' on 'interface'";
|
||||
const context = '1st argument';
|
||||
const opts = { prefix, context };
|
||||
|
||||
// Required arguments.length
|
||||
{
|
||||
assert.throws(() => webidl.requiredArguments(0, 3, { prefix }), {
|
||||
code: 'ERR_MISSING_ARGS',
|
||||
name: 'TypeError',
|
||||
message: `${prefix}: 3 arguments required, but only 0 present.`
|
||||
});
|
||||
|
||||
assert.throws(() => webidl.requiredArguments(0, 1, { prefix }), {
|
||||
code: 'ERR_MISSING_ARGS',
|
||||
name: 'TypeError',
|
||||
message: `${prefix}: 1 argument required, but only 0 present.`
|
||||
});
|
||||
|
||||
// Does not throw when extra are added
|
||||
webidl.requiredArguments(4, 3, { prefix });
|
||||
}
|
||||
|
||||
// boolean
|
||||
{
|
||||
assert.strictEqual(converters.boolean(0), false);
|
||||
assert.strictEqual(converters.boolean(NaN), false);
|
||||
assert.strictEqual(converters.boolean(undefined), false);
|
||||
assert.strictEqual(converters.boolean(null), false);
|
||||
assert.strictEqual(converters.boolean(false), false);
|
||||
assert.strictEqual(converters.boolean(''), false);
|
||||
|
||||
assert.strictEqual(converters.boolean(1), true);
|
||||
assert.strictEqual(converters.boolean(Number.POSITIVE_INFINITY), true);
|
||||
assert.strictEqual(converters.boolean(Number.NEGATIVE_INFINITY), true);
|
||||
assert.strictEqual(converters.boolean('1'), true);
|
||||
assert.strictEqual(converters.boolean('0'), true);
|
||||
assert.strictEqual(converters.boolean('false'), true);
|
||||
assert.strictEqual(converters.boolean(function() {}), true);
|
||||
assert.strictEqual(converters.boolean(Symbol()), true);
|
||||
assert.strictEqual(converters.boolean([]), true);
|
||||
assert.strictEqual(converters.boolean({}), true);
|
||||
}
|
||||
|
||||
// int conversion
|
||||
// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
|
||||
{
|
||||
for (const [converter, max] of [
|
||||
[converters.octet, Math.pow(2, 8) - 1],
|
||||
[converters['unsigned short'], Math.pow(2, 16) - 1],
|
||||
[converters['unsigned long'], Math.pow(2, 32) - 1],
|
||||
]) {
|
||||
assert.strictEqual(converter(0), 0);
|
||||
assert.strictEqual(converter(max), max);
|
||||
assert.strictEqual(converter('' + 0), 0);
|
||||
assert.strictEqual(converter('' + max), max);
|
||||
assert.strictEqual(converter(3), 3);
|
||||
assert.strictEqual(converter('' + 3), 3);
|
||||
assert.strictEqual(converter(3.1), 3);
|
||||
assert.strictEqual(converter(3.7), 3);
|
||||
|
||||
assert.strictEqual(converter(max + 1), 0);
|
||||
assert.strictEqual(converter(max + 2), 1);
|
||||
assert.throws(() => converter(max + 1, { ...opts, enforceRange: true }), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message: `${prefix}: ${context} is outside the expected range of 0 to ${max}.`,
|
||||
});
|
||||
|
||||
assert.strictEqual(converter({}), 0);
|
||||
assert.strictEqual(converter(NaN), 0);
|
||||
assert.strictEqual(converter(false), 0);
|
||||
assert.strictEqual(converter(true), 1);
|
||||
assert.strictEqual(converter('1'), 1);
|
||||
assert.strictEqual(converter('0'), 0);
|
||||
assert.strictEqual(converter('{}'), 0);
|
||||
assert.strictEqual(converter({}), 0);
|
||||
assert.strictEqual(converter([]), 0);
|
||||
assert.strictEqual(converter(function() {}), 0);
|
||||
|
||||
assert.throws(() => converter(Symbol(), opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is a Symbol and cannot be converted to a number.`
|
||||
});
|
||||
assert.throws(() => converter(0n, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is a BigInt and cannot be converted to a number.`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// DOMString
|
||||
{
|
||||
assert.strictEqual(converters.DOMString(1), '1');
|
||||
assert.strictEqual(converters.DOMString(1n), '1');
|
||||
assert.strictEqual(converters.DOMString(false), 'false');
|
||||
assert.strictEqual(converters.DOMString(true), 'true');
|
||||
assert.strictEqual(converters.DOMString(undefined), 'undefined');
|
||||
assert.strictEqual(converters.DOMString(NaN), 'NaN');
|
||||
assert.strictEqual(converters.DOMString({}), '[object Object]');
|
||||
assert.strictEqual(converters.DOMString({ foo: 'bar' }), '[object Object]');
|
||||
assert.strictEqual(converters.DOMString([]), '');
|
||||
assert.strictEqual(converters.DOMString([1, 2]), '1,2');
|
||||
|
||||
assert.throws(() => converters.DOMString(Symbol(), opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is a Symbol and cannot be converted to a string.`
|
||||
});
|
||||
}
|
||||
|
||||
// object
|
||||
{
|
||||
for (const good of [{}, [], new Array(), function() {}]) {
|
||||
assert.deepStrictEqual(converters.object(good), good);
|
||||
}
|
||||
|
||||
for (const bad of [undefined, null, NaN, false, true, 0, 1, '', 'foo', Symbol(), 9n]) {
|
||||
assert.throws(() => converters.object(bad, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is not an object.`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Uint8Array
|
||||
{
|
||||
for (const good of [Buffer.alloc(0), new Uint8Array()]) {
|
||||
assert.deepStrictEqual(converters.Uint8Array(good), good);
|
||||
}
|
||||
|
||||
for (const bad of [new ArrayBuffer(), new SharedArrayBuffer(), [], null, 'foo', undefined, true]) {
|
||||
assert.throws(() => converters.Uint8Array(bad, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is not an Uint8Array object.`
|
||||
});
|
||||
}
|
||||
|
||||
assert.throws(() => converters.Uint8Array(new Uint8Array(new SharedArrayBuffer()), opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is a view on a SharedArrayBuffer, which is not allowed.`
|
||||
});
|
||||
}
|
||||
|
||||
// BufferSource
|
||||
{
|
||||
for (const good of [
|
||||
Buffer.alloc(0),
|
||||
new Uint8Array(),
|
||||
new ArrayBuffer(),
|
||||
new DataView(new ArrayBuffer()),
|
||||
new BigInt64Array(),
|
||||
new BigUint64Array(),
|
||||
new Float32Array(),
|
||||
new Float64Array(),
|
||||
new Int8Array(),
|
||||
new Int16Array(),
|
||||
new Int32Array(),
|
||||
new Uint8ClampedArray(),
|
||||
new Uint16Array(),
|
||||
new Uint32Array(),
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.BufferSource(good), good);
|
||||
}
|
||||
|
||||
for (const bad of [new SharedArrayBuffer(), [], null, 'foo', undefined, true]) {
|
||||
assert.throws(() => converters.BufferSource(bad, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is not instance of ArrayBuffer, Buffer, TypedArray, or DataView.`
|
||||
});
|
||||
}
|
||||
|
||||
assert.throws(() => converters.BufferSource(new Uint8Array(new SharedArrayBuffer()), opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is a view on a SharedArrayBuffer, which is not allowed.`
|
||||
});
|
||||
}
|
||||
|
||||
// CryptoKey
|
||||
{
|
||||
|
||||
subtle.generateKey({ name: 'AES-CBC', length: 128 }, false, ['encrypt']).then((key) => {
|
||||
assert.deepStrictEqual(converters.CryptoKey(key), key);
|
||||
}).then(common.mustCall());
|
||||
|
||||
for (const bad of [
|
||||
generateKeySync('aes', { length: 128 }),
|
||||
undefined, null, 1, {}, Symbol(), true, false, [],
|
||||
]) {
|
||||
assert.throws(() => converters.CryptoKey(bad, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `${prefix}: ${context} is not of type CryptoKey.`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// AlgorithmIdentifier (Union for (object or DOMString))
|
||||
{
|
||||
assert.strictEqual(converters.AlgorithmIdentifier('foo'), 'foo');
|
||||
assert.deepStrictEqual(converters.AlgorithmIdentifier({ name: 'foo' }), { name: 'foo' });
|
||||
}
|
||||
|
||||
// JsonWebKey
|
||||
{
|
||||
for (const good of [
|
||||
{},
|
||||
{ use: 'sig' },
|
||||
{ key_ops: ['sign'] },
|
||||
{ ext: true },
|
||||
{ oth: [] },
|
||||
{ oth: [{ r: '', d: '', t: '' }] },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.JsonWebKey(good), good);
|
||||
assert.deepStrictEqual(converters.JsonWebKey({ ...good, filtered: 'out' }), good);
|
||||
}
|
||||
}
|
||||
|
||||
// KeyFormat
|
||||
{
|
||||
for (const good of ['jwk', 'spki', 'pkcs8', 'raw']) {
|
||||
assert.strictEqual(converters.KeyFormat(good), good);
|
||||
}
|
||||
|
||||
for (const bad of ['foo', 1, false]) {
|
||||
assert.throws(() => converters.KeyFormat(bad, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: `${prefix}: ${context} value '${bad}' is not a valid enum value of type KeyFormat.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// KeyUsage
|
||||
{
|
||||
for (const good of [
|
||||
'encrypt',
|
||||
'decrypt',
|
||||
'sign',
|
||||
'verify',
|
||||
'deriveKey',
|
||||
'deriveBits',
|
||||
'wrapKey',
|
||||
'unwrapKey',
|
||||
]) {
|
||||
assert.strictEqual(converters.KeyUsage(good), good);
|
||||
}
|
||||
|
||||
for (const bad of ['foo', 1, false]) {
|
||||
assert.throws(() => converters.KeyUsage(bad, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: `${prefix}: ${context} value '${bad}' is not a valid enum value of type KeyUsage.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithm
|
||||
{
|
||||
const good = { name: 'RSA-PSS' };
|
||||
assert.deepStrictEqual(converters.Algorithm({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
assert.throws(() => converters.Algorithm({}, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'Algorithm' because 'name' is required in 'Algorithm'.`,
|
||||
});
|
||||
}
|
||||
|
||||
// RsaHashedKeyGenParams
|
||||
{
|
||||
for (const good of [
|
||||
{
|
||||
name: 'RSA-OAEP',
|
||||
hash: { name: 'SHA-1' },
|
||||
modulusLength: 2048,
|
||||
publicExponent: new Uint8Array([1, 0, 1]),
|
||||
},
|
||||
{
|
||||
name: 'RSA-OAEP',
|
||||
hash: 'SHA-1',
|
||||
modulusLength: 2048,
|
||||
publicExponent: new Uint8Array([1, 0, 1]),
|
||||
},
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.RsaHashedKeyGenParams({ ...good, filtered: 'out' }, opts), good);
|
||||
for (const required of ['hash', 'publicExponent', 'modulusLength']) {
|
||||
assert.throws(() => converters.RsaHashedKeyGenParams({ ...good, [required]: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'RsaHashedKeyGenParams' because '${required}' is required in 'RsaHashedKeyGenParams'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RsaHashedImportParams
|
||||
{
|
||||
for (const good of [
|
||||
{ name: 'RSA-OAEP', hash: { name: 'SHA-1' } },
|
||||
{ name: 'RSA-OAEP', hash: 'SHA-1' },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.RsaHashedImportParams({ ...good, filtered: 'out' }, opts), good);
|
||||
assert.throws(() => converters.RsaHashedImportParams({ ...good, hash: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'RsaHashedImportParams' because 'hash' is required in 'RsaHashedImportParams'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// RsaPssParams
|
||||
{
|
||||
const good = { name: 'RSA-PSS', saltLength: 20 };
|
||||
assert.deepStrictEqual(converters.RsaPssParams({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
assert.throws(() => converters.RsaPssParams({ ...good, saltLength: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'RsaPssParams' because 'saltLength' is required in 'RsaPssParams'.`,
|
||||
});
|
||||
}
|
||||
|
||||
// RsaOaepParams
|
||||
{
|
||||
for (const good of [{ name: 'RSA-OAEP' }, { name: 'RSA-OAEP', label: Buffer.alloc(0) }]) {
|
||||
assert.deepStrictEqual(converters.RsaOaepParams({ ...good, filtered: 'out' }, opts), good);
|
||||
}
|
||||
}
|
||||
|
||||
// EcKeyImportParams, EcKeyGenParams
|
||||
{
|
||||
for (const name of ['EcKeyImportParams', 'EcKeyGenParams']) {
|
||||
const { [name]: converter } = converters;
|
||||
|
||||
const good = { name: 'ECDSA', namedCurve: 'P-256' };
|
||||
assert.deepStrictEqual(converter({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
assert.throws(() => converter({ ...good, namedCurve: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to '${name}' because 'namedCurve' is required in '${name}'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// EcdsaParams
|
||||
{
|
||||
for (const good of [
|
||||
{ name: 'ECDSA', hash: { name: 'SHA-1' } },
|
||||
{ name: 'ECDSA', hash: 'SHA-1' },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.EcdsaParams({ ...good, filtered: 'out' }, opts), good);
|
||||
assert.throws(() => converters.EcdsaParams({ ...good, hash: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'EcdsaParams' because 'hash' is required in 'EcdsaParams'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// HmacKeyGenParams, HmacImportParams
|
||||
{
|
||||
for (const name of ['HmacKeyGenParams', 'HmacImportParams']) {
|
||||
const { [name]: converter } = converters;
|
||||
|
||||
for (const good of [
|
||||
{ name: 'HMAC', hash: { name: 'SHA-1' } },
|
||||
{ name: 'HMAC', hash: { name: 'SHA-1' }, length: 20 },
|
||||
{ name: 'HMAC', hash: 'SHA-1' },
|
||||
{ name: 'HMAC', hash: 'SHA-1', length: 20 },
|
||||
]) {
|
||||
assert.deepStrictEqual(converter({ ...good, filtered: 'out' }, opts), good);
|
||||
assert.throws(() => converter({ ...good, hash: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to '${name}' because 'hash' is required in '${name}'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AesKeyGenParams, AesDerivedKeyParams
|
||||
{
|
||||
for (const name of ['AesKeyGenParams', 'AesDerivedKeyParams']) {
|
||||
const { [name]: converter } = converters;
|
||||
|
||||
const good = { name: 'AES-CBC', length: 128 };
|
||||
assert.deepStrictEqual(converter({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
assert.throws(() => converter({ ...good, length: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to '${name}' because 'length' is required in '${name}'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// HkdfParams
|
||||
{
|
||||
for (const good of [
|
||||
{ name: 'HKDF', hash: { name: 'SHA-1' }, salt: Buffer.alloc(0), info: Buffer.alloc(0) },
|
||||
{ name: 'HKDF', hash: 'SHA-1', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.HkdfParams({ ...good, filtered: 'out' }, opts), good);
|
||||
for (const required of ['hash', 'salt', 'info']) {
|
||||
assert.throws(() => converters.HkdfParams({ ...good, [required]: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'HkdfParams' because '${required}' is required in 'HkdfParams'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pbkdf2Params
|
||||
{
|
||||
for (const good of [
|
||||
{ name: 'PBKDF2', hash: { name: 'SHA-1' }, iterations: 5, salt: Buffer.alloc(0) },
|
||||
{ name: 'PBKDF2', hash: 'SHA-1', iterations: 5, salt: Buffer.alloc(0) },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.Pbkdf2Params({ ...good, filtered: 'out' }, opts), good);
|
||||
for (const required of ['hash', 'iterations', 'salt']) {
|
||||
assert.throws(() => converters.Pbkdf2Params({ ...good, [required]: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'Pbkdf2Params' because '${required}' is required in 'Pbkdf2Params'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AesCbcParams
|
||||
{
|
||||
const good = { name: 'AES-CBC', iv: Buffer.alloc(0) };
|
||||
assert.deepStrictEqual(converters.AesCbcParams({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
assert.throws(() => converters.AesCbcParams({ ...good, iv: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'AesCbcParams' because 'iv' is required in 'AesCbcParams'.`,
|
||||
});
|
||||
}
|
||||
|
||||
// AesGcmParams
|
||||
{
|
||||
for (const good of [
|
||||
{ name: 'AES-GCM', iv: Buffer.alloc(0) },
|
||||
{ name: 'AES-GCM', iv: Buffer.alloc(0), tagLength: 16 },
|
||||
{ name: 'AES-GCM', iv: Buffer.alloc(0), tagLength: 16, additionalData: Buffer.alloc(0) },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.AesGcmParams({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
assert.throws(() => converters.AesGcmParams({ ...good, iv: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'AesGcmParams' because 'iv' is required in 'AesGcmParams'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// AesCtrParams
|
||||
{
|
||||
const good = { name: 'AES-CTR', counter: Buffer.alloc(0), length: 20 };
|
||||
assert.deepStrictEqual(converters.AesCtrParams({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
for (const required of ['counter', 'length']) {
|
||||
assert.throws(() => converters.AesCtrParams({ ...good, [required]: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'AesCtrParams' because '${required}' is required in 'AesCtrParams'.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// EcdhKeyDeriveParams
|
||||
{
|
||||
subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits']).then((kp) => {
|
||||
const good = { name: 'ECDH', public: kp.publicKey };
|
||||
assert.deepStrictEqual(converters.EcdhKeyDeriveParams({ ...good, filtered: 'out' }, opts), good);
|
||||
|
||||
assert.throws(() => converters.EcdhKeyDeriveParams({ ...good, public: undefined }, opts), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_MISSING_OPTION',
|
||||
message: `${prefix}: ${context} can not be converted to 'EcdhKeyDeriveParams' because 'public' is required in 'EcdhKeyDeriveParams'.`,
|
||||
});
|
||||
}).then(common.mustCall());
|
||||
}
|
||||
|
||||
// Ed448Params
|
||||
{
|
||||
for (const good of [
|
||||
{ name: 'Ed448', context: new Uint8Array() },
|
||||
{ name: 'Ed448' },
|
||||
]) {
|
||||
assert.deepStrictEqual(converters.Ed448Params({ ...good, filtered: 'out' }, opts), good);
|
||||
}
|
||||
}
|
@ -382,7 +382,7 @@ async function setupBaseKeys() {
|
||||
promises.push(
|
||||
subtle.importKey(
|
||||
'raw',
|
||||
kPasswords[size],
|
||||
Buffer.from(kPasswords[size], 'hex'),
|
||||
{ name: 'PBKDF2' },
|
||||
false,
|
||||
['deriveBits'])
|
||||
@ -391,7 +391,7 @@ async function setupBaseKeys() {
|
||||
promises.push(
|
||||
subtle.importKey(
|
||||
'raw',
|
||||
kPasswords[size],
|
||||
Buffer.from(kPasswords[size], 'hex'),
|
||||
{ name: 'PBKDF2' },
|
||||
false,
|
||||
['deriveKey'])
|
||||
@ -474,7 +474,7 @@ async function testDeriveBitsBadHash(
|
||||
hash,
|
||||
iterations) {
|
||||
const salt = Buffer.from(kSalts[saltSize], 'hex');
|
||||
const algorithm = { name: 'HKDF', salt, iterations };
|
||||
const algorithm = { name: 'PBKDF2', salt, iterations };
|
||||
|
||||
return Promise.all([
|
||||
assert.rejects(
|
||||
@ -483,7 +483,8 @@ async function testDeriveBitsBadHash(
|
||||
...algorithm,
|
||||
hash: hash.substring(0, 3) + hash.substring(4)
|
||||
}, baseKeys[size], 256), {
|
||||
message: /Unrecognized name/
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
assert.rejects(
|
||||
subtle.deriveBits(
|
||||
@ -492,7 +493,8 @@ async function testDeriveBitsBadHash(
|
||||
hash: 'HKDF'
|
||||
},
|
||||
baseKeys[size], 256), {
|
||||
message: /Unrecognized name/
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@ -569,7 +571,10 @@ async function testDeriveKeyBadHash(
|
||||
keyType,
|
||||
true,
|
||||
usages),
|
||||
{ message: /Unrecognized name/ }),
|
||||
{
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
assert.rejects(
|
||||
subtle.deriveKey(
|
||||
{
|
||||
@ -580,7 +585,10 @@ async function testDeriveKeyBadHash(
|
||||
keyType,
|
||||
true,
|
||||
usages),
|
||||
{ message: /Unrecognized name/ }),
|
||||
{
|
||||
message: /Unrecognized algorithm name/,
|
||||
name: 'NotSupportedError',
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,17 @@
|
||||
{
|
||||
"encrypt_decrypt/aes_gcm.https.any.js": {
|
||||
"fail": {
|
||||
"note": "We're throwing correct error for the WebIDL definition, WPT needs update: https://github.com/web-platform-tests/wpt/pull/37734",
|
||||
"expected": [
|
||||
"AES-GCM 128-bit key, illegal tag length 256-bits",
|
||||
"AES-GCM 192-bit key, illegal tag length 256-bits",
|
||||
"AES-GCM 256-bit key, illegal tag length 256-bits",
|
||||
"AES-GCM 128-bit key, illegal tag length 256-bits decryption",
|
||||
"AES-GCM 192-bit key, illegal tag length 256-bits decryption",
|
||||
"AES-GCM 256-bit key, illegal tag length 256-bits decryption"
|
||||
]
|
||||
}
|
||||
},
|
||||
"algorithm-discards-context.https.window.js": {
|
||||
"skip": "Not relevant in Node.js context"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user