Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

// 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();