Commits:
-
84c46aeb
by Anna Weine at 2024-08-31T12:30:34+08:00
Bug 1760806 - WebCrypto: ECDH and ECDSA JWK import to check that the crv in params and crv in alg are the same r=keeler
https://treeherder.mozilla.org/jobs?repo=try&revision=ed7936b105dea8e588650feb6baf26a50a6439fc
Differential Revision: https://phabricator.services.mozilla.com/D217273
4 changed files:
Changes:
dom/crypto/WebCryptoTask.cpp
... |
... |
@@ -1777,7 +1777,8 @@ class ImportEcKeyTask : public ImportKeyTask { |
1777
|
1777
|
return;
|
1778
|
1778
|
}
|
1779
|
1779
|
|
1780
|
|
- if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
|
|
1780
|
+ if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
|
|
1781
|
+ mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
1781
|
1782
|
RootedDictionary<EcKeyImportParams> params(aCx);
|
1782
|
1783
|
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
1783
|
1784
|
if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
|
... |
... |
@@ -1882,11 +1883,21 @@ class ImportEcKeyTask : public ImportKeyTask { |
1882
|
1883
|
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
1883
|
1884
|
}
|
1884
|
1885
|
|
1885
|
|
- // Extract 'crv' parameter from JWKs.
|
|
1886
|
+ // Checking the 'crv' consistency
|
1886
|
1887
|
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
1887
|
|
- if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
|
|
1888
|
+ // the curve stated in 'crv field'
|
|
1889
|
+ nsString namedCurveFromCrv;
|
|
1890
|
+ if (!NormalizeToken(mJwk.mCrv.Value(), namedCurveFromCrv)) {
|
1888
|
1891
|
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
1889
|
1892
|
}
|
|
1893
|
+
|
|
1894
|
+ // https://w3c.github.io/webcrypto/#ecdh-operations
|
|
1895
|
+ // https://w3c.github.io/webcrypto/#ecdsa-operations
|
|
1896
|
+ // If namedCurve is not equal to the namedCurve member of
|
|
1897
|
+ // normalizedAlgorithm (mNamedCurve in our case), throw a DataError.
|
|
1898
|
+ if (!mNamedCurve.Equals(namedCurveFromCrv)) {
|
|
1899
|
+ return NS_ERROR_DOM_DATA_ERR;
|
|
1900
|
+ }
|
1890
|
1901
|
}
|
1891
|
1902
|
return NS_OK;
|
1892
|
1903
|
}
|
dom/crypto/test/test-vectors.js
... |
... |
@@ -901,6 +901,13 @@ let tv = { |
901
|
901
|
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8",
|
902
|
902
|
},
|
903
|
903
|
|
|
904
|
+ jwk_different_crv: {
|
|
905
|
+ kty: "EC",
|
|
906
|
+ crv: "P-521",
|
|
907
|
+ x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
|
|
908
|
+ y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8",
|
|
909
|
+ },
|
|
910
|
+
|
904
|
911
|
// The crv parameter is missing.
|
905
|
912
|
jwk_missing_crv: {
|
906
|
913
|
kty: "EC",
|
... |
... |
@@ -1017,6 +1024,18 @@ let tv = { |
1017
|
1024
|
},
|
1018
|
1025
|
},
|
1019
|
1026
|
|
|
1027
|
+ // An ECDSA key in JWK format, which an "crv" field doesn't match the alg's crv.
|
|
1028
|
+ ecdsa_jwk_crv_mismatch: {
|
|
1029
|
+ pub_jwk: {
|
|
1030
|
+ kty: "EC",
|
|
1031
|
+ crv: "P-256",
|
|
1032
|
+ alg: "ECDSA",
|
|
1033
|
+
|
|
1034
|
+ x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
|
|
1035
|
+ y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8",
|
|
1036
|
+ },
|
|
1037
|
+ },
|
|
1038
|
+
|
1020
|
1039
|
ecdsa_bad: {
|
1021
|
1040
|
pub_jwk: {
|
1022
|
1041
|
kty: "EC",
|
dom/crypto/test/test_WebCrypto_ECDH.html
... |
... |
@@ -152,12 +152,24 @@ TestArray.addTest( |
152
|
152
|
}
|
153
|
153
|
);
|
154
|
154
|
|
|
155
|
+// -----------------------------------------------------------------------------
|
|
156
|
+TestArray.addTest(
|
|
157
|
+ "Verify that ECDH import fails with a key with a mismatched 'crv' field",
|
|
158
|
+ function() {
|
|
159
|
+ var that = this;
|
|
160
|
+ var alg = { name: "ECDH", namedCurve: "P-521"};
|
|
161
|
+
|
|
162
|
+ crypto.subtle.importKey("jwk", tv.ecdsa_jwk_crv_mismatch.pub_jwk, alg, true, ["verify"])
|
|
163
|
+ .then(error(that), complete(that));
|
|
164
|
+ }
|
|
165
|
+);
|
|
166
|
+
|
155
|
167
|
// -----------------------------------------------------------------------------
|
156
|
168
|
TestArray.addTest(
|
157
|
169
|
"JWK import an ECDH public and private key and derive bits (P-256)",
|
158
|
170
|
function() {
|
159
|
171
|
var that = this;
|
160
|
|
- var alg = { name: "ECDH" };
|
|
172
|
+ var alg = { name: "ECDH", namedCurve: "P-256" };
|
161
|
173
|
|
162
|
174
|
var pubKey, privKey;
|
163
|
175
|
function setPub(x) { pubKey = x; }
|
... |
... |
@@ -182,7 +194,7 @@ TestArray.addTest( |
182
|
194
|
"JWK import an ECDH public and private key and derive bits (P-384)",
|
183
|
195
|
function() {
|
184
|
196
|
var that = this;
|
185
|
|
- var alg = { name: "ECDH" };
|
|
197
|
+ var alg = { name: "ECDH", namedCurve: "P-384"};
|
186
|
198
|
|
187
|
199
|
var pubKey, privKey;
|
188
|
200
|
function setPub(x) { pubKey = x; }
|
... |
... |
@@ -207,7 +219,7 @@ TestArray.addTest( |
207
|
219
|
"JWK import an ECDH public and private key and derive bits (P-521)",
|
208
|
220
|
function() {
|
209
|
221
|
var that = this;
|
210
|
|
- var alg = { name: "ECDH" };
|
|
222
|
+ var alg = { name: "ECDH", namedCurve : "P-521" };
|
211
|
223
|
|
212
|
224
|
var pubKey, privKey;
|
213
|
225
|
function setPub(x) { pubKey = x; }
|
... |
... |
@@ -232,7 +244,7 @@ TestArray.addTest( |
232
|
244
|
"JWK import/export roundtrip with ECDH (P-256)",
|
233
|
245
|
function() {
|
234
|
246
|
var that = this;
|
235
|
|
- var alg = { name: "ECDH" };
|
|
247
|
+ var alg = { name: "ECDH", namedCurve : "P-256" };
|
236
|
248
|
|
237
|
249
|
var pubKey, privKey;
|
238
|
250
|
function setPub(x) { pubKey = x; }
|
... |
... |
@@ -277,7 +289,7 @@ TestArray.addTest( |
277
|
289
|
"PKCS8 import/export roundtrip with ECDH (P-256)",
|
278
|
290
|
function() {
|
279
|
291
|
var that = this;
|
280
|
|
- var alg = { name: "ECDH", namedCurve: "P-256" };
|
|
292
|
+ var alg = { name: "ECDH", namedCurve: "P-256" };
|
281
|
293
|
|
282
|
294
|
function doExportPriv(x) {
|
283
|
295
|
return crypto.subtle.exportKey("pkcs8", x);
|
... |
... |
@@ -296,7 +308,7 @@ TestArray.addTest( |
296
|
308
|
"Test that importing bad JWKs fails",
|
297
|
309
|
function() {
|
298
|
310
|
var that = this;
|
299
|
|
- var alg = { name: "ECDH" };
|
|
311
|
+ var alg = { name: "ECDH", namedCurve: "P-256" };
|
300
|
312
|
var tvs = tv.ecdh_p256_negative;
|
301
|
313
|
|
302
|
314
|
function doTryImport(jwk) {
|
... |
... |
@@ -306,6 +318,7 @@ TestArray.addTest( |
306
|
318
|
}
|
307
|
319
|
|
308
|
320
|
doTryImport(tvs.jwk_bad_crv)()
|
|
321
|
+ .then(error(that), doTryImport(tvs.jwk_different_crv))
|
309
|
322
|
.then(error(that), doTryImport(tvs.jwk_missing_crv))
|
310
|
323
|
.then(error(that), doTryImport(tvs.jwk_missing_x))
|
311
|
324
|
.then(error(that), doTryImport(tvs.jwk_missing_y))
|
... |
... |
@@ -349,7 +362,7 @@ TestArray.addTest( |
349
|
362
|
"Derive an HMAC key from two ECDH keys and test sign/verify",
|
350
|
363
|
function() {
|
351
|
364
|
var that = this;
|
352
|
|
- var alg = { name: "ECDH" };
|
|
365
|
+ var alg = { name: "ECDH", namedCurve: "P-521" };
|
353
|
366
|
var algDerived = { name: "HMAC", hash: {name: "SHA-1"} };
|
354
|
367
|
|
355
|
368
|
var pubKey, privKey;
|
... |
... |
@@ -391,6 +404,28 @@ TestArray.addTest( |
391
|
404
|
}
|
392
|
405
|
);
|
393
|
406
|
|
|
407
|
+// -----------------------------------------------------------------------------
|
|
408
|
+TestArray.addTest(
|
|
409
|
+ "Derive an HKDF key from two ECDH keys and derive an HMAC key from that",
|
|
410
|
+ function() {
|
|
411
|
+ var that = this;
|
|
412
|
+ var alg = { name: "ECDH", namedCurve: "P-256" };
|
|
413
|
+
|
|
414
|
+ async function doTest() {
|
|
415
|
+ let privKey = await crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveKey"]);
|
|
416
|
+ let pubKey = await crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_pub, alg, false, []);
|
|
417
|
+ let ecdhAlg = { name: "ECDH", public: pubKey };
|
|
418
|
+ let hkdfAlg = { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(), info: new Uint8Array() };
|
|
419
|
+ let hkdfKey = await crypto.subtle.deriveKey(ecdhAlg, privKey, hkdfAlg, false, ["deriveKey"]);
|
|
420
|
+ let hmacAlg = { name: "HMAC", hash: "SHA-256" };
|
|
421
|
+ let hmacKey = await crypto.subtle.deriveKey(hkdfAlg, hkdfKey, hmacAlg, false, ["sign"]);
|
|
422
|
+ return crypto.subtle.sign("HMAC", hmacKey, new Uint8Array());
|
|
423
|
+ }
|
|
424
|
+ const expected = util.hex2abv("acf62832fa93469824cd997593bc963b28a68e6f73f4516bbe51b35942fe9811");
|
|
425
|
+ doTest().then(memcmp_complete(that, expected), error(that));
|
|
426
|
+ }
|
|
427
|
+);
|
|
428
|
+
|
394
|
429
|
// -----------------------------------------------------------------------------
|
395
|
430
|
TestArray.addTest(
|
396
|
431
|
"SPKI import/export of public ECDH keys (P-256)",
|
... |
... |
@@ -433,7 +468,7 @@ TestArray.addTest( |
433
|
468
|
"SPKI/JWK import ECDH keys (P-256) and derive a known secret",
|
434
|
469
|
function() {
|
435
|
470
|
var that = this;
|
436
|
|
- var alg = { name: "ECDH" };
|
|
471
|
+ var alg = { name: "ECDH", namedCurve: "P-256" };
|
437
|
472
|
|
438
|
473
|
var pubKey, privKey;
|
439
|
474
|
function setPub(x) { pubKey = x; }
|
dom/crypto/test/test_WebCrypto_ECDSA.html
... |
... |
@@ -91,7 +91,7 @@ TestArray.addTest( |
91
|
91
|
"ECDSA JWK import and reject a known-bad signature",
|
92
|
92
|
function() {
|
93
|
93
|
var that = this;
|
94
|
|
- var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
|
|
94
|
+ var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
|
95
|
95
|
|
96
|
96
|
function doVerify(x) {
|
97
|
97
|
return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig_tampered,
|
... |
... |
@@ -141,6 +141,18 @@ TestArray.addTest( |
141
|
141
|
}
|
142
|
142
|
);
|
143
|
143
|
|
|
144
|
+// -----------------------------------------------------------------------------
|
|
145
|
+TestArray.addTest(
|
|
146
|
+ "Verify that ECDSA import fails with a key with a mismatched 'crv' field",
|
|
147
|
+ function() {
|
|
148
|
+ var that = this;
|
|
149
|
+ var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
|
|
150
|
+
|
|
151
|
+ crypto.subtle.importKey("jwk", tv.ecdsa_jwk_crv_mismatch.pub_jwk, alg, true, ["verify"])
|
|
152
|
+ .then(error(that), complete(that));
|
|
153
|
+ }
|
|
154
|
+);
|
|
155
|
+
|
144
|
156
|
// -----------------------------------------------------------------------------
|
145
|
157
|
TestArray.addTest(
|
146
|
158
|
"Verify that ECDSA import fails with a known-bad public key",
|
|