diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index b7d1abf4a85daf..ff2cc108c53e0a 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -47,6 +47,7 @@ const { InternalCryptoKey, SecretKeyObject, createSecretKey, + kAlgorithm, } = require('internal/crypto/keys'); const { @@ -108,7 +109,7 @@ function asyncAesCtrCipher(mode, key, data, algorithm) { mode, key[kKeyObject][kHandle], data, - getVariant('AES-CTR', key.algorithm.length), + getVariant('AES-CTR', key[kAlgorithm].length), algorithm.counter, algorithm.length)); } @@ -119,7 +120,7 @@ function asyncAesCbcCipher(mode, key, data, algorithm) { mode, key[kKeyObject][kHandle], data, - getVariant('AES-CBC', key.algorithm.length), + getVariant('AES-CBC', key[kAlgorithm].length), algorithm.iv)); } @@ -129,7 +130,7 @@ function asyncAesKwCipher(mode, key, data) { mode, key[kKeyObject][kHandle], data, - getVariant('AES-KW', key.algorithm.length))); + getVariant('AES-KW', key[kAlgorithm].length))); } function asyncAesGcmCipher(mode, key, data, algorithm) { @@ -166,7 +167,7 @@ function asyncAesGcmCipher(mode, key, data, algorithm) { mode, key[kKeyObject][kHandle], data, - getVariant('AES-GCM', key.algorithm.length), + getVariant('AES-GCM', key[kAlgorithm].length), algorithm.iv, tag, algorithm.additionalData)); diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index e8af5750a865fd..97272ab1672021 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -47,6 +47,7 @@ const { PublicKeyObject, createPrivateKey, createPublicKey, + kKeyType, } = require('internal/crypto/keys'); const generateKeyPair = promisify(_generateKeyPair); @@ -343,7 +344,7 @@ function eddsaSignVerify(key, data, algorithm, signature) { const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify; const type = mode === kSignJobModeSign ? 'private' : 'public'; - if (key.type !== type) + if (key[kKeyType] !== type) throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError'); return jobPromise(() => new SignJob( diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index c01427f889f159..b6ba8cfc78541e 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -52,6 +52,8 @@ const { const { KeyObject, + kAlgorithm, + kKeyType, } = require('internal/crypto/keys'); const { @@ -328,20 +330,20 @@ let masks; async function ecdhDeriveBits(algorithm, baseKey, length) { const { 'public': key } = algorithm; - if (baseKey.type !== 'private') { + if (baseKey[kKeyType] !== 'private') { throw lazyDOMException( 'baseKey must be a private key', 'InvalidAccessError'); } - if (key.algorithm.name !== baseKey.algorithm.name) { + if (key[kAlgorithm].name !== baseKey[kAlgorithm].name) { throw lazyDOMException( 'The public and private keys must be of the same type', 'InvalidAccessError'); } if ( - key.algorithm.name === 'ECDH' && - key.algorithm.namedCurve !== baseKey.algorithm.namedCurve + key[kAlgorithm].name === 'ECDH' && + key[kAlgorithm].namedCurve !== baseKey[kAlgorithm].namedCurve ) { throw lazyDOMException('Named curve mismatch', 'InvalidAccessError'); } diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index f4ea317b86ee73..52791412835300 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -41,6 +41,7 @@ const { PublicKeyObject, createPrivateKey, createPublicKey, + kKeyType, } = require('internal/crypto/keys'); const generateKeyPair = promisify(_generateKeyPair); @@ -284,7 +285,7 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) { const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify; const type = mode === kSignJobModeSign ? 'private' : 'public'; - if (key.type !== type) + if (key[kKeyType] !== type) throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError'); const hashname = normalizeHashName(hash.name); diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index c34f36a277ea37..fdaa93a7f8fb5b 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, ArrayPrototypeSlice, ObjectDefineProperties, ObjectDefineProperty, @@ -81,6 +82,8 @@ const kAlgorithm = Symbol('kAlgorithm'); const kExtractable = Symbol('kExtractable'); const kKeyType = Symbol('kKeyType'); const kKeyUsages = Symbol('kKeyUsages'); +const kCachedAlgorithm = Symbol('kCachedAlgorithm'); +const kCachedKeyUsages = Symbol('kCachedKeyUsages'); // Key input contexts. const kConsumePublic = 0; @@ -217,7 +220,7 @@ const { throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); } - if (result.usages.length === 0) { + if (result[kKeyUsages].length === 0) { throw lazyDOMException( `Usages cannot be empty when importing a ${result.type} key.`, 'SyntaxError'); @@ -309,7 +312,7 @@ const { throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); } - if (result.type === 'private' && result.usages.length === 0) { + if (result.type === 'private' && result[kKeyUsages].length === 0) { throw lazyDOMException( `Usages cannot be empty when importing a ${result.type} key.`, 'SyntaxError'); @@ -735,8 +738,8 @@ function prepareSecretKey(key, encoding, bufferOnly = false) { throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret'); return key[kHandle]; } else if (isCryptoKey(key)) { - if (key.type !== 'secret') - throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret'); + if (key[kKeyType] !== 'secret') + throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key[kKeyType], 'secret'); return key[kKeyObject][kHandle]; } } @@ -785,7 +788,7 @@ function createPrivateKey(key) { } function isKeyObject(obj) { - return obj != null && obj[kKeyType] !== undefined; + return obj != null && obj[kKeyType] !== undefined && obj[kKeyObject] === undefined; } // Our implementation of CryptoKey is a simple wrapper around a KeyObject @@ -809,17 +812,21 @@ class CryptoKey { }; return `CryptoKey ${inspect({ - type: this.type, - extractable: this.extractable, - algorithm: this.algorithm, - usages: this.usages, + type: this[kKeyType], + extractable: this[kExtractable], + algorithm: this[kAlgorithm], + usages: this[kKeyUsages], }, opts)}`; } + get [kKeyType]() { + return this[kKeyObject].type; + } + get type() { if (!(this instanceof CryptoKey)) throw new ERR_INVALID_THIS('CryptoKey'); - return this[kKeyObject].type; + return this[kKeyType]; } get extractable() { @@ -831,13 +838,19 @@ class CryptoKey { get algorithm() { if (!(this instanceof CryptoKey)) throw new ERR_INVALID_THIS('CryptoKey'); - return this[kAlgorithm]; + if (!this[kCachedAlgorithm]) { + this[kCachedAlgorithm] ??= { ...this[kAlgorithm] }; + this[kCachedAlgorithm].hash &&= { ...this[kCachedAlgorithm].hash }; + this[kCachedAlgorithm].publicExponent &&= new Uint8Array(this[kCachedAlgorithm].publicExponent); + } + return this[kCachedAlgorithm]; } get usages() { if (!(this instanceof CryptoKey)) throw new ERR_INVALID_THIS('CryptoKey'); - return this[kKeyUsages]; + this[kCachedKeyUsages] ??= ArrayFrom(this[kKeyUsages]); + return this[kCachedKeyUsages]; } } @@ -1008,4 +1021,8 @@ module.exports = { isKeyObject, isCryptoKey, importGenericSecretKey, + kAlgorithm, + kExtractable, + kKeyType, + kKeyUsages, }; diff --git a/lib/internal/crypto/mac.js b/lib/internal/crypto/mac.js index 4a78c45c70d84d..ed30a64ba239ea 100644 --- a/lib/internal/crypto/mac.js +++ b/lib/internal/crypto/mac.js @@ -36,6 +36,7 @@ const { InternalCryptoKey, SecretKeyObject, createSecretKey, + kAlgorithm, } = require('internal/crypto/keys'); const generateKey = promisify(_generateKey); @@ -161,7 +162,7 @@ function hmacSignVerify(key, data, algorithm, signature) { return jobPromise(() => new HmacJob( kCryptoJobAsync, mode, - normalizeHashName(key.algorithm.hash.name), + normalizeHashName(key[kAlgorithm].hash.name), key[kKeyObject][kHandle], data, signature)); diff --git a/lib/internal/crypto/ml_dsa.js b/lib/internal/crypto/ml_dsa.js index 21dc95786b10e1..3dabe5743b9a99 100644 --- a/lib/internal/crypto/ml_dsa.js +++ b/lib/internal/crypto/ml_dsa.js @@ -51,6 +51,8 @@ const { PublicKeyObject, createPrivateKey, createPublicKey, + kAlgorithm, + kKeyType, } = require('internal/crypto/keys'); const generateKeyPair = promisify(_generateKeyPair); @@ -116,7 +118,7 @@ function mlDsaExportKey(key, format) { try { switch (format) { case kWebCryptoKeyFormatRaw: { - if (key.type === 'private') { + if (key[kKeyType] === 'private') { const { priv } = key[kKeyObject][kHandle].exportJwk({}, false); return Buffer.alloc(32, priv, 'base64url').buffer; } @@ -136,7 +138,7 @@ function mlDsaExportKey(key, format) { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x00, 0x04, 0x22, 0x80, 0x20, ], 0); - switch (key.algorithm.name) { + switch (key[kAlgorithm].name) { case 'ML-DSA-44': buffer.set([0x11], 17); break; @@ -292,7 +294,7 @@ function mlDsaSignVerify(key, data, algorithm, signature) { const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify; const type = mode === kSignJobModeSign ? 'private' : 'public'; - if (key.type !== type) + if (key[kKeyType] !== type) throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError'); return jobPromise(() => new SignJob( diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js index dd0669df4ed032..ecb3759ba48354 100644 --- a/lib/internal/crypto/rsa.js +++ b/lib/internal/crypto/rsa.js @@ -50,6 +50,8 @@ const { PublicKeyObject, createPublicKey, createPrivateKey, + kAlgorithm, + kKeyType, } = require('internal/crypto/keys'); const { @@ -95,7 +97,7 @@ function rsaOaepCipher(mode, key, data, algorithm) { validateRsaOaepAlgorithm(algorithm); const type = mode === kWebCryptoCipherEncrypt ? 'public' : 'private'; - if (key.type !== type) { + if (key[kKeyType] !== type) { throw lazyDOMException( 'The requested operation is not valid for the provided key', 'InvalidAccessError'); @@ -107,7 +109,7 @@ function rsaOaepCipher(mode, key, data, algorithm) { key[kKeyObject][kHandle], data, kKeyVariantRSA_OAEP, - normalizeHashName(key.algorithm.hash.name), + normalizeHashName(key[kAlgorithm].hash.name), algorithm.label)); } @@ -201,7 +203,7 @@ function rsaExportKey(key, format) { kCryptoJobAsync, format, key[kKeyObject][kHandle], - kRsaVariants[key.algorithm.name])); + kRsaVariants[key[kAlgorithm].name])); } function rsaImportKey( @@ -329,16 +331,16 @@ function rsaSignVerify(key, data, { saltLength }, signature) { const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify; const type = mode === kSignJobModeSign ? 'private' : 'public'; - if (key.type !== type) + if (key[kKeyType] !== type) throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError'); return jobPromise(() => { - if (key.algorithm.name === 'RSA-PSS') { + if (key[kAlgorithm].name === 'RSA-PSS') { validateInt32( saltLength, 'algorithm.saltLength', 0, - MathCeil((key.algorithm.modulusLength - 1) / 8) - getDigestSizeInBytes(key.algorithm.hash.name) - 2); + MathCeil((key[kAlgorithm].modulusLength - 1) / 8) - getDigestSizeInBytes(key[kAlgorithm].hash.name) - 2); } return new SignJob( @@ -349,9 +351,9 @@ function rsaSignVerify(key, data, { saltLength }, signature) { undefined, undefined, data, - normalizeHashName(key.algorithm.hash.name), + normalizeHashName(key[kAlgorithm].hash.name), saltLength, - key.algorithm.name === 'RSA-PSS' ? RSA_PKCS1_PSS_PADDING : undefined, + key[kAlgorithm].name === 'RSA-PSS' ? RSA_PKCS1_PSS_PADDING : undefined, undefined, signature); }); diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index 685637ab7435df..3ea8059a3bf8c0 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -397,7 +397,9 @@ const kSupportedAlgorithms = createSupportedAlgorithms(kAlgorithmDefinitions); const simpleAlgorithmDictionaries = { AeadParams: { iv: 'BufferSource', additionalData: 'BufferSource' }, - RsaHashedKeyGenParams: { hash: 'HashAlgorithmIdentifier' }, + // publicExponent is not strictly a BufferSource but it is a Uint8Array that we normalize + // this way + RsaHashedKeyGenParams: { hash: 'HashAlgorithmIdentifier', publicExponent: 'BufferSource' }, EcKeyGenParams: {}, HmacKeyGenParams: { hash: 'HashAlgorithmIdentifier' }, RsaPssParams: {}, diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js index 5961ee3e67dbf7..6c2407cdca3445 100644 --- a/lib/internal/crypto/webcrypto.js +++ b/lib/internal/crypto/webcrypto.js @@ -33,6 +33,10 @@ const { createPublicKey, CryptoKey, importGenericSecretKey, + kAlgorithm, + kKeyUsages, + kExtractable, + kKeyType, } = require('internal/crypto/keys'); const { @@ -175,9 +179,9 @@ async function generateKey( if ( (resultType === 'CryptoKey' && - (result.type === 'secret' || result.type === 'private') && - result.usages.length === 0) || - (resultType === 'CryptoKeyPair' && result.privateKey.usages.length === 0) + (result[kKeyType] === 'secret' || result[kKeyType] === 'private') && + result[kKeyUsages].length === 0) || + (resultType === 'CryptoKeyPair' && result.privateKey[kKeyUsages].length === 0) ) { throw lazyDOMException( 'Usages cannot be empty when creating a key.', @@ -209,12 +213,12 @@ async function deriveBits(algorithm, baseKey, length = null) { } algorithm = normalizeAlgorithm(algorithm, 'deriveBits'); - if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveBits')) { + if (!ArrayPrototypeIncludes(baseKey[kKeyUsages], 'deriveBits')) { throw lazyDOMException( 'baseKey does not have deriveBits usage', 'InvalidAccessError'); } - if (baseKey.algorithm.name !== algorithm.name) + if (baseKey[kAlgorithm].name !== algorithm.name) throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError'); switch (algorithm.name) { case 'X25519': @@ -296,12 +300,12 @@ async function deriveKey( algorithm = normalizeAlgorithm(algorithm, 'deriveBits'); derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm, 'importKey'); - if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveKey')) { + if (!ArrayPrototypeIncludes(baseKey[kKeyUsages], 'deriveKey')) { throw lazyDOMException( 'baseKey does not have deriveKey usage', 'InvalidAccessError'); } - if (baseKey.algorithm.name !== algorithm.name) + if (baseKey[kAlgorithm].name !== algorithm.name) throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError'); const length = getKeyLength(normalizeAlgorithm(arguments[2], 'get key length')); @@ -335,7 +339,7 @@ async function deriveKey( } async function exportKeySpki(key) { - switch (key.algorithm.name) { + switch (key[kAlgorithm].name) { case 'RSASSA-PKCS1-v1_5': // Fall through case 'RSA-PSS': @@ -371,7 +375,7 @@ async function exportKeySpki(key) { } async function exportKeyPkcs8(key) { - switch (key.algorithm.name) { + switch (key[kAlgorithm].name) { case 'RSASSA-PKCS1-v1_5': // Fall through case 'RSA-PSS': @@ -407,7 +411,7 @@ async function exportKeyPkcs8(key) { } async function exportKeyRawPublic(key, format) { - switch (key.algorithm.name) { + switch (key[kAlgorithm].name) { case 'ECDSA': // Fall through case 'ECDH': @@ -440,7 +444,7 @@ async function exportKeyRawPublic(key, format) { } async function exportKeyRawSeed(key) { - switch (key.algorithm.name) { + switch (key[kAlgorithm].name) { case 'ML-DSA-44': // Fall through case 'ML-DSA-65': @@ -455,7 +459,7 @@ async function exportKeyRawSeed(key) { } async function exportKeyRawSecret(key, format) { - switch (key.algorithm.name) { + switch (key[kAlgorithm].name) { case 'AES-CTR': // Fall through case 'AES-CBC': @@ -478,27 +482,27 @@ async function exportKeyRawSecret(key, format) { async function exportKeyJWK(key) { const parameters = { - key_ops: key.usages, - ext: key.extractable, + key_ops: key[kKeyUsages], + ext: key[kExtractable], }; - switch (key.algorithm.name) { + switch (key[kAlgorithm].name) { case 'RSASSA-PKCS1-v1_5': { const alg = normalizeHashName( - key.algorithm.hash.name, + key[kAlgorithm].hash.name, normalizeHashName.kContextJwkRsa); if (alg) parameters.alg = alg; break; } case 'RSA-PSS': { const alg = normalizeHashName( - key.algorithm.hash.name, + key[kAlgorithm].hash.name, normalizeHashName.kContextJwkRsaPss); if (alg) parameters.alg = alg; break; } case 'RSA-OAEP': { const alg = normalizeHashName( - key.algorithm.hash.name, + key[kAlgorithm].hash.name, normalizeHashName.kContextJwkRsaOaep); if (alg) parameters.alg = alg; break; @@ -520,7 +524,7 @@ async function exportKeyJWK(key) { case 'Ed25519': // Fall through case 'Ed448': - parameters.alg = key.algorithm.name; + parameters.alg = key[kAlgorithm].name; break; case 'AES-CTR': // Fall through @@ -530,14 +534,14 @@ async function exportKeyJWK(key) { // Fall through case 'AES-KW': parameters.alg = require('internal/crypto/aes') - .getAlgorithmName(key.algorithm.name, key.algorithm.length); + .getAlgorithmName(key[kAlgorithm].name, key[kAlgorithm].length); break; case 'ChaCha20-Poly1305': parameters.alg = 'C20P'; break; case 'HMAC': { const alg = normalizeHashName( - key.algorithm.hash.name, + key[kAlgorithm].hash.name, normalizeHashName.kContextJwkHmac); if (alg) parameters.alg = alg; break; @@ -565,25 +569,25 @@ async function exportKey(format, key) { }); try { - normalizeAlgorithm(key.algorithm, 'exportKey'); + normalizeAlgorithm(key[kAlgorithm], 'exportKey'); } catch { throw lazyDOMException( - `${key.algorithm.name} key export is not supported`, 'NotSupportedError'); + `${key[kAlgorithm].name} key export is not supported`, 'NotSupportedError'); } - if (!key.extractable) + if (!key[kExtractable]) throw lazyDOMException('key is not extractable', 'InvalidAccessException'); let result; switch (format) { case 'spki': { - if (key.type === 'public') { + if (key[kKeyType] === 'public') { result = await exportKeySpki(key); } break; } case 'pkcs8': { - if (key.type === 'private') { + if (key[kKeyType] === 'private') { result = await exportKeyPkcs8(key); } break; @@ -593,27 +597,27 @@ async function exportKey(format, key) { break; } case 'raw-secret': { - if (key.type === 'secret') { + if (key[kKeyType] === 'secret') { result = await exportKeyRawSecret(key, format); } break; } case 'raw-public': { - if (key.type === 'public') { + if (key[kKeyType] === 'public') { result = await exportKeyRawPublic(key, format); } break; } case 'raw-seed': { - if (key.type === 'private') { + if (key[kKeyType] === 'private') { result = await exportKeyRawSeed(key); } break; } case 'raw': { - if (key.type === 'secret') { + if (key[kKeyType] === 'secret') { result = await exportKeyRawSecret(key, format); - } else if (key.type === 'public') { + } else if (key[kKeyType] === 'public') { result = await exportKeyRawPublic(key, format); } break; @@ -622,7 +626,7 @@ async function exportKey(format, key) { if (!result) { throw lazyDOMException( - `Unable to export ${key.algorithm.name} ${key.type} key using ${format} format`, + `Unable to export ${key[kAlgorithm].name} ${key[kKeyType]} key using ${format} format`, 'NotSupportedError'); } @@ -749,7 +753,7 @@ async function importKey( 'NotSupportedError'); } - if ((result.type === 'secret' || result.type === 'private') && result.usages.length === 0) { + if ((result.type === 'secret' || result.type === 'private') && result[kKeyUsages].length === 0) { throw lazyDOMException( `Usages cannot be empty when importing a ${result.type} key.`, 'SyntaxError'); @@ -897,8 +901,8 @@ function signVerify(algorithm, key, data, signature) { } algorithm = normalizeAlgorithm(algorithm, usage); - if (!ArrayPrototypeIncludes(key.usages, usage) || - algorithm.name !== key.algorithm.name) { + if (!ArrayPrototypeIncludes(key[kKeyUsages], usage) || + algorithm.name !== key[kAlgorithm].name) { throw lazyDOMException( `Unable to use this key to ${usage}`, 'InvalidAccessError'); @@ -987,8 +991,8 @@ async function cipherOrWrap(mode, algorithm, key, data, op) { // in this case. Both Firefox and Chrome throw simple TypeErrors here. // The key algorithm and cipher algorithm must match, and the // key must have the proper usage. - if (key.algorithm.name !== algorithm.name || - !ArrayPrototypeIncludes(key.usages, op)) { + if (key[kAlgorithm].name !== algorithm.name || + !ArrayPrototypeIncludes(key[kKeyUsages], op)) { throw lazyDOMException( 'The requested operation is not valid for the provided key', 'InvalidAccessError'); @@ -1085,12 +1089,12 @@ async function getPublicKey(key, keyUsages) { context: '2nd argument', }); - if (key.type !== 'private') + if (key[kKeyType] !== 'private') throw lazyDOMException('key must be a private key', 'InvalidAccessError'); const keyObject = createPublicKey(key[kKeyObject]); - return keyObject.toCryptoKey(key.algorithm, true, keyUsages); + return keyObject.toCryptoKey(key[kAlgorithm], true, keyUsages); } // The SubtleCrypto and Crypto classes are defined as part of the diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index e9f486289d81fe..81eefcba15f4d4 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -136,8 +136,6 @@ const kIsArray = 1; const kIsSet = 2; const kIsMap = 3; -let kKeyObject; - // Check if they have the same source and flags function areSimilarRegExps(a, b) { return a.source === b.source && @@ -401,11 +399,12 @@ function objectComparisonStart(val1, val2, mode, memos) { return false; } } else if (isCryptoKey(val1)) { - kKeyObject ??= require('internal/crypto/util').kKeyObject; + const { kKeyObject } = require('internal/crypto/util'); + const { kExtractable, kAlgorithm, kKeyUsages } = require('internal/crypto/keys'); if (!isCryptoKey(val2) || - val1.extractable !== val2.extractable || - !innerDeepEqual(val1.algorithm, val2.algorithm, mode, memos) || - !innerDeepEqual(val1.usages, val2.usages, mode, memos) || + val1[kExtractable] !== val2[kExtractable] || + !innerDeepEqual(val1[kAlgorithm], val2[kAlgorithm], mode, memos) || + !innerDeepEqual(val1[kKeyUsages], val2[kKeyUsages], mode, memos) || !innerDeepEqual(val1[kKeyObject], val2[kKeyObject], mode, memos) ) { return false; diff --git a/test/parallel/test-webcrypto-internal-slots.mjs b/test/parallel/test-webcrypto-internal-slots.mjs new file mode 100644 index 00000000000000..9b824167ba4553 --- /dev/null +++ b/test/parallel/test-webcrypto-internal-slots.mjs @@ -0,0 +1,21 @@ +import * as common from '../common/index.mjs'; + +if (!common.hasCrypto) + common.skip('missing crypto'); + +import * as assert from 'node:assert'; +import * as util from 'node:util'; + +const { subtle } = globalThis.crypto; + +const kp = await subtle.generateKey('Ed25519', true, ['sign', 'verify']); +assert.notStrictEqual(kp.publicKey.algorithm, kp.privateKey.algorithm); +assert.notStrictEqual(kp.publicKey.usages, kp.privateKey.usages); +kp.publicKey.algorithm.name = 'ed25519'; +assert.strictEqual(kp.publicKey.algorithm.name, 'ed25519'); +kp.publicKey.usages.push('foo'); +assert.ok(kp.publicKey.usages.includes('foo')); +assert.ok(util.inspect(kp.publicKey).includes("algorithm: { name: 'Ed25519' }")); +assert.ok(util.inspect(kp.publicKey).includes("usages: [ 'verify' ]")); + +await subtle.sign('Ed25519', kp.privateKey, Buffer.alloc(32)); diff --git a/test/parallel/test-webcrypto-keygen.js b/test/parallel/test-webcrypto-keygen.js index 95be384c16b046..1b9e4e01404d6c 100644 --- a/test/parallel/test-webcrypto-keygen.js +++ b/test/parallel/test-webcrypto-keygen.js @@ -311,14 +311,20 @@ if (hasOpenSSL(3, 5)) { assert.deepStrictEqual(privateKey.usages, privateUsages); assert.strictEqual(publicKey.algorithm.name, name); assert.strictEqual(publicKey.algorithm.modulusLength, modulusLength); - assert.deepStrictEqual(publicKey.algorithm.publicExponent, publicExponent); + assert(publicKey.algorithm.publicExponent instanceof Uint8Array); + assert.notStrictEqual(publicKey.algorithm.publicExponent, publicExponent); + assert(!Buffer.isBuffer(publicKey.algorithm.publicExponent)); + assert.deepStrictEqual(publicKey.algorithm.publicExponent, new Uint8Array(publicExponent)); assert.strictEqual( KeyObject.from(publicKey).asymmetricKeyDetails.publicExponent, bigIntArrayToUnsignedBigInt(publicExponent)); assert.strictEqual(publicKey.algorithm.hash.name, hash); assert.strictEqual(privateKey.algorithm.name, name); assert.strictEqual(privateKey.algorithm.modulusLength, modulusLength); - assert.deepStrictEqual(privateKey.algorithm.publicExponent, publicExponent); + assert(privateKey.algorithm.publicExponent instanceof Uint8Array); + assert.notStrictEqual(privateKey.algorithm.publicExponent, publicExponent); + assert(!Buffer.isBuffer(privateKey.algorithm.publicExponent)); + assert.deepStrictEqual(privateKey.algorithm.publicExponent, new Uint8Array(publicExponent)); assert.strictEqual( KeyObject.from(privateKey).asymmetricKeyDetails.publicExponent, bigIntArrayToUnsignedBigInt(publicExponent));