From 09b8c8be0a47a3a3a4e2d6ae11bcead55ef52c2b Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 22 Sep 2026 22:18:26 +0200 Subject: [PATCH] crypto: convert importKey data as a union Choose the BufferSource or JsonWebKey branch from the value, then check the requested format after algorithm normalization. Signed-off-by: Filip Skokan Assisted-by: Codex PR-URL: https://github.com/nodejs/node/pull/66237 Reviewed-By: James M Snell Reviewed-By: Aviv Keller --- lib/internal/crypto/webcrypto.js | 11 +++++- test/parallel/test-webcrypto-import-union.js | 35 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-webcrypto-import-union.js diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js index 847017529dc..73ae08fa1e1 100644 --- a/lib/internal/crypto/webcrypto.js +++ b/lib/internal/crypto/webcrypto.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayBufferIsView, ArrayIsArray, ArrayPrototypeSlice, FunctionPrototypeCall, @@ -39,6 +40,7 @@ const { const { codes: { ERR_ILLEGAL_CONSTRUCTOR, + ERR_INVALID_ARG_TYPE, ERR_INVALID_THIS, }, } = require('internal/errors'); @@ -85,6 +87,7 @@ const { } = require('internal/crypto/random'); const { + isArrayBuffer, isPromise, } = require('internal/util/types'); @@ -1114,7 +1117,8 @@ function importKeyImpl( const prefix = prepareSubtleMethod(this, 'importKey', arguments.length, 5); let i = 0; format = convertSubtleArgument(prefix, 'KeyFormat', format, i++); - const type = format === 'jwk' ? 'JsonWebKey' : 'BufferSource'; + const type = ArrayBufferIsView(keyData) || isArrayBuffer(keyData) ? + 'BufferSource' : 'JsonWebKey'; keyData = convertSubtleArgument(prefix, type, keyData, i++); algorithm = convertSubtleArgument( prefix, 'AlgorithmIdentifier', algorithm, i++); @@ -1124,6 +1128,11 @@ function importKeyImpl( const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'importKey'); + if ((format === 'jwk') !== (type === 'JsonWebKey')) { + throw new ERR_INVALID_ARG_TYPE( + 'keyData', format === 'jwk' ? 'JsonWebKey' : 'BufferSource', keyData); + } + return FunctionPrototypeCall( importKeySync, this, diff --git a/test/parallel/test-webcrypto-import-union.js b/test/parallel/test-webcrypto-import-union.js new file mode 100644 index 00000000000..3391ba5c8dc --- /dev/null +++ b/test/parallel/test-webcrypto-import-union.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; +const jwk = { kty: 'oct', k: 'AAAAAAAAAAAAAAAAAAAAAA' }; + +(async () => { + for (const data of [new ArrayBuffer(16), new Uint8Array(16), + new DataView(new ArrayBuffer(16)), Buffer.alloc(16)]) { + await assert.rejects(subtle.importKey('jwk', data, 'AES-GCM', true, ['encrypt']), + TypeError); + Object.assign(data, jwk); + await assert.rejects(subtle.importKey('jwk', data, 'AES-GCM', true, ['encrypt']), + TypeError); + await assert.rejects(subtle.importKey('jwk', data, 'unknown', true, ['encrypt']), + { name: 'NotSupportedError' }); + } + await assert.rejects(subtle.importKey('raw', {}, 'unknown', true, ['encrypt']), + { name: 'NotSupportedError' }); + await assert.rejects(subtle.importKey('raw', {}, 'AES-GCM', true, ['encrypt']), + TypeError); + for (const data of [null, new SharedArrayBuffer(16)]) { + await assert.rejects(subtle.importKey('jwk', data, 'AES-GCM', true, ['encrypt']), + { name: 'DataError' }); + } + assert.strictEqual((await subtle.importKey('jwk', jwk, 'AES-GCM', true, + ['encrypt'])).type, 'secret'); + await assert.rejects(subtle.importKey('raw', { + get kty() { throw new Error('converted JWK'); }, + }, 'unknown', true, ['encrypt']), { message: 'converted JWK' }); +})().then(common.mustCall());