Bug 1760806 - WebCrypto: ECDH and ECDSA JWK import to check that the crv in params and crv in alg are the same a=diannaS

https://treeherder.mozilla.org/jobs?repo=try&revision=ed7936b105dea8e588650feb6baf26a50a6439fc

Original Revision: https://phabricator.services.mozilla.com/D217273

Differential Revision: https://phabricator.services.mozilla.com/D220783
This commit is contained in:
Anna Weine 2024-09-04 19:00:46 +00:00
parent 3ce8045867
commit 5622512f6b
4 changed files with 70 additions and 14 deletions

View file

@ -1802,7 +1802,8 @@ class ImportEcKeyTask : public ImportKeyTask {
return; return;
} }
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) { if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
RootedDictionary<EcKeyImportParams> params(aCx); RootedDictionary<EcKeyImportParams> params(aCx);
mEarlyRv = Coerce(aCx, params, aAlgorithm); mEarlyRv = Coerce(aCx, params, aAlgorithm);
if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) { if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
@ -1907,11 +1908,21 @@ class ImportEcKeyTask : public ImportKeyTask {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR; return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
} }
// Extract 'crv' parameter from JWKs. // Checking the 'crv' consistency
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) { // the curve stated in 'crv field'
nsString namedCurveFromCrv;
if (!NormalizeToken(mJwk.mCrv.Value(), namedCurveFromCrv)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR; return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
} }
// https://w3c.github.io/webcrypto/#ecdh-operations
// https://w3c.github.io/webcrypto/#ecdsa-operations
// If namedCurve is not equal to the namedCurve member of
// normalizedAlgorithm (mNamedCurve in our case), throw a DataError.
if (!mNamedCurve.Equals(namedCurveFromCrv)) {
return NS_ERROR_DOM_DATA_ERR;
}
} }
return NS_OK; return NS_OK;
} }

View file

