successes.js (9554B)
1 function run_test(algorithmNames, slowTest) { 2 var subtle = crypto.subtle; // Change to test prefixed implementations 3 4 setup({explicit_timeout: true}); 5 6 // These tests check that generateKey successfully creates keys 7 // when provided any of a wide set of correct parameters 8 // and that they can be exported afterwards. 9 // 10 // There are a lot of combinations of possible parameters, 11 // resulting in a very large number of tests 12 // performed. 13 14 15 // Setup: define the correct behaviors that should be sought, and create 16 // helper functions that generate all possible test parameters for 17 // different situations. 18 19 var allTestVectors = [ // Parameters that should work for generateKey 20 {name: "AES-CTR", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 21 {name: "AES-CBC", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 22 {name: "AES-GCM", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 23 {name: "AES-OCB", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 24 {name: "ChaCha20-Poly1305", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 25 {name: "AES-KW", resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []}, 26 {name: "HMAC", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, 27 {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 28 {name: "RSA-PSS", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 29 {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]}, 30 {name: "ECDSA", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 31 {name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 32 {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 33 {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 34 {name: "ML-DSA-44", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 35 {name: "ML-DSA-65", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 36 {name: "ML-DSA-87", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 37 {name: "ML-KEM-512", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]}, 38 {name: "ML-KEM-768", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]}, 39 {name: "ML-KEM-1024", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]}, 40 {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 41 {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 42 {name: "KMAC128", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, 43 {name: "KMAC256", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, 44 ]; 45 46 var testVectors = []; 47 if (algorithmNames && !Array.isArray(algorithmNames)) { 48 algorithmNames = [algorithmNames]; 49 }; 50 allTestVectors.forEach(function(vector) { 51 if (!algorithmNames || algorithmNames.includes(vector.name)) { 52 testVectors.push(vector); 53 } 54 }); 55 56 function parameterString(algorithm, extractable, usages) { 57 var result = "(" + 58 objectToString(algorithm) + ", " + 59 objectToString(extractable) + ", " + 60 objectToString(usages) + 61 ")"; 62 63 return result; 64 } 65 66 // Test that a given combination of parameters is successful 67 function testSuccess(algorithm, extractable, usages, resultType, testTag) { 68 // algorithm, extractable, and usages are the generateKey parameters 69 // resultType is the expected result, either the CryptoKey object or "CryptoKeyPair" 70 // testTag is a string to prepend to the test name. 71 72 promise_test(function(test) { 73 return subtle.generateKey(algorithm, extractable, usages) 74 .then(function(result) { 75 if (resultType === "CryptoKeyPair") { 76 assert_goodCryptoKey(result.privateKey, algorithm, extractable, usages, "private"); 77 assert_goodCryptoKey(result.publicKey, algorithm, true, usages, "public"); 78 } else { 79 assert_goodCryptoKey(result, algorithm, extractable, usages, "secret"); 80 } 81 return result; 82 }, function(err) { 83 assert_unreached("generateKey threw an unexpected error: " + err.toString()); 84 }) 85 .then(async function (result) { 86 // TODO: remove this block to enable ML-KEM JWK when its definition is done in IETF JOSE WG 87 if (result.publicKey?.algorithm.name.startsWith('ML-KEM')) { 88 const promises = [ 89 subtle.exportKey('spki', result.publicKey), 90 extractable ? subtle.exportKey('pkcs8', result.privateKey) : undefined, 91 subtle.exportKey('raw-public', result.publicKey), 92 ]; 93 if (extractable) 94 promises.push(subtle.exportKey('raw-seed', result.privateKey)); 95 } else if (resultType === "CryptoKeyPair") { 96 const promises = [ 97 subtle.exportKey('jwk', result.publicKey), 98 extractable ? subtle.exportKey('jwk', result.privateKey) : undefined, 99 subtle.exportKey('spki', result.publicKey), 100 extractable ? subtle.exportKey('pkcs8', result.privateKey) : undefined, 101 ]; 102 103 switch (result.publicKey.algorithm.name.substring(0, 2)) { 104 case 'ML': 105 promises.push(subtle.exportKey('raw-public', result.publicKey)); 106 if (extractable) 107 promises.push(subtle.exportKey('raw-seed', result.privateKey)); 108 break; 109 case 'SL': 110 promises.push(subtle.exportKey('raw-public', result.publicKey)); 111 if (extractable) 112 promises.push(subtle.exportKey('raw-private', result.privateKey)); 113 break; 114 case 'EC': 115 case 'Ed': 116 case 'X2': 117 case 'X4': 118 promises.push(subtle.exportKey('raw', result.publicKey)); 119 break; 120 case 'RS': 121 break; 122 default: 123 throw new Error('not implemented'); 124 } 125 126 const [jwkPub, jwkPriv] = await Promise.all(promises); 127 128 if (extractable) { 129 // Test that the JWK public key is a superset of the JWK private key. 130 for (const [prop, value] of Object.entries(jwkPub)) { 131 if (prop !== 'key_ops') { 132 assert_equals(value, jwkPriv[prop], `Property ${prop} is equal in public and private JWK`); 133 } 134 } 135 } 136 } else { 137 if (extractable) { 138 await Promise.all([ 139 subtle.exportKey(/cha|ocb|kmac/i.test(result.algorithm.name) ? 'raw-secret' : 'raw', result), 140 subtle.exportKey('jwk', result), 141 ]); 142 } 143 } 144 }, function(err) { 145 assert_unreached("exportKey threw an unexpected error: " + err.toString()); 146 }) 147 }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages)); 148 } 149 150 // Test all valid sets of parameters for successful 151 // key generation. 152 testVectors.forEach(function(vector) { 153 allNameVariants(vector.name, slowTest).forEach(function(name) { 154 allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { 155 allValidUsages(vector.usages, false, vector.mandatoryUsages).forEach(function(usages) { 156 [false, true].forEach(function(extractable) { 157 subsetTest(testSuccess, algorithm, extractable, usages, vector.resultType, "Success"); 158 }); 159 }); 160 }); 161 }); 162 }); 163 164 }