|
7 | 7 |
|
8 | 8 | var wrappers = []; // Things we wrap (and upwrap) keys with
|
9 | 9 | var keys = []; // Things to wrap and unwrap
|
10 |
| - var ecdhPeerKey; // ECDH peer public key needed for non-extractable ECDH key comparison |
11 | 10 |
|
12 | 11 | // Generate all the keys needed, then iterate over all combinations
|
13 | 12 | // to test wrapping and unwrapping.
|
14 | 13 | promise_test(function() {
|
15 |
| - return Promise.all([generateWrappingKeys(), generateKeysToWrap(), generateEcdhPeerKey()]) |
| 14 | + return Promise.all([generateWrappingKeys(), generateKeysToWrap()]) |
16 | 15 | .then(function(results) {
|
17 | 16 | var promises = [];
|
18 | 17 | wrappers.forEach(function(wrapper) {
|
|
83 | 82 | {algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["sign"], publicUsages: ["verify"]},
|
84 | 83 | {algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["deriveBits"], publicUsages: []},
|
85 | 84 | {algorithm: {name: "Ed25519" }, privateUsages: ["sign"], publicUsages: ["verify"]},
|
| 85 | + {algorithm: {name: "Ed448" }, privateUsages: ["sign"], publicUsages: ["verify"]}, |
| 86 | + {algorithm: {name: "X25519" }, privateUsages: ["deriveBits"], publicUsages: []}, |
| 87 | + {algorithm: {name: "X448" }, privateUsages: ["deriveBits"], publicUsages: []}, |
86 | 88 | {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]},
|
87 | 89 | {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]},
|
88 | 90 | {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]},
|
|
112 | 114 | }));
|
113 | 115 | }
|
114 | 116 |
|
115 |
| - function generateEcdhPeerKey() { |
116 |
| - return subtle.generateKey({name: "ECDH", namedCurve: "P-256"},true,["deriveBits"]) |
117 |
| - .then(function(result){ |
118 |
| - ecdhPeerKey = result.publicKey; |
119 |
| - }); |
120 |
| - } |
121 |
| - |
122 | 117 | // Can we successfully "round-trip" (wrap, then unwrap, a key)?
|
123 | 118 | function testWrapping(wrapper, toWrap) {
|
124 | 119 | var formats;
|
|
135 | 130 | var originalExport;
|
136 | 131 | return subtle.exportKey(fmt, toWrap.key).then(function(exportedKey) {
|
137 | 132 | originalExport = exportedKey;
|
138 |
| - if (wrappingIsPossible(originalExport, wrapper.parameters.name)) { |
139 |
| - promise_test(function(test) { |
| 133 | + const isPossible = wrappingIsPossible(originalExport, wrapper.parameters.name); |
| 134 | + promise_test(function(test) { |
| 135 | + if (!isPossible) { |
| 136 | + return Promise.resolve().then(() => { |
| 137 | + assert_false(false, "Wrapping is not possible"); |
| 138 | + }) |
| 139 | + } |
| 140 | + return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters) |
| 141 | + .then(function(wrappedResult) { |
| 142 | + return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); |
| 143 | + }).then(function(unwrappedResult) { |
| 144 | + assert_true(unwrappedResult.extractable, "Unwrapped result is extractable"); |
| 145 | + return subtle.exportKey(fmt, unwrappedResult) |
| 146 | + }).then(function(roundTripExport) { |
| 147 | + assert_true(equalExport(originalExport, roundTripExport), "Post-wrap export matches original export"); |
| 148 | + }, function(err) { |
| 149 | + assert_unreached("Round trip for extractable key threw an error - " + err.name + ': "' + err.message + '"'); |
| 150 | + }); |
| 151 | + }, "Can wrap and unwrap " + toWrap.name + " keys using " + fmt + " and " + wrapper.parameters.name); |
| 152 | + |
| 153 | + if (canCompareNonExtractableKeys(toWrap.key)) { |
| 154 | + promise_test(function(test){ |
| 155 | + if (!isPossible) { |
| 156 | + return Promise.resolve().then(() => { |
| 157 | + assert_false(false, "Wrapping is not possible"); |
| 158 | + }) |
| 159 | + } |
140 | 160 | return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters)
|
141 | 161 | .then(function(wrappedResult) {
|
142 |
| - return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); |
143 |
| - }).then(function(unwrappedResult) { |
144 |
| - assert_true(unwrappedResult.extractable, "Unwrapped result is extractable"); |
145 |
| - return subtle.exportKey(fmt, unwrappedResult) |
146 |
| - }).then(function(roundTripExport) { |
147 |
| - assert_true(equalExport(originalExport, roundTripExport), "Post-wrap export matches original export"); |
148 |
| - }, function(err) { |
149 |
| - assert_unreached("Round trip for extractable key threw an error - " + err.name + ': "' + err.message + '"'); |
| 162 | + return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); |
| 163 | + }).then(function(unwrappedResult){ |
| 164 | + assert_false(unwrappedResult.extractable, "Unwrapped result is non-extractable"); |
| 165 | + return equalKeys(toWrap.key, unwrappedResult); |
| 166 | + }).then(function(result){ |
| 167 | + assert_true(result, "Unwrapped key matches original"); |
| 168 | + }).catch(function(err){ |
| 169 | + assert_unreached("Round trip for key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"'); |
150 | 170 | });
|
151 |
| - }, "Can wrap and unwrap " + toWrap.name + " keys using " + fmt + " and " + wrapper.parameters.name); |
| 171 | + }, "Can wrap and unwrap " + toWrap.name + " keys as non-extractable using " + fmt + " and " + wrapper.parameters.name); |
152 | 172 |
|
153 |
| - if (canCompareNonExtractableKeys(toWrap.key)) { |
| 173 | + if (fmt === "jwk") { |
154 | 174 | promise_test(function(test){
|
155 |
| - return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters) |
156 |
| - .then(function(wrappedResult) { |
157 |
| - return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); |
| 175 | + if (!isPossible) { |
| 176 | + return Promise.resolve().then(() => { |
| 177 | + assert_false(false, "Wrapping is not possible"); |
| 178 | + }) |
| 179 | + } |
| 180 | + var wrappedKey; |
| 181 | + return wrapAsNonExtractableJwk(toWrap.key,wrapper).then(function(wrappedResult){ |
| 182 | + wrappedKey = wrappedResult; |
| 183 | + return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); |
158 | 184 | }).then(function(unwrappedResult){
|
159 |
| - assert_false(unwrappedResult.extractable, "Unwrapped result is non-extractable"); |
160 |
| - return equalKeys(toWrap.key, unwrappedResult); |
| 185 | + assert_false(unwrappedResult.extractable, "Unwrapped key is non-extractable"); |
| 186 | + return equalKeys(toWrap.key,unwrappedResult); |
161 | 187 | }).then(function(result){
|
162 | 188 | assert_true(result, "Unwrapped key matches original");
|
163 | 189 | }).catch(function(err){
|
164 |
| - assert_unreached("Round trip for key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"'); |
| 190 | + assert_unreached("Round trip for non-extractable key threw an error - " + err.name + ': "' + err.message + '"'); |
| 191 | + }).then(function(){ |
| 192 | + return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); |
| 193 | + }).then(function(unwrappedResult){ |
| 194 | + assert_unreached("Unwrapping a non-extractable JWK as extractable should fail"); |
| 195 | + }).catch(function(err){ |
| 196 | + assert_equals(err.name, "DataError", "Unwrapping a non-extractable JWK as extractable fails with DataError"); |
165 | 197 | });
|
166 |
| - }, "Can wrap and unwrap " + toWrap.name + " keys as non-extractable using " + fmt + " and " + wrapper.parameters.name); |
167 |
| - |
168 |
| - if (fmt === "jwk") { |
169 |
| - promise_test(function(test){ |
170 |
| - var wrappedKey; |
171 |
| - return wrapAsNonExtractableJwk(toWrap.key,wrapper).then(function(wrappedResult){ |
172 |
| - wrappedKey = wrappedResult; |
173 |
| - return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); |
174 |
| - }).then(function(unwrappedResult){ |
175 |
| - assert_false(unwrappedResult.extractable, "Unwrapped key is non-extractable"); |
176 |
| - return equalKeys(toWrap.key,unwrappedResult); |
177 |
| - }).then(function(result){ |
178 |
| - assert_true(result, "Unwrapped key matches original"); |
179 |
| - }).catch(function(err){ |
180 |
| - assert_unreached("Round trip for non-extractable key threw an error - " + err.name + ': "' + err.message + '"'); |
181 |
| - }).then(function(){ |
182 |
| - return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); |
183 |
| - }).then(function(unwrappedResult){ |
184 |
| - assert_unreached("Unwrapping a non-extractable JWK as extractable should fail"); |
185 |
| - }).catch(function(err){ |
186 |
| - assert_equals(err.name, "DataError", "Unwrapping a non-extractable JWK as extractable fails with DataError"); |
187 |
| - }); |
188 |
| - }, "Can unwrap " + toWrap.name + " non-extractable keys using jwk and " + wrapper.parameters.name); |
189 |
| - } |
| 198 | + }, "Can unwrap " + toWrap.name + " non-extractable keys using jwk and " + wrapper.parameters.name); |
190 | 199 | }
|
191 | 200 | }
|
192 | 201 | });
|
|
386 | 395 | case "Ed25519" :
|
387 | 396 | signParams = {name: "Ed25519"};
|
388 | 397 | break;
|
| 398 | + case "Ed448" : |
| 399 | + signParams = {name: "Ed448"}; |
| 400 | + break; |
| 401 | + case "X25519" : |
| 402 | + deriveParams = {name: "X25519"}; |
| 403 | + break; |
| 404 | + case "X448" : |
| 405 | + deriveParams = {name: "X448"}; |
| 406 | + break; |
389 | 407 | case "HMAC" :
|
390 | 408 | signParams = {name: "HMAC"};
|
391 | 409 | break;
|
392 | 410 | case "AES-KW" :
|
393 | 411 | wrapParams = {name: "AES-KW"};
|
394 | 412 | break;
|
395 | 413 | case "ECDH" :
|
396 |
| - deriveParams = {name: "ECDH", public: ecdhPeerKey}; |
| 414 | + deriveParams = {name: "ECDH"}; |
397 | 415 | break;
|
398 | 416 | default:
|
399 | 417 | throw new Error("Unsupported algorithm for key comparison");
|
|
422 | 440 | if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.name === "RSASSA-PKCS1-v1_5") {
|
423 | 441 | ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; });
|
424 | 442 | }
|
425 |
| - if (expected.algorithm.name === "ECDSA" || expected.algorithm.name === "Ed25519") { |
| 443 | + if (expected.algorithm.name === "ECDSA" || expected.algorithm.name.startsWith("Ed")) { |
426 | 444 | delete jwkExpectedKey["d"];
|
427 | 445 | }
|
428 | 446 | jwkExpectedKey.key_ops = ["verify"];
|
|
446 | 464 | var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).values());
|
447 | 465 | return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i]);
|
448 | 466 | });
|
449 |
| - } else { |
| 467 | + } else if (deriveParams) { |
450 | 468 | var expectedDerivedBits;
|
451 |
| - return subtle.deriveBits(deriveParams, expected, 128) |
| 469 | + return subtle.generateKey(expected.algorithm, true, ['deriveBits']).then(({ publicKey }) => { |
| 470 | + deriveParams.public = publicKey; |
| 471 | + return subtle.deriveBits(deriveParams, expected, 128) |
| 472 | + }) |
452 | 473 | .then(function(result){
|
453 | 474 | expectedDerivedBits = Array.from((new Uint8Array(result)).values());
|
454 | 475 | return subtle.deriveBits(deriveParams, got, 128);
|
|
0 commit comments