--- a/security/manager/ssl/ContentSignatureVerifier.cpp
+++ b/security/manager/ssl/ContentSignatureVerifier.cpp
@@ -37,22 +37,23 @@ ContentSignatureVerifier::~ContentSignat
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
destructorSafeDestroyNSSReference();
shutdown(calledFromObject);
}
-nsresult
+NS_IMETHODIMP
ContentSignatureVerifier::VerifyContentSignature(
const nsACString& aData, const nsACString& aCSHeader,
- const nsACString& aCertChain, const uint32_t aSource, bool* _retval)
+ const nsACString& aCertChain, const nsACString& aName, bool* _retval)
{
- nsresult rv = CreateContext(aData, aCSHeader, aCertChain, aSource);
+ NS_ENSURE_ARG(_retval);
+ nsresult rv = CreateContext(aData, aCSHeader, aCertChain, aName);
if (NS_FAILED(rv)) {
*_retval = false;
CSVerifier_LOG(("CSVerifier: Signature verification failed\n"));
if (rv == NS_ERROR_INVALID_SIGNATURE) {
return NS_OK;
}
return rv;
}
@@ -120,23 +121,24 @@ ReadChainIntoCertList(const nsACString&
// the PEM data did not end; bad data.
CSVerifier_LOG(("CSVerifier: supplied chain contains bad data\n"));
return NS_ERROR_FAILURE;
}
return NS_OK;
}
// Create a context for a content signature verification.
-// It sets signature, certificate chain, and context that shold be used to
-// verify the data. The optional data parameter is added to the data to verify.
+// It sets signature, certificate chain and name that should be used to verify
+// the data. The data parameter is the first part of the data to verify (this
+// can be the empty string).
NS_IMETHODIMP
ContentSignatureVerifier::CreateContext(const nsACString& aData,
const nsACString& aCSHeader,
const nsACString& aCertChain,
- const uint32_t aSource)
+ const nsACString& aName)
{
MutexAutoLock lock(mMutex);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
return NS_ERROR_FAILURE;
}
@@ -181,34 +183,20 @@ ContentSignatureVerifier::CreateContext(
nullptr/*stapledOCSPResponse*/);
if (result != Success) {
// the chain is bad
CSVerifier_LOG(("CSVerifier: The supplied chain is bad\n"));
return NS_ERROR_INVALID_SIGNATURE;
}
// Check the SAN
- nsAutoCString hostname;
-
Input hostnameInput;
- switch (aSource) {
- case ABOUT_NEWTAB:
- hostname = "remote-newtab-signer.mozilla.org";
- break;
- case ONECRL:
- hostname = "oneCRL-signer.mozilla.org";
- break;
- default:
- CSVerifier_LOG(("CSVerifier: bad context\n"));
- return NS_ERROR_INVALID_ARG;
- }
-
- result = hostnameInput.Init(uint8_t_ptr_cast(hostname.BeginReading()),
- hostname.Length());
+ result = hostnameInput.Init(uint8_t_ptr_cast(aName.BeginReading()),
+ aName.Length());
if (result != Success) {
return NS_ERROR_FAILURE;
}
BRNameMatchingPolicy nameMatchingPolicy(BRNameMatchingPolicy::Mode::Enforce);
result = CheckCertHostname(certDER, hostnameInput, nameMatchingPolicy);
if (result != Success) {
return NS_ERROR_INVALID_SIGNATURE;
--- a/security/manager/ssl/tests/unit/test_content_signing.js
+++ b/security/manager/ssl/tests/unit/test_content_signing.js
@@ -6,16 +6,19 @@
// These tests ensure content signatures are working correctly.
// First, we need to set up some data
const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
const TEST_DATA_DIR = "test_content_signing/";
+const ONECRL_NAME = "oneCRL-signer.mozilla.org";
+const ABOUT_NEWTAB_NAME = "remote-newtab-signer.mozilla.org";
+
function getSignatureVerifier() {
return Cc["@mozilla.org/security/contentsignatureverifier;1"]
.createInstance(Ci.nsIContentSignatureVerifier);
}
function setRoot(filename) {
let cert = constructCertFromFile(filename);
Services.prefs.setCharPref(PREF_SIGNATURE_ROOT, cert.sha256Fingerprint);
@@ -50,77 +53,102 @@ function run_test() {
["onecrl_ee", "int", "root"]);
let oneCRLBadKeyChain = loadChain(TEST_DATA_DIR + "content_signing",
["onecrl_wrong_key_ee", "int", "root"]);
let oneCRLRSAKeyChain = loadChain(TEST_DATA_DIR + "content_signing",
["onecrl_RSA_ee", "int", "root"]);
+ let noSANChain = loadChain(TEST_DATA_DIR + "content_signing",
+ ["onecrl_no_SAN_ee", "int", "root"]);
+
// Check good signatures from good certificates with the correct SAN
let chain1 = oneCRLChain.join("\n");
let verifier = getSignatureVerifier();
- ok(verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
- verifier.ONECRL),
+ ok(verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME),
"A OneCRL signature should verify with the OneCRL chain");
let chain2 = remoteNewTabChain.join("\n");
verifier = getSignatureVerifier();
ok(verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain2,
- verifier.ABOUT_NEWTAB),
+ ABOUT_NEWTAB_NAME),
"A newtab signature should verify with the newtab chain");
// Check a bad signature when a good chain is provided
chain1 = oneCRLChain.join("\n");
verifier = getSignatureVerifier();
- ok(!verifier.verifyContentSignature(DATA, BAD_SIGNATURE, chain1,
- verifier.ONECRL),
+ ok(!verifier.verifyContentSignature(DATA, BAD_SIGNATURE, chain1, ONECRL_NAME),
"A bad signature should not verify");
// Check a good signature from cert with good SAN but a different key than the
// one used to create the signature
let badKeyChain = oneCRLBadKeyChain.join("\n");
verifier = getSignatureVerifier();
ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, badKeyChain,
- verifier.ONECRL),
+ ONECRL_NAME),
"A signature should not verify if the signing key is wrong");
// Check a good signature from cert with good SAN but a different key than the
// one used to create the signature (this time, an RSA key)
let rsaKeyChain = oneCRLBadKeyChain.join("\n");
verifier = getSignatureVerifier();
ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, rsaKeyChain,
- verifier.ONECRL),
+ ONECRL_NAME),
"A signature should not verify if the signing key is wrong (RSA)");
// Check a good signature from cert with good SAN but with chain missing root
let missingRoot = [oneCRLChain[0], oneCRLChain[1]].join("\n");
verifier = getSignatureVerifier();
ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, missingRoot,
- verifier.ONECRL),
+ ONECRL_NAME),
"A signature should not verify if the chain is incomplete (missing root)");
// Check a good signature from cert with good SAN but with no path to root
let missingInt = [oneCRLChain[0], oneCRLChain[2]].join("\n");
verifier = getSignatureVerifier();
ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, missingInt,
- verifier.ONECRL),
+ ONECRL_NAME),
"A signature should not verify if the chain is incomplete (missing int)");
- // Check good signatures from good certificates with incorrect SANs
+ // Check good signatures from good certificates with the wrong SANs
chain1 = oneCRLChain.join("\n");
verifier = getSignatureVerifier();
ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
- verifier.ABOUT_NEWTAB),
- "A OneCRL signature should not verify if the signer has the newtab SAN");
+ ABOUT_NEWTAB_NAME),
+ "A OneCRL signature should not verify if we require the newtab SAN");
chain2 = remoteNewTabChain.join("\n");
verifier = getSignatureVerifier();
ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain2,
- verifier.ONECRL),
- "A newtab signature should not verify if the signer has the OneCRL SAN");
+ ONECRL_NAME),
+ "A newtab signature should not verify if we require the OneCRL SAN");
+
+ // Check good signatures with good chains with some other invalid names
+ verifier = getSignatureVerifier();
+ ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, ""),
+ "A signature should not verify if the SANs do not match an empty name");
+
+ let relatedName = "subdomain." + ONECRL_NAME;
+ verifier = getSignatureVerifier();
+ ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
+ relatedName),
+ "A signature should not verify if the SANs do not match a related name");
+
+ let randomName = "\xb1\x9bU\x1c\xae\xaa3\x19H\xdb\xed\xa1\xa1\xe0\x81\xfb" +
+ "\xb2\x8f\x1cP\xe5\x8b\x9c\xc2s\xd3\x1f\x8e\xbbN";
+ verifier = getSignatureVerifier();
+ ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, randomName),
+ "A signature should not verify if the SANs do not match a random name");
+
+ // check good signatures with chains that have strange or missing SANs
+ chain1 = noSANChain.join("\n");
+ verifier = getSignatureVerifier();
+ ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
+ ONECRL_NAME),
+ "A signature should not verify if the SANs do not match a supplied name");
// Check malformed signature data
chain1 = oneCRLChain.join("\n");
let bad_signatures = [
// wrong length
"p384ecdsa=WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2rUWM4GJke4pE8ecHiXoi-" +
"7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1Gq25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L==",
// incorrectly encoded
@@ -134,18 +162,17 @@ function run_test() {
"6ob3l3gCTXrsMnOXMeht0kPP3wLfVgXbuuO135pQnsv0c-ltRMWLe56Cm4S4Z6E7WWKLPWaj" +
"jhAcG5dZxjffP9g7tuPP4lTUJztyc4d1z_zQZakEG7R0vN7P5_CaX9MiMzP4R7nC3H4Ba6yi" +
"yjlGvsZwJ_C5zDQzWWs95czUbMzbDScEZ_7AWnidw91jZn-fUK3xLb6m-Zb_b4GAqZ-vnXIf" +
"LpLB1Nzal42BQZn7i4rhAldYdcVvy7rOMlsTUb5Zz6vpVW9LCT9lMJ7Sq1xbU-0g=="
];
for (let badSig of bad_signatures) {
throws(() => {
verifier = getSignatureVerifier();
- verifier.verifyContentSignature(DATA, badSig, chain1,
- verifier.ONECRL);
+ verifier.verifyContentSignature(DATA, badSig, chain1, ONECRL_NAME);
}, /NS_ERROR/, `Bad or malformed signature "${badSig}" should be rejected`);
}
// Check malformed and missing certificate chain data
let chainSuffix = [oneCRLChain[1], oneCRLChain[2]].join("\n");
let badChains = [
// no data
"",
@@ -170,66 +197,65 @@ function run_test() {
// ... and as part of a chain with good certificates
badChains.push(badSection + '\n' + chainSuffix);
}
for (let badChain of badChains) {
throws(() => {
verifier = getSignatureVerifier();
verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, badChain,
- verifier.ONECRL);
+ ONECRL_NAME);
}, /NS_ERROR/, `Bad chain data starting "${badChain.substring(0, 80)}" ` +
"should be rejected");
}
// Check the streaming interface works OK when a good chain / data
// combination is provided
chain1 = oneCRLChain.join("\n");
verifier = getSignatureVerifier();
- verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+ verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
verifier.update(DATA);
ok(verifier.end(),
"A good signature should verify using the stream interface");
// Check that the streaming interface works with multiple update calls
verifier = getSignatureVerifier();
- verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+ verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
for (let c of DATA) {
verifier.update(c);
}
ok(verifier.end(),
"A good signature should verify using multiple updates");
// Check that the streaming interface works with multiple update calls and
// some data provided in CreateContext
verifier = getSignatureVerifier();
let start = DATA.substring(0, 5);
let rest = DATA.substring(start.length);
- verifier.createContext(start, GOOD_SIGNATURE, chain1, verifier.ONECRL);
+ verifier.createContext(start, GOOD_SIGNATURE, chain1, ONECRL_NAME);
for (let c of rest) {
verifier.update(c);
}
ok(verifier.end(),
"A good signature should verify using data in CreateContext and updates");
// Check that a bad chain / data combination fails
verifier = getSignatureVerifier();
- verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+ verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
ok(!verifier.end(),
"A bad signature should fail using the stream interface");
// Check that re-creating a context throws ...
verifier = getSignatureVerifier();
- verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+ verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
// ... firstly, creating a context explicitly
throws(() => {
- verifier.createContext(DATA, GOOD_SIGNATURE, chain1, verifier.ONECRL);
+ verifier.createContext(DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME);
}, /NS_ERROR/, "Ensure a verifier cannot be re-used with createContext");
// ... secondly, by calling verifyContentSignature
throws(() => {
- verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
- verifier.ONECRL);
+ verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME);
}, /NS_ERROR/, "Ensure a verifier cannot be re-used with verifyContentSignature");
run_next_test();
}