diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index 34e09b8b6ed..537870f25e8 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -1220,15 +1220,16 @@ function validateKeyOps(keyOps, usagesSet) { if (keyOps === undefined) return; validateArray(keyOps, 'keyData.key_ops'); let keyOpsMask = 0; + const seen = new SafeSet(); for (let n = 0; n < keyOps.length; n++) { const op = keyOps[n]; + if (seen.has(op)) + throw lazyDOMException('Duplicate key operation', 'DataError'); + seen.add(op); const opMask = kUsageMasks[op]; // Skipping unknown key ops if (opMask === undefined) continue; - // Have we seen it already? if so, error - if (keyOpsMask & opMask) - throw lazyDOMException('Duplicate key operation', 'DataError'); keyOpsMask |= opMask; // TODO(@jasnell): RFC7517 section 4.3 strong recommends validating diff --git a/test/parallel/test-webcrypto-key-ops-duplicates.js b/test/parallel/test-webcrypto-key-ops-duplicates.js new file mode 100644 index 00000000000..4a666a1caa4 --- /dev/null +++ b/test/parallel/test-webcrypto-key-ops-duplicates.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; + +(async () => { + const algorithm = { name: 'HMAC', hash: 'SHA-256' }; + const jwk = { kty: 'oct', k: 'AAAAAAAAAAAAAAAAAAAAAA' }; + await subtle.importKey('jwk', { ...jwk, key_ops: ['sign', 'future'] }, + algorithm, false, ['sign']); + for (const key_ops of [['sign', 'sign'], ['sign', 'future', 'future']]) { + await assert.rejects(subtle.importKey('jwk', { ...jwk, key_ops }, + algorithm, false, ['sign']), + { name: 'DataError' }); + } +})().then(common.mustCall());