| // META: title=WebCrypto API: supports method tests |
| // META: script=util/helpers.js |
| |
| 'use strict'; |
| |
| const standardAlgorithms = { |
| // Asymmetric algorithms |
| 'RSASSA-PKCS1-v1_5': { |
| operations: ['generateKey', 'importKey', 'sign', 'verify'], |
| keyGenParams: { |
| name: 'RSASSA-PKCS1-v1_5', |
| modulusLength: 2048, |
| publicExponent: new Uint8Array([1, 0, 1]), |
| hash: 'SHA-256', |
| }, |
| importParams: { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, |
| signParams: { name: 'RSASSA-PKCS1-v1_5' }, |
| }, |
| 'RSA-PSS': { |
| operations: ['generateKey', 'importKey', 'sign', 'verify'], |
| keyGenParams: { |
| name: 'RSA-PSS', |
| modulusLength: 2048, |
| publicExponent: new Uint8Array([1, 0, 1]), |
| hash: 'SHA-256', |
| }, |
| importParams: { name: 'RSA-PSS', hash: 'SHA-256' }, |
| signParams: { name: 'RSA-PSS', saltLength: 32 }, |
| }, |
| 'RSA-OAEP': { |
| operations: ['generateKey', 'importKey', 'encrypt', 'decrypt'], |
| keyGenParams: { |
| name: 'RSA-OAEP', |
| modulusLength: 2048, |
| publicExponent: new Uint8Array([1, 0, 1]), |
| hash: 'SHA-256', |
| }, |
| importParams: { name: 'RSA-OAEP', hash: 'SHA-256' }, |
| encryptParams: { name: 'RSA-OAEP' }, |
| }, |
| ECDSA: { |
| operations: ['generateKey', 'importKey', 'sign', 'verify'], |
| keyGenParams: { name: 'ECDSA', namedCurve: 'P-256' }, |
| importParams: { name: 'ECDSA', namedCurve: 'P-256' }, |
| signParams: { name: 'ECDSA', hash: 'SHA-256' }, |
| }, |
| ECDH: { |
| operations: ['generateKey', 'importKey', 'deriveBits'], |
| keyGenParams: { name: 'ECDH', namedCurve: 'P-256' }, |
| importParams: { name: 'ECDH', namedCurve: 'P-256' }, |
| deriveBitsParams: { |
| name: 'ECDH', |
| public: crypto.subtle.generateKey( |
| { name: 'ECDH', namedCurve: 'P-256' }, |
| false, |
| ['deriveBits'] |
| ), |
| }, |
| }, |
| Ed25519: { |
| operations: ['generateKey', 'importKey', 'sign', 'verify'], |
| keyGenParams: null, |
| signParams: { name: 'Ed25519' }, |
| }, |
| X25519: { |
| operations: ['generateKey', 'importKey', 'deriveBits'], |
| keyGenParams: null, |
| deriveBitsParams: { |
| name: 'X25519', |
| public: crypto.subtle.generateKey('X25519', false, ['deriveBits']), |
| }, |
| }, |
| |
| // Symmetric algorithms |
| 'AES-CBC': { |
| operations: ['generateKey', 'importKey', 'encrypt', 'decrypt'], |
| keyGenParams: { name: 'AES-CBC', length: 256 }, |
| encryptParams: { name: 'AES-CBC', iv: new Uint8Array(16) }, |
| }, |
| 'AES-CTR': { |
| operations: ['generateKey', 'importKey', 'encrypt', 'decrypt'], |
| keyGenParams: { name: 'AES-CTR', length: 256 }, |
| encryptParams: { |
| name: 'AES-CTR', |
| counter: new Uint8Array(16), |
| length: 128, |
| }, |
| }, |
| 'AES-GCM': { |
| operations: ['generateKey', 'importKey', 'encrypt', 'decrypt'], |
| keyGenParams: { name: 'AES-GCM', length: 256 }, |
| encryptParams: { name: 'AES-GCM', iv: new Uint8Array(12) }, |
| }, |
| 'AES-KW': { |
| operations: ['generateKey', 'importKey'], // wrapKey/unwrapKey not in requested operations |
| keyGenParams: { name: 'AES-KW', length: 256 }, |
| }, |
| HMAC: { |
| operations: ['generateKey', 'importKey', 'sign', 'verify'], |
| keyGenParams: { name: 'HMAC', hash: 'SHA-256' }, |
| importParams: { name: 'HMAC', hash: 'SHA-256' }, |
| signParams: { name: 'HMAC' }, |
| }, |
| |
| // Hash algorithms |
| 'SHA-1': { |
| operations: ['digest'], |
| keyGenParams: null, |
| }, |
| 'SHA-256': { |
| operations: ['digest'], |
| keyGenParams: null, |
| }, |
| 'SHA-384': { |
| operations: ['digest'], |
| keyGenParams: null, |
| }, |
| 'SHA-512': { |
| operations: ['digest'], |
| keyGenParams: null, |
| }, |
| |
| // Key derivation algorithms |
| HKDF: { |
| operations: ['importKey', 'deriveBits'], |
| keyGenParams: null, |
| deriveBitsParams: { |
| name: 'HKDF', |
| hash: 'SHA-256', |
| salt: new Uint8Array(16), |
| info: new Uint8Array(0), |
| }, |
| }, |
| PBKDF2: { |
| operations: ['importKey', 'deriveBits'], |
| keyGenParams: null, |
| deriveBitsParams: { |
| name: 'PBKDF2', |
| hash: 'SHA-256', |
| salt: new Uint8Array(16), |
| iterations: 100000, |
| }, |
| }, |
| }; |
| |
| const operations = [ |
| 'generateKey', |
| 'importKey', |
| 'sign', |
| 'verify', |
| 'encrypt', |
| 'decrypt', |
| 'deriveBits', |
| 'digest', |
| ]; |
| |
| // Test that supports method exists and is a static method |
| test(() => { |
| assert_true( |
| typeof SubtleCrypto.supports === 'function', |
| 'SubtleCrypto.supports should be a function' |
| ); |
| }, 'SubtleCrypto.supports method exists'); |
| |
| // Test invalid operation names |
| test(() => { |
| assert_false( |
| SubtleCrypto.supports('invalidOperation', 'AES-GCM'), |
| 'Invalid operation should return false' |
| ); |
| assert_false( |
| SubtleCrypto.supports('', 'AES-GCM'), |
| 'Empty operation should return false' |
| ); |
| assert_false( |
| SubtleCrypto.supports('GENERATEKEY', 'AES-GCM'), |
| 'Case-sensitive operation check' |
| ); |
| }, 'supports returns false for invalid operations'); |
| |
| // Test invalid algorithm identifiers |
| test(() => { |
| assert_false( |
| SubtleCrypto.supports('generateKey', 'InvalidAlgorithm'), |
| 'Invalid algorithm should return false' |
| ); |
| assert_false( |
| SubtleCrypto.supports('generateKey', ''), |
| 'Empty algorithm should return false' |
| ); |
| }, 'supports returns false for invalid algorithms'); |
| |
| // Test standard WebCrypto algorithms for requested operations |
| for (const [algorithmName, algorithmInfo] of Object.entries( |
| standardAlgorithms |
| )) { |
| for (const operation of operations) { |
| promise_test(async (t) => { |
| const isSupported = algorithmInfo.operations.includes(operation); |
| |
| // Use appropriate algorithm parameters for each operation |
| let algorithm; |
| let length; |
| switch (operation) { |
| case 'generateKey': |
| algorithm = algorithmInfo.keyGenParams || algorithmName; |
| break; |
| case 'importKey': |
| algorithm = algorithmInfo.importParams || algorithmName; |
| break; |
| case 'sign': |
| case 'verify': |
| algorithm = algorithmInfo.signParams || algorithmName; |
| break; |
| case 'encrypt': |
| case 'decrypt': |
| algorithm = algorithmInfo.encryptParams || algorithmName; |
| break; |
| case 'deriveBits': |
| algorithm = algorithmInfo.deriveBitsParams || algorithmName; |
| if (algorithm?.public instanceof Promise) { |
| algorithm.public = (await algorithm.public).publicKey; |
| } |
| if (algorithmName === 'PBKDF2' || algorithmName === 'HKDF') { |
| length = 256; |
| } |
| break; |
| case 'digest': |
| algorithm = algorithmName; |
| break; |
| default: |
| algorithm = algorithmName; |
| } |
| |
| const result = SubtleCrypto.supports(operation, algorithm, length); |
| |
| if (isSupported) { |
| assert_true(result, `${algorithmName} should support ${operation}`); |
| } else { |
| assert_false(result, `${algorithmName} should not support ${operation}`); |
| } |
| }, `supports(${operation}, ${algorithmName})`); |
| } |
| } |
| |
| // Test algorithm objects (not just strings) |
| test(() => { |
| assert_true( |
| SubtleCrypto.supports('generateKey', { name: 'AES-GCM', length: 256 }), |
| 'Algorithm object should be supported' |
| ); |
| assert_true( |
| SubtleCrypto.supports('generateKey', { name: 'HMAC', hash: 'SHA-256' }), |
| 'Algorithm object with parameters should be supported' |
| ); |
| }, 'supports works with algorithm objects'); |
| |
| // Test with algorithm objects that have invalid parameters |
| test(() => { |
| assert_false( |
| SubtleCrypto.supports('generateKey', { name: 'AES-GCM', length: 100 }), |
| 'Invalid key length should return false' |
| ); |
| assert_false( |
| SubtleCrypto.supports('generateKey', { |
| name: 'HMAC', |
| hash: 'INVALID-HASH', |
| }), |
| 'Invalid hash parameter should return false' |
| ); |
| }, 'supports returns false for algorithm objects with invalid parameters'); |
| |
| // Test some specific combinations that should work |
| test(() => { |
| // RSA algorithms |
| assert_true( |
| SubtleCrypto.supports('generateKey', { |
| name: 'RSASSA-PKCS1-v1_5', |
| modulusLength: 2048, |
| publicExponent: new Uint8Array([1, 0, 1]), |
| hash: 'SHA-256', |
| }), |
| 'RSASSA-PKCS1-v1_5 generateKey' |
| ); |
| assert_true( |
| SubtleCrypto.supports('sign', { name: 'RSASSA-PKCS1-v1_5' }), |
| 'RSASSA-PKCS1-v1_5 sign' |
| ); |
| assert_true( |
| SubtleCrypto.supports('verify', { name: 'RSASSA-PKCS1-v1_5' }), |
| 'RSASSA-PKCS1-v1_5 verify' |
| ); |
| |
| // ECDSA |
| assert_true( |
| SubtleCrypto.supports('generateKey', { |
| name: 'ECDSA', |
| namedCurve: 'P-256', |
| }), |
| 'ECDSA generateKey' |
| ); |
| assert_true( |
| SubtleCrypto.supports('sign', { name: 'ECDSA', hash: 'SHA-256' }), |
| 'ECDSA sign' |
| ); |
| assert_true( |
| SubtleCrypto.supports('verify', { name: 'ECDSA', hash: 'SHA-256' }), |
| 'ECDSA verify' |
| ); |
| |
| // AES-GCM |
| assert_true( |
| SubtleCrypto.supports('generateKey', { name: 'AES-GCM', length: 256 }), |
| 'AES-GCM generateKey' |
| ); |
| assert_true( |
| SubtleCrypto.supports('encrypt', { |
| name: 'AES-GCM', |
| iv: new Uint8Array(12), |
| }), |
| 'AES-GCM encrypt' |
| ); |
| assert_true( |
| SubtleCrypto.supports('decrypt', { |
| name: 'AES-GCM', |
| iv: new Uint8Array(12), |
| }), |
| 'AES-GCM decrypt' |
| ); |
| |
| // HMAC |
| assert_true( |
| SubtleCrypto.supports('generateKey', { name: 'HMAC', hash: 'SHA-256' }), |
| 'HMAC generateKey' |
| ); |
| assert_true(SubtleCrypto.supports('sign', { name: 'HMAC' }), 'HMAC sign'); |
| assert_true(SubtleCrypto.supports('verify', { name: 'HMAC' }), 'HMAC verify'); |
| }, 'Common algorithm and operation combinations work'); |
| |
| // Test some specific combinations that should not work |
| test(() => { |
| // Hash algorithms don't support key operations |
| assert_false( |
| SubtleCrypto.supports('generateKey', 'SHA-256'), |
| 'SHA-256 generateKey should fail' |
| ); |
| assert_false( |
| SubtleCrypto.supports('sign', 'SHA-256'), |
| 'SHA-256 sign should fail' |
| ); |
| |
| // AES can't sign/verify (these require algorithm parameters due to normalization) |
| assert_false( |
| SubtleCrypto.supports('sign', 'AES-GCM'), |
| 'AES-GCM sign should fail' |
| ); |
| assert_false( |
| SubtleCrypto.supports('verify', 'AES-GCM'), |
| 'AES-GCM verify should fail' |
| ); |
| |
| // ECDSA can't encrypt/decrypt |
| assert_false( |
| SubtleCrypto.supports('encrypt', 'ECDSA'), |
| 'ECDSA encrypt should fail' |
| ); |
| assert_false( |
| SubtleCrypto.supports('decrypt', 'ECDSA'), |
| 'ECDSA decrypt should fail' |
| ); |
| |
| // HMAC can't encrypt/decrypt |
| assert_false( |
| SubtleCrypto.supports('encrypt', 'HMAC'), |
| 'HMAC encrypt should fail' |
| ); |
| assert_false( |
| SubtleCrypto.supports('decrypt', 'HMAC'), |
| 'HMAC decrypt should fail' |
| ); |
| |
| // Non-hash algorithms can't digest |
| assert_false( |
| SubtleCrypto.supports('digest', 'AES-GCM'), |
| 'AES-GCM digest should fail' |
| ); |
| assert_false( |
| SubtleCrypto.supports('digest', 'ECDSA'), |
| 'ECDSA digest should fail' |
| ); |
| assert_false( |
| SubtleCrypto.supports('digest', 'HMAC'), |
| 'HMAC digest should fail' |
| ); |
| }, 'Invalid algorithm and operation combinations fail'); |
| |
| done(); |