@ -901,6 +901,13 @@ let tv = {
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8", y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8",
}, },
jwk_different_crv: {
kty: "EC",
crv: "P-521",
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8",
},
// The crv parameter is missing. // The crv parameter is missing.
jwk_missing_crv: { jwk_missing_crv: {
kty: "EC", kty: "EC",
@ -1017,6 +1024,18 @@ let tv = {
}, },
}, },
// An ECDSA key in JWK format, which an "crv" field doesn't match the alg's crv.
ecdsa_jwk_crv_mismatch: {
pub_jwk: {
kty: "EC",
crv: "P-256",
alg: "ECDSA",
x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
y: "9M8HWzlAXdHxresJAQftz7K0ljc52HZ54wVssFV9Ct8",
},
},
ecdsa_bad: { ecdsa_bad: {
pub_jwk: { pub_jwk: {
kty: "EC", kty: "EC",

View file

@ -152,12 +152,24 @@ TestArray.addTest(
} }
); );
// -----------------------------------------------------------------------------
TestArray.addTest(
"Verify that ECDH import fails with a key with a mismatched 'crv' field",
function() {
var that = this;
var alg = { name: "ECDH", namedCurve: "P-521"};
crypto.subtle.importKey("jwk", tv.ecdsa_jwk_crv_mismatch.pub_jwk, alg, true, ["verify"])
.then(error(that), complete(that));
}
);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TestArray.addTest( TestArray.addTest(
"JWK import an ECDH public and private key and derive bits (P-256)", "JWK import an ECDH public and private key and derive bits (P-256)",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH" }; var alg = { name: "ECDH", namedCurve: "P-256" };
var pubKey, privKey; var pubKey, privKey;
function setPub(x) { pubKey = x; } function setPub(x) { pubKey = x; }
@ -182,7 +194,7 @@ TestArray.addTest(
"JWK import an ECDH public and private key and derive bits (P-384)", "JWK import an ECDH public and private key and derive bits (P-384)",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH" }; var alg = { name: "ECDH", namedCurve: "P-384"};
var pubKey, privKey; var pubKey, privKey;
function setPub(x) { pubKey = x; } function setPub(x) { pubKey = x; }
@ -207,7 +219,7 @@ TestArray.addTest(
"JWK import an ECDH public and private key and derive bits (P-521)", "JWK import an ECDH public and private key and derive bits (P-521)",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH" }; var alg = { name: "ECDH", namedCurve : "P-521" };
var pubKey, privKey; var pubKey, privKey;
function setPub(x) { pubKey = x; } function setPub(x) { pubKey = x; }
@ -232,7 +244,7 @@ TestArray.addTest(
"JWK import/export roundtrip with ECDH (P-256)", "JWK import/export roundtrip with ECDH (P-256)",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH" }; var alg = { name: "ECDH", namedCurve : "P-256" };
var pubKey, privKey; var pubKey, privKey;
function setPub(x) { pubKey = x; } function setPub(x) { pubKey = x; }
@ -296,7 +308,7 @@ TestArray.addTest(
"Test that importing bad JWKs fails", "Test that importing bad JWKs fails",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH" }; var alg = { name: "ECDH", namedCurve: "P-256" };
var tvs = tv.ecdh_p256_negative; var tvs = tv.ecdh_p256_negative;
function doTryImport(jwk) { function doTryImport(jwk) {
@ -306,6 +318,7 @@ TestArray.addTest(
} }
doTryImport(tvs.jwk_bad_crv)() doTryImport(tvs.jwk_bad_crv)()
.then(error(that), doTryImport(tvs.jwk_different_crv))
.then(error(that), doTryImport(tvs.jwk_missing_crv)) .then(error(that), doTryImport(tvs.jwk_missing_crv))
.then(error(that), doTryImport(tvs.jwk_missing_x)) .then(error(that), doTryImport(tvs.jwk_missing_x))
.then(error(that), doTryImport(tvs.jwk_missing_y)) .then(error(that), doTryImport(tvs.jwk_missing_y))
@ -349,7 +362,7 @@ TestArray.addTest(
"Derive an HMAC key from two ECDH keys and test sign/verify", "Derive an HMAC key from two ECDH keys and test sign/verify",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH" }; var alg = { name: "ECDH", namedCurve: "P-521" };
var algDerived = { name: "HMAC", hash: {name: "SHA-1"} }; var algDerived = { name: "HMAC", hash: {name: "SHA-1"} };
var pubKey, privKey; var pubKey, privKey;
@ -396,10 +409,11 @@ TestArray.addTest(
"Derive an HKDF key from two ECDH keys and derive an HMAC key from that", "Derive an HKDF key from two ECDH keys and derive an HMAC key from that",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH", namedCurve: "P-256" };
async function doTest() { async function doTest() {
let privKey = await crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, "ECDH", false, ["deriveKey"]); let privKey = await crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_priv, alg, false, ["deriveKey"]);
let pubKey = await crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_pub, "ECDH", false, []); let pubKey = await crypto.subtle.importKey("jwk", tv.ecdh_p256.jwk_pub, alg, false, []);
let ecdhAlg = { name: "ECDH", public: pubKey }; let ecdhAlg = { name: "ECDH", public: pubKey };
let hkdfAlg = { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(), info: new Uint8Array() }; let hkdfAlg = { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(), info: new Uint8Array() };
let hkdfKey = await crypto.subtle.deriveKey(ecdhAlg, privKey, hkdfAlg, false, ["deriveKey"]); let hkdfKey = await crypto.subtle.deriveKey(ecdhAlg, privKey, hkdfAlg, false, ["deriveKey"]);
@ -454,7 +468,7 @@ TestArray.addTest(
"SPKI/JWK import ECDH keys (P-256) and derive a known secret", "SPKI/JWK import ECDH keys (P-256) and derive a known secret",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDH" }; var alg = { name: "ECDH", namedCurve: "P-256" };
var pubKey, privKey; var pubKey, privKey;
function setPub(x) { pubKey = x; } function setPub(x) { pubKey = x; }

View file

@ -91,7 +91,7 @@ TestArray.addTest(
"ECDSA JWK import and reject a known-bad signature", "ECDSA JWK import and reject a known-bad signature",
function() { function() {
var that = this; var that = this;
var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" }; var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
function doVerify(x) { function doVerify(x) {
return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig_tampered, return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig_tampered,
@ -141,6 +141,18 @@ TestArray.addTest(
} }
); );
// -----------------------------------------------------------------------------
TestArray.addTest(
"Verify that ECDSA import fails with a key with a mismatched 'crv' field",
function() {
var that = this;
var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
crypto.subtle.importKey("jwk", tv.ecdsa_jwk_crv_mismatch.pub_jwk, alg, true, ["verify"])
.then(error(that), complete(that));
}
);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TestArray.addTest( TestArray.addTest(
"Verify that ECDSA import fails with a known-bad public key", "Verify that ECDSA import fails with a known-bad public key",