[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor-browser/tor-browser-31.2.0esr-4.5-1] Bug #11955 Backport certificate pinning
commit cd9887977227df33462e755200ac6f6ade351bea
Author: Camilo Viecco <cviecco@xxxxxxxxxxx>
Date: Thu Jun 20 10:35:43 2013 -0700
Bug #11955 Backport certificate pinning
Includes the following Mozilla patches, some modified for Tor Browser:
Bug 744204 - Allow Key pining part 1 - Built-in Pinning Service. r=keeler
Bug 744204 - Allow Certificate key pinning Part 2 - Certverifier Interface. r=keeler
--HG--
extra : rebase_source : 2f9748ba0b241c697e22b7ff72f2f5a0fad4a2ca
Bug 998057: Add test pinset to the pin generator (r=cviecco)
--HG--
rename : security/manager/ssl/tests/unit/tlsserver/default-ee.der => security/manager/boot/src/default-ee.der
Bug 998057: Add tests for certificate pinning (r=cviecco,dkeeler)
Bug 1002696 - Minimum set of changes to make genHPKPStaticPins.js productionizable. r=cviecco, dkeeler
--HG--
rename : security/manager/boot/src/PreloadedHPKPins.json => security/manager/tools/PreloadedHPKPins.json
rename : security/manager/boot/src/genHPKPStaticPins.js => security/manager/tools/genHPKPStaticPins.js
Bug 951315 - Add telemetry to PK pinning. r=dkeeler
Bug 1006107 - Disable pining by default, setup pinning for *.addons.mozilla.org. r=dkeeler
Tor project: only patching two files:
security/manager/ssl/src/nsNSSComponent.cpp
netwerk/base/public/security-prefs.js
--HG--
extra : rebase_source : 93b1dbd5dc31490424060729a3941deffa8ee1d5
Bug 772756: Implement sha1 support, import Chrome's pinsets wholesale, add test mode (r=cviecco,keeler)
Tor project, we only patch:
security/manager/ssl/tests/unit/test_pinning.js
security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
security/manager/ssl/tests/unit/tlsserver/default-ee.der
security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
security/manager/ssl/tests/unit/tlsserver/test-ca.der
Bug 1009720: Telemetry for CERT_PINNING_TEST_RESULTS (r=keeler)
Bug 1007844: Implement per-host telemetry for pin violations for AMO and aus4 (r=keeler)
Only patching toolkit/components/telemetry/Histograms.json
Bug 1011269: Add CertVerifier::pinningEnforceTestMode (r=keeler)
Tor project, only commit:
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/manager/ssl/src/nsNSSComponent.cpp
Bug 1012882: Restrict pinning to desktop (r=keeler)
Tor Bug #11955: Backport certificate pinning
Bring the following files up to date:
security/manager/boot/src/PublicKeyPinningService.cpp
security/manager/boot/src/PublicKeyPinningService.h
security/manager/boot/src/StaticHPKPins.h
security/manager/ssl/tests/unit/test_pinning.js
security/manager/tools/PreloadedHPKPins.json
security/manager/tools/genHPKPStaticPins.js
security/pkix/include/pkix/Time.h
security/pkix/lib/pkixtime.cpp
---
.gitignore | 2 +-
browser/app/profile/firefox.js | 3 +
modules/libpref/src/init/all.js | 3 +
security/apps/AppTrustDomain.h | 2 +
security/certverifier/CertVerifier.cpp | 225 +++-
security/certverifier/CertVerifier.h | 17 +-
security/certverifier/NSSCertDBTrustDomain.cpp | 35 +-
security/certverifier/NSSCertDBTrustDomain.h | 6 +-
security/certverifier/moz.build | 1 +
.../manager/boot/src/PublicKeyPinningService.cpp | 306 ++++++
.../boot/src/PublicKeyPinningService.cpp.rej | 11 +
.../manager/boot/src/PublicKeyPinningService.h | 35 +
security/manager/boot/src/StaticHPKPins.h | 1095 ++++++++++++++++++++
security/manager/boot/src/moz.build | 6 +
.../manager/ssl/src/SSLServerCertVerification.cpp | 5 +-
security/manager/ssl/src/SharedCertVerifier.h | 6 +-
security/manager/ssl/src/nsCMS.cpp | 5 +-
security/manager/ssl/src/nsNSSCertificate.cpp | 13 +-
security/manager/ssl/src/nsNSSCertificateDB.cpp | 19 +-
security/manager/ssl/src/nsNSSComponent.cpp | 12 +-
security/manager/ssl/src/nsUsageArrayHelper.cpp | 5 +-
security/manager/ssl/tests/unit/head_psm.js | 1 +
.../manager/ssl/tests/unit/test_cert_overrides.js | 2 +-
security/manager/ssl/tests/unit/test_pinning.js | 182 ++++
security/manager/ssl/tests/unit/tlsserver/cert8.db | Bin 65536 -> 65536 bytes
.../ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp | 11 +
.../ssl/tests/unit/tlsserver/default-ee.der | Bin 527 -> 639 bytes
.../ssl/tests/unit/tlsserver/generate_certs.sh | 17 +-
security/manager/ssl/tests/unit/tlsserver/key3.db | Bin 49152 -> 57344 bytes
.../ssl/tests/unit/tlsserver/other-test-ca.der | Bin 452 -> 452 bytes
.../manager/ssl/tests/unit/tlsserver/secmod.db | Bin 16384 -> 16384 bytes
.../manager/ssl/tests/unit/tlsserver/test-ca.der | Bin 440 -> 440 bytes
security/manager/ssl/tests/unit/xpcshell.ini | 4 +
security/manager/tools/PreloadedHPKPins.json | 247 +++++
security/manager/tools/genHPKPStaticPins.js | 576 ++++++++++
security/pkix/include/pkix/Result.h | 174 ++++
security/pkix/include/pkix/Time.h | 126 +++
security/pkix/include/pkix/pkixtypes.h | 5 +
security/pkix/lib/pkixbuild.cpp | 24 +
security/pkix/lib/pkixtime.cpp | 70 ++
toolkit/components/telemetry/Histograms.json | 32 +
41 files changed, 3198 insertions(+), 85 deletions(-)
diff --git a/.gitignore b/.gitignore
index 8df1754..6d17511 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,7 @@ ID
.*.sw[a-z]
# User files that may appear at the root
-/.mozconfig*
+#/.mozconfig*
/mozconfig
/configure
/config.cache
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 2ec7f9a..b61756d 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1484,6 +1484,9 @@ pref("security.csp.speccompliant", true);
// Block insecure active content on https pages
pref("security.mixed_content.block_active_content", true);
+// 1 = allow MITM for certificate pinning checks.
+pref("security.cert_pinning.enforcement_level", 1);
+
// Override the Gecko-default value of false for Firefox.
pref("plain_text.wrap_long_lines", true);
diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js
index 8efad8f..8a389e3 100644
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1566,6 +1566,9 @@ pref("security.csp.experimentalEnabled", false);
pref("security.mixed_content.block_active_content", false);
pref("security.mixed_content.block_display_content", false);
+// Disable pinning checks by default.
+pref("security.cert_pinning.enforcement_level", 0);
+
// Modifier key prefs: default to Windows settings,
// menu access key = alt, accelerator key = control.
// Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js
diff --git a/security/apps/AppTrustDomain.h b/security/apps/AppTrustDomain.h
index 875c1db..d7e4734 100644
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -35,6 +35,8 @@ public:
/*const*/ CERTCertificate* issuerCertToDup,
PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse);
+ SECStatus IsChainValid(const CERTCertList* certChain) { return SECSuccess; }
+
private:
void* mPinArg; // non-owning!
mozilla::pkix::ScopedCERTCertificate mTrustedRoot;
diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp
index b8b84d7..b2db9fc 100644
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -11,9 +11,11 @@
#include "pkix/pkix.h"
#include "ExtendedValidation.h"
#include "NSSCertDBTrustDomain.h"
+#include "PublicKeyPinningService.h"
#include "cert.h"
#include "ocsp.h"
#include "secerr.h"
+#include "pk11pub.h"
#include "prerror.h"
#include "sslerr.h"
@@ -38,7 +40,8 @@ CertVerifier::CertVerifier(implementation_config ic,
#endif
ocsp_download_config odc,
ocsp_strict_config osc,
- ocsp_get_config ogc)
+ ocsp_get_config ogc,
+ pinning_enforcement_config pel)
: mImplementation(ic)
#ifndef NSS_NO_LIBPKIX
, mMissingCertDownloadEnabled(mcdc == missing_cert_download_on)
@@ -47,6 +50,7 @@ CertVerifier::CertVerifier(implementation_config ic,
, mOCSPDownloadEnabled(odc == ocsp_on)
, mOCSPStrict(osc == ocsp_strict)
, mOCSPGETEnabled(ogc == ocsp_get_enabled)
+ , mPinningEnforcementLevel(pel)
{
}
@@ -64,7 +68,6 @@ InitCertVerifierLog()
#endif
}
-#if 0
// Once we migrate to mozilla::pkix or change the overridable error
// logic this will become unnecesary.
static SECStatus
@@ -95,23 +98,102 @@ insertErrorIntoVerifyLog(CERTCertificate* cert, const PRErrorCode err,
return SECSuccess;
}
-#endif
+
+SECStatus
+IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
+ result = false;
+ ScopedPtr<PK11SlotList, PK11_FreeSlotList> slots;
+ slots = PK11_GetAllSlotsForCert(cert, nullptr);
+ if (!slots) {
+ if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
+ // no list
+ return SECSuccess;
+ }
+ return SECFailure;
+ }
+ for (PK11SlotListElement* le = slots->head; le; le = le->next) {
+ char* token = PK11_GetTokenName(le->slot);
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("BuiltInRoot? subject=%s token=%s",cert->subjectName, token));
+ if (strcmp("Builtin Object Token", token) == 0) {
+ result = true;
+ return SECSuccess;
+ }
+ }
+ return SECSuccess;
+}
+
+struct ChainValidationCallbackState
+{
+ const char* hostname;
+ const CertVerifier::pinning_enforcement_config pinningEnforcementLevel;
+ const SECCertificateUsage usage;
+ const PRTime time;
+};
SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
PRBool* chainOK)
{
+ ChainValidationCallbackState* callbackState =
+ reinterpret_cast<ChainValidationCallbackState*>(state);
+
*chainOK = PR_FALSE;
- PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Inside the Callback \n"));
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Inside the Callback \n"));
// On sanity failure we fail closed.
if (!certList) {
- PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Short circuit, callback, "
- "sanity check failed \n"));
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Short circuit, callback, sanity check failed \n"));
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return SECFailure;
+ }
+ if (!callbackState) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Short circuit, callback, no state! \n"));
PR_SetError(PR_INVALID_STATE_ERROR, 0);
return SECFailure;
}
- *chainOK = PR_TRUE;
+
+ if (callbackState->usage != certificateUsageSSLServer ||
+ callbackState->pinningEnforcementLevel == CertVerifier::pinningDisabled) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Callback shortcut pel=%d \n",
+ callbackState->pinningEnforcementLevel));
+ *chainOK = PR_TRUE;
+ return SECSuccess;
+ }
+
+ for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
+ !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ CERTCertificate* currentCert = node->cert;
+ if (CERT_LIST_END(CERT_LIST_NEXT(node), certList)) {
+ bool isBuiltInRoot = false;
+ SECStatus srv = IsCertBuiltInRoot(currentCert, isBuiltInRoot);
+ if (srv != SECSuccess) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Is BuiltInRoot failure"));
+ return srv;
+ }
+ // If desired, the user can enable "allow user CA MITM mode", in which
+ // case key pinning is not enforced for certificates that chain to trust
+ // anchors that are not in Mozilla's root program
+ if (!isBuiltInRoot &&
+ (callbackState->pinningEnforcementLevel ==
+ CertVerifier::pinningAllowUserCAMITM)) {
+ *chainOK = PR_TRUE;
+ return SECSuccess;
+ }
+ }
+ }
+
+ const bool enforceTestMode = (callbackState->pinningEnforcementLevel ==
+ CertVerifier::pinningEnforceTestMode);
+ *chainOK = PublicKeyPinningService::
+ ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
+ enforceTestMode);
+
return SECSuccess;
}
@@ -120,42 +202,41 @@ ClassicVerifyCert(CERTCertificate* cert,
const SECCertificateUsage usage,
const PRTime time,
void* pinArg,
+ ChainValidationCallbackState* callbackState,
/*optional out*/ ScopedCERTCertList* validationChain,
/*optional out*/ CERTVerifyLog* verifyLog)
{
SECStatus rv;
SECCertUsage enumUsage;
- if (validationChain) {
- switch(usage){
- case certificateUsageSSLClient:
- enumUsage = certUsageSSLClient;
- break;
- case certificateUsageSSLServer:
- enumUsage = certUsageSSLServer;
- break;
- case certificateUsageSSLCA:
- enumUsage = certUsageSSLCA;
- break;
- case certificateUsageEmailSigner:
- enumUsage = certUsageEmailSigner;
- break;
- case certificateUsageEmailRecipient:
- enumUsage = certUsageEmailRecipient;
- break;
- case certificateUsageObjectSigner:
- enumUsage = certUsageObjectSigner;
- break;
- case certificateUsageVerifyCA:
- enumUsage = certUsageVerifyCA;
- break;
- case certificateUsageStatusResponder:
- enumUsage = certUsageStatusResponder;
- break;
- default:
- PR_NOT_REACHED("unexpected usage");
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
- }
+ switch (usage) {
+ case certificateUsageSSLClient:
+ enumUsage = certUsageSSLClient;
+ break;
+ case certificateUsageSSLServer:
+ enumUsage = certUsageSSLServer;
+ break;
+ case certificateUsageSSLCA:
+ enumUsage = certUsageSSLCA;
+ break;
+ case certificateUsageEmailSigner:
+ enumUsage = certUsageEmailSigner;
+ break;
+ case certificateUsageEmailRecipient:
+ enumUsage = certUsageEmailRecipient;
+ break;
+ case certificateUsageObjectSigner:
+ enumUsage = certUsageObjectSigner;
+ break;
+ case certificateUsageVerifyCA:
+ enumUsage = certUsageVerifyCA;
+ break;
+ case certificateUsageStatusResponder:
+ enumUsage = certUsageStatusResponder;
+ break;
+ default:
+ PR_NOT_REACHED("unexpected usage");
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
}
if (usage == certificateUsageSSLServer) {
// SSL server cert verification has always used CERT_VerifyCert, so we
@@ -168,13 +249,38 @@ ClassicVerifyCert(CERTCertificate* cert,
rv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert, true,
usage, time, pinArg, verifyLog, nullptr);
}
- if (rv == SECSuccess && validationChain) {
- PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: getting chain in 'classic' \n"));
- *validationChain = CERT_GetCertChainFromCert(cert, time, enumUsage);
- if (!*validationChain) {
- rv = SECFailure;
+
+ if (rv == SECSuccess &&
+ (validationChain || usage == certificateUsageSSLServer)) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("VerifyCert: getting chain in 'classic' \n"));
+ ScopedCERTCertList certChain(CERT_GetCertChainFromCert(cert, time,
+ enumUsage));
+ if (!certChain) {
+ return SECFailure;
+ }
+ if (usage == certificateUsageSSLServer) {
+ PRBool chainOK = PR_FALSE;
+ SECStatus srv = chainValidationCallback(callbackState, certChain.get(),
+ &chainOK);
+ if (srv != SECSuccess) {
+ return srv;
+ }
+ if (chainOK != PR_TRUE) {
+ if (verifyLog) {
+ insertErrorIntoVerifyLog(cert,
+ SEC_ERROR_APPLICATION_CALLBACK_ERROR,
+ verifyLog);
+ }
+ PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
+ return SECFailure;
+ }
+ }
+ if (rv == SECSuccess && validationChain) {
+ *validationChain = certChain.release();
}
}
+
return rv;
}
@@ -227,6 +333,7 @@ CertVerifier::MozillaPKIXVerifyCert(
const PRTime time,
void* pinArg,
const Flags flags,
+ ChainValidationCallbackState* callbackState,
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
/*optional out*/ SECOidTag* evOidPolicy)
@@ -249,6 +356,10 @@ CertVerifier::MozillaPKIXVerifyCert(
return SECFailure;
}
+ CERTChainVerifyCallback callbackContainer;
+ callbackContainer.isChainValid = chainValidationCallback;
+ callbackContainer.isChainValidArg = callbackState;
+
NSSCertDBTrustDomain::OCSPFetching ocspFetching
= !mOCSPDownloadEnabled ||
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
@@ -295,7 +406,7 @@ CertVerifier::MozillaPKIXVerifyCert(
ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
: NSSCertDBTrustDomain::FetchOCSPForEV,
- mOCSPCache, pinArg);
+ mOCSPCache, pinArg, &callbackContainer);
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
KeyUsage::digitalSignature, // ECDHE/DHE
KeyUsage::keyEncipherment, // RSA
@@ -321,7 +432,7 @@ CertVerifier::MozillaPKIXVerifyCert(
// Now try non-EV.
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, &callbackContainer);
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
KeyUsage::digitalSignature, // (EC)DHE
KeyUsage::keyEncipherment, // RSA
@@ -443,19 +554,25 @@ CertVerifier::MozillaPKIXVerifyCert(
SECStatus
CertVerifier::VerifyCert(CERTCertificate* cert,
- /*optional*/ const SECItem* stapledOCSPResponse,
const SECCertificateUsage usage,
const PRTime time,
void* pinArg,
+ const char* hostname,
const Flags flags,
+ /*optional in*/ const SECItem* stapledOCSPResponse,
/*optional out*/ ScopedCERTCertList* validationChain,
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ CERTVerifyLog* verifyLog)
{
+ ChainValidationCallbackState callbackState = { hostname,
+ mPinningEnforcementLevel,
+ usage,
+ time };
+
if (mImplementation == mozillapkix) {
return MozillaPKIXVerifyCert(cert, usage, time, pinArg, flags,
- stapledOCSPResponse, validationChain,
- evOidPolicy);
+ &callbackState, stapledOCSPResponse,
+ validationChain, evOidPolicy);
}
if (!cert)
@@ -581,7 +698,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
CERTChainVerifyCallback callbackContainer;
if (usage == certificateUsageSSLServer) {
callbackContainer.isChainValid = chainValidationCallback;
- callbackContainer.isChainValidArg = nullptr;
+ callbackContainer.isChainValidArg = &callbackState;
cvin[i].type = cert_pi_chainVerifyCallback;
cvin[i].value.pointer.chainVerifyCallback = &callbackContainer;
++i;
@@ -685,8 +802,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
if (mImplementation == classic) {
// XXX: we do not care about the localOnly flag (currently) as the
// caller that wants localOnly should disable and reenable the fetching.
- return ClassicVerifyCert(cert, usage, time, pinArg, validationChain,
- verifyLog);
+ return ClassicVerifyCert(cert, usage, time, pinArg, &callbackState,
+ validationChain, verifyLog);
}
#ifdef NSS_NO_LIBPKIX
@@ -826,9 +943,9 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
// CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
// if VerifyCert succeeded.
ScopedCERTCertList validationChain;
- SECStatus rv = VerifyCert(peerCert, stapledOCSPResponse,
- certificateUsageSSLServer, time,
- pinarg, 0, &validationChain, evOidPolicy);
+ SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
+ hostname, 0, stapledOCSPResponse, &validationChain,
+ evOidPolicy, nullptr);
if (rv != SECSuccess) {
return rv;
}
diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h
index 09ed4b0..270e9a1 100644
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -12,6 +12,8 @@
namespace mozilla { namespace psm {
+struct ChainValidationCallbackState;
+
class CertVerifier
{
public:
@@ -24,11 +26,12 @@ public:
// *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
// Only one usage per verification is supported.
SECStatus VerifyCert(CERTCertificate* cert,
- /*optional*/ const SECItem* stapledOCSPResponse,
const SECCertificateUsage usage,
const PRTime time,
void* pinArg,
+ const char* hostname,
const Flags flags = 0,
+ /*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
/*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain = nullptr,
/*optional out*/ SECOidTag* evOidPolicy = nullptr ,
/*optional out*/ CERTVerifyLog* verifyLog = nullptr);
@@ -52,6 +55,13 @@ public:
mozillapkix = 2
};
+ enum pinning_enforcement_config {
+ pinningDisabled = 0,
+ pinningAllowUserCAMITM = 1,
+ pinningStrict = 2,
+ pinningEnforceTestMode = 3
+ };
+
enum missing_cert_download_config { missing_cert_download_off = 0, missing_cert_download_on };
enum crl_download_config { crl_local_only = 0, crl_download_allowed };
enum ocsp_download_config { ocsp_off = 0, ocsp_on };
@@ -65,7 +75,8 @@ public:
missing_cert_download_config ac, crl_download_config cdc,
#endif
ocsp_download_config odc, ocsp_strict_config osc,
- ocsp_get_config ogc);
+ ocsp_get_config ogc,
+ pinning_enforcement_config pinningEnforcementLevel);
~CertVerifier();
void ClearOCSPCache() { mOCSPCache.Clear(); }
@@ -78,6 +89,7 @@ public:
const bool mOCSPDownloadEnabled;
const bool mOCSPStrict;
const bool mOCSPGETEnabled;
+ const pinning_enforcement_config mPinningEnforcementLevel;
private:
SECStatus MozillaPKIXVerifyCert(CERTCertificate* cert,
@@ -85,6 +97,7 @@ private:
const PRTime time,
void* pinArg,
const Flags flags,
+ ChainValidationCallbackState* callbackState,
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
/*optional out*/ SECOidTag* evOidPolicy);
diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp
index 9bed2ce..fd2e363 100644
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -43,11 +43,13 @@ typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
OCSPFetching ocspFetching,
OCSPCache& ocspCache,
- void* pinArg)
+ void* pinArg,
+ CERTChainVerifyCallback* checkChainCallback)
: mCertDBTrustType(certDBTrustType)
, mOCSPFetching(ocspFetching)
, mOCSPCache(ocspCache)
, mPinArg(pinArg)
+ , mCheckChainCallback(checkChainCallback)
{
}
@@ -475,6 +477,37 @@ NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
return rv;
}
+SECStatus
+NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) {
+ SECStatus rv = SECFailure;
+
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("NSSCertDBTrustDomain: Top of IsChainValid mCheckCallback=%p",
+ mCheckChainCallback));
+
+ if (!mCheckChainCallback) {
+ return SECSuccess;
+ }
+ if (!mCheckChainCallback->isChainValid) {
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return SECFailure;
+ }
+ PRBool chainOK;
+ rv = (mCheckChainCallback->isChainValid)(mCheckChainCallback->isChainValidArg,
+ certChain, &chainOK);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ // rv = SECSuccess only implies successful call, now is time
+ // to check the chain check status
+ // we should only return success if the chain is valid
+ if (chainOK) {
+ return SECSuccess;
+ }
+ PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0);
+ return SECFailure;
+}
+
namespace {
static char*
diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h
index 979c3e2..c2f211d 100644
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -57,7 +57,8 @@ public:
LocalOnlyOCSPForEV = 4,
};
NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
- OCSPCache& ocspCache, void* pinArg);
+ OCSPCache& ocspCache, void* pinArg,
+ CERTChainVerifyCallback* checkChainCallback = nullptr);
virtual SECStatus FindPotentialIssuers(
const SECItem* encodedIssuerName,
@@ -78,6 +79,8 @@ public:
PRTime time,
/*optional*/ const SECItem* stapledOCSPResponse);
+ virtual SECStatus IsChainValid(const CERTCertList* certChain);
+
private:
enum EncodedResponseSource {
ResponseIsFromNetwork = 1,
@@ -93,6 +96,7 @@ private:
const OCSPFetching mOCSPFetching;
OCSPCache& mOCSPCache; // non-owning!
void* mPinArg; // non-owning!
+ CERTChainVerifyCallback* mCheckChainCallback; // non-owning!
};
} } // namespace mozilla::psm
diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build
index 5e99b12..434c3cc 100644
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -17,6 +17,7 @@ if not CONFIG['NSS_NO_EV_CERTS']:
]
LOCAL_INCLUDES += [
+ '../manager/boot/src',
'../manager/ssl/src',
'../pkix/include',
]
diff --git a/security/manager/boot/src/PublicKeyPinningService.cpp b/security/manager/boot/src/PublicKeyPinningService.cpp
new file mode 100644
index 0000000..82abaaf
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.cpp
@@ -0,0 +1,306 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PublicKeyPinningService.h"
+#include "pkix/nullptr.h"
+#include "StaticHPKPins.h" // autogenerated by genHPKPStaticpins.js
+
+#include "cert.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Telemetry.h"
+#include "nsString.h"
+#include "nssb64.h"
+#include "pkix/pkixtypes.h"
+#include "prlog.h"
+#include "ScopedNSSTypes.h"
+#include "seccomon.h"
+#include "sechash.h"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+using namespace mozilla::psm;
+
+#if defined(PR_LOGGING)
+PRLogModuleInfo* gPublicKeyPinningLog =
+ PR_NewLogModule("PublicKeyPinningService");
+#endif
+
+/**
+ Computes in the location specified by base64Out the SHA256 digest
+ of the DER Encoded subject Public Key Info for the given cert
+*/
+static SECStatus
+GetBase64HashSPKI(const CERTCertificate* cert, SECOidTag hashType,
+ nsACString& hashSPKIDigest)
+{
+ hashSPKIDigest.Truncate();
+ Digest digest;
+ nsresult rv = digest.DigestBuf(hashType, cert->derPublicKey.data,
+ cert->derPublicKey.len);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return SECFailure;
+ }
+ rv = Base64Encode(nsDependentCSubstring(
+ reinterpret_cast<const char*>(digest.get().data),
+ digest.get().len),
+ hashSPKIDigest);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * Returns true if a given cert matches any hashType fingerprints from the
+ * given pinset, false otherwise.
+ */
+static bool
+EvalCertWithHashType(const CERTCertificate* cert, SECOidTag hashType,
+ const StaticFingerprints* fingerprints)
+{
+ if (!fingerprints) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: No hashes found for hash type: %d\n", hashType));
+ return false;
+ }
+
+ nsAutoCString base64Out;
+ SECStatus srv = GetBase64HashSPKI(cert, hashType, base64Out);
+ if (srv != SECSuccess) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: GetBase64HashSPKI failed!\n"));
+ return false;
+ }
+
+ for (size_t i = 0; i < fingerprints->size; i++) {
+ if (base64Out.Equals(fingerprints->data[i])) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: found pin base_64 ='%s'\n", base64Out.get()));
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Returns true if a given chain matches any hashType fingerprints from the
+ * given pinset, false otherwise.
+ */
+static bool
+EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType,
+ const StaticPinset* pinset)
+{
+ CERTCertificate* currentCert;
+
+ const StaticFingerprints* fingerprints = nullptr;
+ if (hashType == SEC_OID_SHA256) {
+ fingerprints = pinset->sha256;
+ } else if (hashType == SEC_OID_SHA1) {
+ fingerprints = pinset->sha1;
+ }
+ if (!fingerprints) {
+ return false;
+ }
+
+ CERTCertListNode* node;
+ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ currentCert = node->cert;
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: certArray subject: '%s'\n",
+ currentCert->subjectName));
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: certArray common_name: '%s'\n",
+ CERT_GetCommonName(&(currentCert->issuer))));
+ if (EvalCertWithHashType(currentCert, hashType, fingerprints)) {
+ return true;
+ }
+ }
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG, ("pkpin: no matches found\n"));
+ return false;
+}
+
+/**
+ * Given a pinset and certlist, return true if one of the certificates on
+ * the list matches a fingerprint in the pinset, false otherwise.
+ */
+static bool
+EvalChainWithPinset(const CERTCertList* certList,
+ const StaticPinset* pinset) {
+ // SHA256 is more trustworthy, try that first.
+ if (EvalChainWithHashType(certList, SEC_OID_SHA256, pinset)) {
+ return true;
+ }
+ return EvalChainWithHashType(certList, SEC_OID_SHA1, pinset);
+}
+
+/**
+ Comparator for the is public key pinned host.
+*/
+static int
+TransportSecurityPreloadCompare(const void *key, const void *entry) {
+ const char *keyStr = reinterpret_cast<const char *>(key);
+ const TransportSecurityPreload *preloadEntry =
+ reinterpret_cast<const TransportSecurityPreload *>(entry);
+
+ return strcmp(keyStr, preloadEntry->mHost);
+}
+
+/**
+ * Check PKPins on the given certlist against the specified hostname
+ */
+static bool
+CheckPinsForHostname(const CERTCertList *certList, const char *hostname,
+ bool enforceTestMode)
+{
+ if (!certList) {
+ return false;
+ }
+ if (!hostname || hostname[0] == 0) {
+ return false;
+ }
+
+ TransportSecurityPreload *foundEntry = nullptr;
+ char *evalHost = const_cast<char*>(hostname);
+ char *evalPart;
+ // Notice how the (xx = strchr) prevents pins for unqualified domain names.
+ while (!foundEntry && (evalPart = strchr(evalHost, '.'))) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Querying pinsets for host: '%s'\n", evalHost));
+ foundEntry = (TransportSecurityPreload *)bsearch(evalHost,
+ kPublicKeyPinningPreloadList,
+ sizeof(kPublicKeyPinningPreloadList) / sizeof(TransportSecurityPreload),
+ sizeof(TransportSecurityPreload),
+ TransportSecurityPreloadCompare);
+ if (foundEntry) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Found pinset for host: '%s'\n", evalHost));
+ if (evalHost != hostname) {
+ if (!foundEntry->mIncludeSubdomains) {
+ // Does not apply to this host, continue iterating
+ foundEntry = nullptr;
+ }
+ }
+ } else {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Didn't find pinset for host: '%s'\n", evalHost));
+ }
+ // Add one for '.'
+ evalHost = evalPart + 1;
+ }
+
+ if (foundEntry && foundEntry->pinset) {
+ bool result = EvalChainWithPinset(certList, foundEntry->pinset);
+ bool retval = result;
+ Telemetry::ID histogram = foundEntry->mIsMoz
+ ? Telemetry::CERT_PINNING_MOZ_RESULTS
+ : Telemetry::CERT_PINNING_RESULTS;
+ if (foundEntry->mTestMode) {
+ histogram = foundEntry->mIsMoz
+ ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS
+ : Telemetry::CERT_PINNING_TEST_RESULTS;
+ if (!enforceTestMode) {
+ retval = true;
+ }
+ }
+ // We can collect per-host pinning violations for this host because it is
+ // operationally critical to Firefox.
+ if (foundEntry->mId != kUnknownId) {
+ int32_t bucket = foundEntry->mId * 2 + (result ? 1 : 0);
+ histogram = foundEntry->mTestMode
+ ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST
+ : Telemetry::CERT_PINNING_MOZ_RESULTS_BY_HOST;
+ Telemetry::Accumulate(histogram, bucket);
+ } else {
+ Telemetry::Accumulate(histogram, result ? 1 : 0);
+ }
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Pin check %s for %s host '%s' (mode=%s)\n",
+ result ? "passed" : "failed",
+ foundEntry->mIsMoz ? "mozilla" : "non-mozilla",
+ hostname, foundEntry->mTestMode ? "test" : "production"));
+ return retval;
+ }
+ return true; // No pinning information for this hostname
+}
+
+/**
+ * Extract all the DNS names for a host (including CN) and evaluate the
+ * certifiate pins against all of them (Currently is an OR so we stop
+ * evaluating at the first OK pin).
+ */
+static bool
+CheckChainAgainstAllNames(const CERTCertList* certList, bool enforceTestMode)
+{
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: top of checkChainAgainstAllNames"));
+ CERTCertListNode* node = CERT_LIST_HEAD(certList);
+ if (!node) {
+ return false;
+ }
+ CERTCertificate* cert = node->cert;
+ if (!cert) {
+ return false;
+ }
+
+ ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!arena) {
+ return false;
+ }
+
+ bool hasValidPins = false;
+ CERTGeneralName* nameList;
+ CERTGeneralName* currentName;
+ nameList = CERT_GetConstrainedCertificateNames(cert, arena.get(), PR_TRUE);
+ if (!nameList) {
+ return false;
+ }
+
+ currentName = nameList;
+ do {
+ if (currentName->type == certDNSName
+ && currentName->name.other.data[0] != 0) {
+ // no need to cleaup, as the arena cleanup will do
+ char *hostName = (char *)PORT_ArenaAlloc(arena.get(),
+ currentName->name.other.len + 1);
+ if (!hostName) {
+ break;
+ }
+ // We use a temporary buffer as the hostname as returned might not be
+ // null terminated.
+ hostName[currentName->name.other.len] = 0;
+ memcpy(hostName, currentName->name.other.data,
+ currentName->name.other.len);
+ if (!hostName[0]) {
+ // cannot call CheckPinsForHostname on empty or null hostname
+ break;
+ }
+ if (CheckPinsForHostname(certList, hostName, enforceTestMode)) {
+ hasValidPins = true;
+ break;
+ }
+ }
+ currentName = CERT_GetNextGeneralName(currentName);
+ } while (currentName != nameList);
+
+ return hasValidPins;
+}
+
+bool
+PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList,
+ const char* hostname,
+ const PRTime time,
+ bool enforceTestMode)
+{
+ if (!certList) {
+ return false;
+ }
+ if (time > kPreloadPKPinsExpirationTime) {
+ return true;
+ }
+ if (!hostname || hostname[0] == 0) {
+ return CheckChainAgainstAllNames(certList, enforceTestMode);
+ }
+ return CheckPinsForHostname(certList, hostname, enforceTestMode);
+}
diff --git a/security/manager/boot/src/PublicKeyPinningService.cpp.rej b/security/manager/boot/src/PublicKeyPinningService.cpp.rej
new file mode 100644
index 0000000..e88f91b
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.cpp.rej
@@ -0,0 +1,11 @@
+diff a/security/manager/boot/src/PublicKeyPinningService.cpp b/security/manager/boot/src/PublicKeyPinningService.cpp (rejected hunks)
+@@ -296,7 +296,8 @@ PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList,
+ if (!certList) {
+ return false;
+ }
+- if (time > TimeFromElapsedSecondsAD(kPreloadPKPinsExpirationTime)) {
++ if (time > TimeFromEpochInSeconds(kPreloadPKPinsExpirationTime /
++ PR_USEC_PER_SEC)) {
+ return true;
+ }
+ if (!hostname || hostname[0] == 0) {
diff --git a/security/manager/boot/src/PublicKeyPinningService.h b/security/manager/boot/src/PublicKeyPinningService.h
new file mode 100644
index 0000000..978c5ec
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.h
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PublicKeyPinningService_h
+#define PublicKeyPinningService_h
+
+#include "cert.h"
+
+namespace mozilla {
+namespace psm {
+
+class PublicKeyPinningService
+{
+public:
+ /**
+ * Returns true if the given (host, certList) passes pinning checks,
+ * false otherwise. If the host is pinned, return true if one of the keys in
+ * the given certificate chain matches the pin set specified by the
+ * hostname. If the hostname is null or empty evaluate against all the
+ * possible names for the EE cert (Common Name (CN) plus all DNS Name:
+ * subject Alt Name entries). The certList's head is the EE cert and the
+ * tail is the trust anchor.
+ * Note: if an alt name is a wildcard, it won't necessarily find a pinset
+ * that would otherwise be valid for it
+ */
+ static bool ChainHasValidPins(const CERTCertList* certList,
+ const char* hostname,
+ const PRTime,
+ bool enforceTestMode);
+};
+
+}} // namespace mozilla::psm
+
+#endif // PublicKeyPinningServiceService_h
diff --git a/security/manager/boot/src/StaticHPKPins.h b/security/manager/boot/src/StaticHPKPins.h
new file mode 100644
index 0000000..4506489
--- /dev/null
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -0,0 +1,1095 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*****************************************************************************/
+/* This is an automatically generated file. If you're not */
+/* PublicKeyPinningService.cpp, you shouldn't be #including it. */
+/*****************************************************************************/
+#include <stdint.h>
+/* AddTrust External Root */
+static const char kAddTrust_External_RootFingerprint[] =
+ "lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=";
+
+/* AddTrust Low-Value Services Root */
+static const char kAddTrust_Low_Value_Services_RootFingerprint[] =
+ "BStocQfshOhzA4JFLsKidFF0XXSFpX1vRk4Np6G2ryo=";
+
+/* AddTrust Public Services Root */
+static const char kAddTrust_Public_Services_RootFingerprint[] =
+ "OGHXtpYfzbISBFb/b8LrdwSxp0G0vZM6g3b14ZFcppg=";
+
+/* AddTrust Qualified Certificates Root */
+static const char kAddTrust_Qualified_Certificates_RootFingerprint[] =
+ "xzr8Lrp3DQy8HuQfJStS6Kk9ErctzOwDHY2DnL+Bink=";
+
+/* AffirmTrust Commercial */
+static const char kAffirmTrust_CommercialFingerprint[] =
+ "bEZLmlsjOl6HTadlwm8EUBDS3c/0V5TwtMfkqvpQFJU=";
+
+/* AffirmTrust Networking */
+static const char kAffirmTrust_NetworkingFingerprint[] =
+ "lAcq0/WPcPkwmOWl9sBMlscQvYSdgxhJGa6Q64kK5AA=";
+
+/* AffirmTrust Premium */
+static const char kAffirmTrust_PremiumFingerprint[] =
+ "x/Q7TPW3FWgpT4IrU3YmBfbd0Vyt7Oc56eLDy6YenWc=";
+
+/* AffirmTrust Premium ECC */
+static const char kAffirmTrust_Premium_ECCFingerprint[] =
+ "MhmwkRT/SVo+tusAwu/qs0ACrl8KVsdnnqCHo/oDfk8=";
+
+/* America Online Root Certification Authority 1 */
+static const char kAmerica_Online_Root_Certification_Authority_1Fingerprint[] =
+ "I4SdCUkj1EpIgbY6sYXpvhWqyO8sMETZNLx/JuLSzWk=";
+
+/* America Online Root Certification Authority 2 */
+static const char kAmerica_Online_Root_Certification_Authority_2Fingerprint[] =
+ "/PfamDYD6IhiAw2WE32OEwMbrftNVsH9TKzDOfa9uyo=";
+
+/* Baltimore CyberTrust Root */
+static const char kBaltimore_CyberTrust_RootFingerprint[] =
+ "Y9mvm0exBk1JoQ57f9Vm28jKo5lFm/woKcVxrYxu80o=";
+
+/* COMODO Certification Authority */
+static const char kCOMODO_Certification_AuthorityFingerprint[] =
+ "AG1751Vd2CAmRCxPGieoDomhmJy4ezREjtIZTBgZbV4=";
+
+/* COMODO ECC Certification Authority */
+static const char kCOMODO_ECC_Certification_AuthorityFingerprint[] =
+ "58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=";
+
+/* Comodo AAA Services root */
+static const char kComodo_AAA_Services_rootFingerprint[] =
+ "vRU+17BDT2iGsXvOi76E7TQMcTLXAqj0+jGPdW7L1vM=";
+
+/* Comodo Secure Services root */
+static const char kComodo_Secure_Services_rootFingerprint[] =
+ "RpHL/ehKa2BS3b4VK7DCFq4lqG5XR4E9vA8UfzOFcL4=";
+
+/* Comodo Trusted Services root */
+static const char kComodo_Trusted_Services_rootFingerprint[] =
+ "4tiR77c4ZpEF1TDeXtcuKyrD9KZweLU0mz/ayklvXrg=";
+
+/* Cybertrust Global Root */
+static const char kCybertrust_Global_RootFingerprint[] =
+ "foeCwVDOOVL4AuY2AjpdPpW7XWjjPoWtsroXgSXOvxU=";
+
+/* DigiCert Assured ID Root CA */
+static const char kDigiCert_Assured_ID_Root_CAFingerprint[] =
+ "I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=";
+
+/* DigiCert ECC Secure Server CA */
+static const char kDigiCert_ECC_Secure_Server_CAFingerprint[] =
+ "PZXN3lRAy+8tBKk2Ox6F7jIlnzr2Yzmwqc3JnyfXoCw=";
+
+/* DigiCert Global Root CA */
+static const char kDigiCert_Global_Root_CAFingerprint[] =
+ "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=";
+
+/* DigiCert High Assurance EV Root CA */
+static const char kDigiCert_High_Assurance_EV_Root_CAFingerprint[] =
+ "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=";
+
+/* End Entity Test Cert */
+static const char kEnd_Entity_Test_CertFingerprint[] =
+ "pVVgLk2kFI2WWRPwDMIX6YmzFhEW4DXQV/U5gP+feGA=";
+
+/* Entrust Root Certification Authority */
+static const char kEntrust_Root_Certification_AuthorityFingerprint[] =
+ "bb+uANN7nNc/j7R95lkXrwDg3d9C286sIMF8AnXuIJU=";
+
+/* Entrust.net Premium 2048 Secure Server CA */
+static const char kEntrust_net_Premium_2048_Secure_Server_CAFingerprint[] =
+ "HqPF5D7WbC2imDpCpKebHpBnhs6fG1hiFBmgBGOofTg=";
+
+/* Equifax Secure CA */
+static const char kEquifax_Secure_CAFingerprint[] =
+ "/1aAzXOlcD2gSBegdf1GJQanNQbEuBoVg+9UlHjSZHY=";
+
+/* Equifax Secure Global eBusiness CA */
+static const char kEquifax_Secure_Global_eBusiness_CAFingerprint[] =
+ "pvH5v4oKndwID7SbHvw9GhwsMtwOE2pbAMlzFvKj3BE=";
+
+/* Equifax Secure eBusiness CA 1 */
+static const char kEquifax_Secure_eBusiness_CA_1Fingerprint[] =
+ "JsGNxu6m9jL2drzrodjCtINS8pwtX82oeOCdy4Mt1uU=";
+
+/* GOOGLE_PIN_AlphaSSL_G2 */
+static const char kGOOGLE_PIN_AlphaSSL_G2Fingerprint[] =
+ "yxgiWGK++SFB9ySwt3M3qpn5HO0ZLFY5D+h+G/vcT/c=";
+
+/* GOOGLE_PIN_CryptoCat1 */
+static const char kGOOGLE_PIN_CryptoCat1Fingerprint[] =
+ "vKaqtTLWmVuXPVJE+0OqN5sRc4VCcSQHI/W3XTDVR24=";
+
+/* GOOGLE_PIN_EntrustRootEC1 */
+static const char kGOOGLE_PIN_EntrustRootEC1Fingerprint[] =
+ "/qK31kX7pz11PB7Jp4cMQOH3sMVh6Se5hb9xGGbjbyI=";
+
+/* GOOGLE_PIN_Entrust_G2 */
+static const char kGOOGLE_PIN_Entrust_G2Fingerprint[] =
+ "du6FkDdMcVQ3u8prumAo6t3i3G27uMP2EOhR8R0at/U=";
+
+/* GOOGLE_PIN_Entrust_SSL */
+static const char kGOOGLE_PIN_Entrust_SSLFingerprint[] =
+ "nsxRNo6G40YPZsKV5JQt1TCA8nseQQr/LRqp1Oa8fnw=";
+
+/* GOOGLE_PIN_GoDaddySecure */
+static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
+ "MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
+
+/* GOOGLE_PIN_Libertylavabitcom */
+static const char kGOOGLE_PIN_LibertylavabitcomFingerprint[] =
+ "WnKzsDXgqPtS1KvtImrhQPqcxfpmfssuI2cSJt4LMks=";
+
+/* GOOGLE_PIN_RapidSSL */
+static const char kGOOGLE_PIN_RapidSSLFingerprint[] =
+ "lT09gPUeQfbYrlxRtpsHrjDblj9Rpz+u7ajfCrg4qDM=";
+
+/* GOOGLE_PIN_Tor2web */
+static const char kGOOGLE_PIN_Tor2webFingerprint[] =
+ "99ogQzjMuUTBkG1ZP7FME0K4kvBEti8Buzu4nZjRItM=";
+
+/* GTE CyberTrust Global Root */
+static const char kGTE_CyberTrust_Global_RootFingerprint[] =
+ "EGn6R6CqT4z3ERscrqNl7q7RC//zJmDe9uBhS/rnCHU=";
+
+/* GeoTrust Global CA */
+static const char kGeoTrust_Global_CAFingerprint[] =
+ "h6801m+z8v3zbgkRHpq6L29Esgfzhj89C1SyUCOQmqU=";
+
+/* GeoTrust Global CA 2 */
+static const char kGeoTrust_Global_CA_2Fingerprint[] =
+ "F3VaXClfPS1y5vAxofB/QAxYi55YKyLxfq4xoVkNEYU=";
+
+/* GeoTrust Primary Certification Authority */
+static const char kGeoTrust_Primary_Certification_AuthorityFingerprint[] =
+ "SQVGZiOrQXi+kqxcvWWE96HhfydlLVqFr4lQTqI5qqo=";
+
+/* GeoTrust Primary Certification Authority - G2 */
+static const char kGeoTrust_Primary_Certification_Authority___G2Fingerprint[] =
+ "vPtEqrmtAhAVcGtBIep2HIHJ6IlnWQ9vlK50TciLePs=";
+
+/* GeoTrust Primary Certification Authority - G3 */
+static const char kGeoTrust_Primary_Certification_Authority___G3Fingerprint[] =
+ "q5hJUnat8eyv8o81xTBIeB5cFxjaucjmelBPT2pRMo8=";
+
+/* GeoTrust Universal CA */
+static const char kGeoTrust_Universal_CAFingerprint[] =
+ "lpkiXF3lLlbN0y3y6W0c/qWqPKC7Us2JM8I7XCdEOCA=";
+
+/* GeoTrust Universal CA 2 */
+static const char kGeoTrust_Universal_CA_2Fingerprint[] =
+ "fKoDRlEkWQxgHlZ+UhSOlSwM/+iQAFMP4NlbbVDqrkE=";
+
+/* GlobalSign Root CA */
+static const char kGlobalSign_Root_CAFingerprint[] =
+ "K87oWBWM9UZfyddvDfoxL+8lpNyoUB2ptGtn0fv6G2Q=";
+
+/* GlobalSign Root CA - R2 */
+static const char kGlobalSign_Root_CA___R2Fingerprint[] =
+ "iie1VXtL7HzAMF+/PVPR9xzT80kQxdZeJ+zduCB3uj0=";
+
+/* GlobalSign Root CA - R3 */
+static const char kGlobalSign_Root_CA___R3Fingerprint[] =
+ "cGuxAXyFXFkWm61cF4HPWX8S0srS9j0aSqN0k4AP+4A=";
+
+/* Go Daddy Class 2 CA */
+static const char kGo_Daddy_Class_2_CAFingerprint[] =
+ "VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=";
+
+/* Go Daddy Root Certificate Authority - G2 */
+static const char kGo_Daddy_Root_Certificate_Authority___G2Fingerprint[] =
+ "Ko8tivDrEjiY90yGasP6ZpBU4jwXvHqVvQI0GS3GNdA=";
+
+/* GoogleBackup2048 */
+static const char kGoogleBackup2048Fingerprint[] =
+ "vq7OyjSnqOco9nyMCDGdy77eijM=";
+
+/* GoogleG2 */
+static const char kGoogleG2Fingerprint[] =
+ "Q9rWMO5T+KmAym79hfRqo3mQ4Oo=";
+
+/* Network Solutions Certificate Authority */
+static const char kNetwork_Solutions_Certificate_AuthorityFingerprint[] =
+ "MtGA7THJNVieydu7ciEjuIO1/C3BD5/KOpXXfhv8tTQ=";
+
+/* Starfield Class 2 CA */
+static const char kStarfield_Class_2_CAFingerprint[] =
+ "FfFKxFycfaIz00eRZOgTf+Ne4POK6FgYPwhBDqgqxLQ=";
+
+/* Starfield Root Certificate Authority - G2 */
+static const char kStarfield_Root_Certificate_Authority___G2Fingerprint[] =
+ "gI1os/q0iEpflxrOfRBVDXqVoWN3Tz7Dav/7IT++THQ=";
+
+/* Starfield Services Root Certificate Authority - G2 */
+static const char kStarfield_Services_Root_Certificate_Authority___G2Fingerprint[] =
+ "KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I=";
+
+/* StartCom Certification Authority */
+static const char kStartCom_Certification_AuthorityFingerprint[] =
+ "5C8kvU039KouVrl52D0eZSGf4Onjo4Khs8tmyTlV3nU=";
+
+/* StartCom Certification Authority G2 */
+static const char kStartCom_Certification_Authority_G2Fingerprint[] =
+ "FSg5faISiQqDCwuVpZlozvI0dzd531GBzxD6ZHU0u2U=";
+
+/* TC TrustCenter Class 2 CA II */
+static const char kTC_TrustCenter_Class_2_CA_IIFingerprint[] =
+ "rPZeHWLLWKK6/W/6tA+4hpnEc5fPXLSD1C1pytNM1Is=";
+
+/* TC TrustCenter Class 3 CA II */
+static const char kTC_TrustCenter_Class_3_CA_IIFingerprint[] =
+ "k5KuIUmSSt435kXbof9L3dzaKykbYJdmnSr6XHo3Jhk=";
+
+/* TC TrustCenter Universal CA I */
+static const char kTC_TrustCenter_Universal_CA_IFingerprint[] =
+ "st71NirT+s0EvSkEekOET3ZwNOpIkvgOVr7mkCQ+JQI=";
+
+/* TC TrustCenter Universal CA III */
+static const char kTC_TrustCenter_Universal_CA_IIIFingerprint[] =
+ "q1zbM1Y5c1bW5pGXPCW4YYtl12qQSG6nqKXBd2f0Zzo=";
+
+/* TestSPKI */
+static const char kTestSPKIFingerprint[] =
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+
+/* Thawte Premium Server CA */
+static const char kThawte_Premium_Server_CAFingerprint[] =
+ "9TwiBZgX3Zb0AGUWOdL4V+IQcKWavtkHlADZ9pVQaQA=";
+
+/* Thawte Server CA */
+static const char kThawte_Server_CAFingerprint[] =
+ "nG9qEjy6pO402+zu4kyX1ziHjLQj88InOQNCT10fbdU=";
+
+/* Tor1 */
+static const char kTor1Fingerprint[] =
+ "juNxSTv9UANmpC9kF5GKpmWNx3Y=";
+
+/* Tor2 */
+static const char kTor2Fingerprint[] =
+ "lia43lPolzSPVIq34Dw57uYcLD8=";
+
+/* Tor3 */
+static const char kTor3Fingerprint[] =
+ "rzEyQIKOh77j87n5bjWUNguXF8Y=";
+
+/* Twitter1 */
+static const char kTwitter1Fingerprint[] =
+ "Vv7zwhR9TtOIN/29MFI4cgHld40=";
+
+/* UTN DATACorp SGC Root CA */
+static const char kUTN_DATACorp_SGC_Root_CAFingerprint[] =
+ "QAL80xHQczFWfnG82XHkYEjI3OjRZZcRdTs9qiommvo=";
+
+/* UTN USERFirst Email Root CA */
+static const char kUTN_USERFirst_Email_Root_CAFingerprint[] =
+ "Laj56jRU0hFGRko/nQKNxMf7tXscUsc8KwVyovWZotM=";
+
+/* UTN USERFirst Hardware Root CA */
+static const char kUTN_USERFirst_Hardware_Root_CAFingerprint[] =
+ "TUDnr0MEoJ3of7+YliBMBVFB4/gJsv5zO7IxD9+YoWI=";
+
+/* UTN USERFirst Object Root CA */
+static const char kUTN_USERFirst_Object_Root_CAFingerprint[] =
+ "D+FMJksXu28NZT56cOs2Pb9UvhWAOe3a5cJXEd9IwQM=";
+
+/* VeriSign Class 3 Public Primary Certification Authority - G4 */
+static const char kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint[] =
+ "UZJDjsNp1+4M5x9cbbdflB779y5YRBcV6Z6rBMLIrO4=";
+
+/* VeriSign Class 3 Public Primary Certification Authority - G5 */
+static const char kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint[] =
+ "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=";
+
+/* VeriSign Universal Root Certification Authority */
+static const char kVeriSign_Universal_Root_Certification_AuthorityFingerprint[] =
+ "lnsM2T/O9/J84sJFdnrpsFp3awZJ+ZZbYpCWhGloaHI=";
+
+/* Verisign Class 1 Public Primary Certification Authority */
+static const char kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint[] =
+ "LclHC+Y+9KzxvYKGCUArt7h72ZY4pkOTTohoLRvowwg=";
+
+/* Verisign Class 1 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "IgduWu9Eu5pBaii30cRDItcFn2D+/6XK9sW+hEeJEwM=";
+
+/* Verisign Class 2 Public Primary Certification Authority - G2 */
+static const char kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint[] =
+ "2oALgLKofTmeZvoZ1y/fSZg7R9jPMix8eVA6DH4o/q8=";
+
+/* Verisign Class 2 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "cAajgxHlj7GTSEIzIYIQxmEloOSoJq7VOaxWHfv72QM=";
+
+/* Verisign Class 3 Public Primary Certification Authority */
+static const char kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint[] =
+ "sRJBQqWhpaKIGcc1NA7/jJ4vgWj+47oYfyU7waOS1+I=";
+
+/* Verisign Class 3 Public Primary Certification Authority - G2 */
+static const char kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint[] =
+ "AjyBzOjnxk+pQtPBUEhwfTXZu1uH9PVExb8bxWQ68vo=";
+
+/* Verisign Class 3 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "SVqWumuteCQHvVIaALrOZXuzVVVeS7f4FGxxu6V+es4=";
+
+/* Verisign Class 4 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "VnuCEf0g09KD7gzXzgZyy52ZvFtIeljJ1U7Gf3fUqPU=";
+
+/* XRamp Global CA Root */
+static const char kXRamp_Global_CA_RootFingerprint[] =
+ "BRz5+pXkDpuD7a7aaWH2Fox4ecRmAXJHnN1RqwPOpis=";
+
+/* thawte Primary Root CA */
+static const char kthawte_Primary_Root_CAFingerprint[] =
+ "HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=";
+
+/* thawte Primary Root CA - G2 */
+static const char kthawte_Primary_Root_CA___G2Fingerprint[] =
+ "Z9xPMvoQ59AaeaBzqgyeAhLsL/w9d54Kp/nA8OHCyJM=";
+
+/* thawte Primary Root CA - G3 */
+static const char kthawte_Primary_Root_CA___G3Fingerprint[] =
+ "GQbGEk27Q4V40A4GbVBUxsN/D6YCjAVUXgmU7drshik=";
+
+/* Pinsets are each an ordered list by the actual value of the fingerprint */
+struct StaticFingerprints {
+ const size_t size;
+ const char* const* data;
+};
+
+struct StaticPinset {
+ const StaticFingerprints* sha1;
+ const StaticFingerprints* sha256;
+};
+
+/* PreloadedHPKPins.json pinsets */
+static const char* kPinset_facebook_sha256_Data[] = {
+ kDigiCert_ECC_Secure_Server_CAFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_facebook_sha256 = {
+ sizeof(kPinset_facebook_sha256_Data) / sizeof(const char*),
+ kPinset_facebook_sha256_Data
+};
+
+static const StaticPinset kPinset_facebook = {
+ nullptr,
+ &kPinset_facebook_sha256
+};
+
+static const char* kPinset_google_root_pems_sha256_Data[] = {
+ kEquifax_Secure_CAFingerprint,
+ kAmerica_Online_Root_Certification_Authority_2Fingerprint,
+ kComodo_Trusted_Services_rootFingerprint,
+ kCOMODO_ECC_Certification_AuthorityFingerprint,
+ kStartCom_Certification_AuthorityFingerprint,
+ kStartCom_Certification_AuthorityFingerprint,
+ kThawte_Premium_Server_CAFingerprint,
+ kCOMODO_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+ kXRamp_Global_CA_RootFingerprint,
+ kAddTrust_Low_Value_Services_RootFingerprint,
+ kGeoTrust_Global_CA_2Fingerprint,
+ kStartCom_Certification_Authority_G2Fingerprint,
+ kStarfield_Class_2_CAFingerprint,
+ kthawte_Primary_Root_CA___G3Fingerprint,
+ kthawte_Primary_Root_CAFingerprint,
+ kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kAmerica_Online_Root_Certification_Authority_1Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kEquifax_Secure_eBusiness_CA_1Fingerprint,
+ kGlobalSign_Root_CAFingerprint,
+ kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
+ kStarfield_Services_Root_Certificate_Authority___G2Fingerprint,
+ kAffirmTrust_Premium_ECCFingerprint,
+ kNetwork_Solutions_Certificate_AuthorityFingerprint,
+ kAddTrust_Public_Services_RootFingerprint,
+ kUTN_DATACorp_SGC_Root_CAFingerprint,
+ kComodo_Secure_Services_rootFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kUTN_USERFirst_Hardware_Root_CAFingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kGo_Daddy_Class_2_CAFingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kBaltimore_CyberTrust_RootFingerprint,
+ kthawte_Primary_Root_CA___G2Fingerprint,
+ kAffirmTrust_CommercialFingerprint,
+ kEntrust_Root_Certification_AuthorityFingerprint,
+ kGlobalSign_Root_CA___R3Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kCybertrust_Global_RootFingerprint,
+ kStarfield_Root_Certificate_Authority___G2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kGlobalSign_Root_CA___R2Fingerprint,
+ kTC_TrustCenter_Class_3_CA_IIFingerprint,
+ kAffirmTrust_NetworkingFingerprint,
+ kAddTrust_External_RootFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kThawte_Server_CAFingerprint,
+ kEquifax_Secure_Global_eBusiness_CAFingerprint,
+ kTC_TrustCenter_Universal_CA_IIIFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kTC_TrustCenter_Class_2_CA_IIFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kTC_TrustCenter_Universal_CA_IFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+ kComodo_AAA_Services_rootFingerprint,
+ kAffirmTrust_PremiumFingerprint,
+ kAddTrust_Qualified_Certificates_RootFingerprint,
+};
+static const StaticFingerprints kPinset_google_root_pems_sha256 = {
+ sizeof(kPinset_google_root_pems_sha256_Data) / sizeof(const char*),
+ kPinset_google_root_pems_sha256_Data
+};
+
+static const StaticPinset kPinset_google_root_pems = {
+ nullptr,
+ &kPinset_google_root_pems_sha256
+};
+
+static const char* kPinset_mozilla_sha256_Data[] = {
+ kGeoTrust_Global_CA_2Fingerprint,
+ kthawte_Primary_Root_CA___G3Fingerprint,
+ kthawte_Primary_Root_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kBaltimore_CyberTrust_RootFingerprint,
+ kthawte_Primary_Root_CA___G2Fingerprint,
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_sha256 = {
+ sizeof(kPinset_mozilla_sha256_Data) / sizeof(const char*),
+ kPinset_mozilla_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla = {
+ nullptr,
+ &kPinset_mozilla_sha256
+};
+
+static const char* kPinset_mozilla_services_sha256_Data[] = {
+ kDigiCert_Global_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_services_sha256 = {
+ sizeof(kPinset_mozilla_services_sha256_Data) / sizeof(const char*),
+ kPinset_mozilla_services_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla_services = {
+ nullptr,
+ &kPinset_mozilla_services_sha256
+};
+
+static const char* kPinset_mozilla_test_sha256_Data[] = {
+ kEnd_Entity_Test_CertFingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_test_sha256 = {
+ sizeof(kPinset_mozilla_test_sha256_Data) / sizeof(const char*),
+ kPinset_mozilla_test_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla_test = {
+ nullptr,
+ &kPinset_mozilla_test_sha256
+};
+
+/* Chrome static pinsets */
+static const char* kPinset_test_sha1_Data[] = {
+ kTestSPKIFingerprint,
+};
+static const StaticFingerprints kPinset_test_sha1 = {
+ sizeof(kPinset_test_sha1_Data) / sizeof(const char*),
+ kPinset_test_sha1_Data
+};
+
+static const StaticPinset kPinset_test = {
+ &kPinset_test_sha1,
+ nullptr
+};
+
+static const char* kPinset_google_sha1_Data[] = {
+ kGoogleG2Fingerprint,
+ kGoogleBackup2048Fingerprint,
+};
+static const StaticFingerprints kPinset_google_sha1 = {
+ sizeof(kPinset_google_sha1_Data) / sizeof(const char*),
+ kPinset_google_sha1_Data
+};
+
+static const StaticPinset kPinset_google = {
+ &kPinset_google_sha1,
+ nullptr
+};
+
+static const char* kPinset_tor_sha1_Data[] = {
+ kTor1Fingerprint,
+ kTor2Fingerprint,
+ kTor3Fingerprint,
+};
+static const StaticFingerprints kPinset_tor_sha1 = {
+ sizeof(kPinset_tor_sha1_Data) / sizeof(const char*),
+ kPinset_tor_sha1_Data
+};
+
+static const char* kPinset_tor_sha256_Data[] = {
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kGOOGLE_PIN_RapidSSLFingerprint,
+};
+static const StaticFingerprints kPinset_tor_sha256 = {
+ sizeof(kPinset_tor_sha256_Data) / sizeof(const char*),
+ kPinset_tor_sha256_Data
+};
+
+static const StaticPinset kPinset_tor = {
+ &kPinset_tor_sha1,
+ &kPinset_tor_sha256
+};
+
+static const char* kPinset_twitterCom_sha1_Data[] = {
+ kTwitter1Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCom_sha1 = {
+ sizeof(kPinset_twitterCom_sha1_Data) / sizeof(const char*),
+ kPinset_twitterCom_sha1_Data
+};
+
+static const char* kPinset_twitterCom_sha256_Data[] = {
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+ kGeoTrust_Global_CA_2Fingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCom_sha256 = {
+ sizeof(kPinset_twitterCom_sha256_Data) / sizeof(const char*),
+ kPinset_twitterCom_sha256_Data
+};
+
+static const StaticPinset kPinset_twitterCom = {
+ &kPinset_twitterCom_sha1,
+ &kPinset_twitterCom_sha256
+};
+
+static const char* kPinset_twitterCDN_sha1_Data[] = {
+ kTwitter1Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCDN_sha1 = {
+ sizeof(kPinset_twitterCDN_sha1_Data) / sizeof(const char*),
+ kPinset_twitterCDN_sha1_Data
+};
+
+static const char* kPinset_twitterCDN_sha256_Data[] = {
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint,
+ kComodo_Trusted_Services_rootFingerprint,
+ kCOMODO_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+ kAddTrust_Low_Value_Services_RootFingerprint,
+ kUTN_USERFirst_Object_Root_CAFingerprint,
+ kGTE_CyberTrust_Global_RootFingerprint,
+ kGeoTrust_Global_CA_2Fingerprint,
+ kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kGlobalSign_Root_CAFingerprint,
+ kUTN_USERFirst_Email_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint,
+ kAddTrust_Public_Services_RootFingerprint,
+ kUTN_DATACorp_SGC_Root_CAFingerprint,
+ kComodo_Secure_Services_rootFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kUTN_USERFirst_Hardware_Root_CAFingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kBaltimore_CyberTrust_RootFingerprint,
+ kEntrust_Root_Certification_AuthorityFingerprint,
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+ kGlobalSign_Root_CA___R3Fingerprint,
+ kGOOGLE_PIN_Entrust_G2Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kGlobalSign_Root_CA___R2Fingerprint,
+ kAddTrust_External_RootFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kGOOGLE_PIN_Entrust_SSLFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+ kComodo_AAA_Services_rootFingerprint,
+ kAddTrust_Qualified_Certificates_RootFingerprint,
+};
+static const StaticFingerprints kPinset_twitterCDN_sha256 = {
+ sizeof(kPinset_twitterCDN_sha256_Data) / sizeof(const char*),
+ kPinset_twitterCDN_sha256_Data
+};
+
+static const StaticPinset kPinset_twitterCDN = {
+ &kPinset_twitterCDN_sha1,
+ &kPinset_twitterCDN_sha256
+};
+
+static const char* kPinset_tor2web_sha256_Data[] = {
+ kGOOGLE_PIN_Tor2webFingerprint,
+ kGOOGLE_PIN_AlphaSSL_G2Fingerprint,
+};
+static const StaticFingerprints kPinset_tor2web_sha256 = {
+ sizeof(kPinset_tor2web_sha256_Data) / sizeof(const char*),
+ kPinset_tor2web_sha256_Data
+};
+
+static const StaticPinset kPinset_tor2web = {
+ nullptr,
+ &kPinset_tor2web_sha256
+};
+
+static const char* kPinset_cryptoCat_sha256_Data[] = {
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kGOOGLE_PIN_CryptoCat1Fingerprint,
+};
+static const StaticFingerprints kPinset_cryptoCat_sha256 = {
+ sizeof(kPinset_cryptoCat_sha256_Data) / sizeof(const char*),
+ kPinset_cryptoCat_sha256_Data
+};
+
+static const StaticPinset kPinset_cryptoCat = {
+ nullptr,
+ &kPinset_cryptoCat_sha256
+};
+
+static const char* kPinset_lavabit_sha256_Data[] = {
+ kGOOGLE_PIN_LibertylavabitcomFingerprint,
+};
+static const StaticFingerprints kPinset_lavabit_sha256 = {
+ sizeof(kPinset_lavabit_sha256_Data) / sizeof(const char*),
+ kPinset_lavabit_sha256_Data
+};
+
+static const StaticPinset kPinset_lavabit = {
+ nullptr,
+ &kPinset_lavabit_sha256
+};
+
+static const char* kPinset_dropbox_sha256_Data[] = {
+ kGOOGLE_PIN_EntrustRootEC1Fingerprint,
+ kThawte_Premium_Server_CAFingerprint,
+ kthawte_Primary_Root_CA___G3Fingerprint,
+ kthawte_Primary_Root_CAFingerprint,
+ kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
+ kGOOGLE_PIN_GoDaddySecureFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kGo_Daddy_Class_2_CAFingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kthawte_Primary_Root_CA___G2Fingerprint,
+ kEntrust_Root_Certification_AuthorityFingerprint,
+ kGOOGLE_PIN_Entrust_G2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_dropbox_sha256 = {
+ sizeof(kPinset_dropbox_sha256_Data) / sizeof(const char*),
+ kPinset_dropbox_sha256_Data
+};
+
+static const StaticPinset kPinset_dropbox = {
+ nullptr,
+ &kPinset_dropbox_sha256
+};
+
+/* Domainlist */
+struct TransportSecurityPreload {
+ const char* mHost;
+ const bool mIncludeSubdomains;
+ const bool mTestMode;
+ const bool mIsMoz;
+ const int32_t mId;
+ const StaticPinset *pinset;
+};
+
+/* Sort hostnames for binary search. */
+static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
+ { "2mdn.net", true, false, false, -1, &kPinset_google_root_pems },
+ { "accounts.firefox.com", true, false, false, 4, &kPinset_mozilla_services },
+ { "accounts.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "addons.mozilla.net", true, false, true, 2, &kPinset_mozilla },
+ { "addons.mozilla.org", true, false, true, 1, &kPinset_mozilla },
+ { "admin.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "android.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "api.accounts.firefox.com", true, false, false, 5, &kPinset_mozilla_services },
+ { "api.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "appengine.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "aus4.mozilla.org", true, true, true, 3, &kPinset_mozilla },
+ { "blog.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "business.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "cdn.mozilla.net", true, false, true, -1, &kPinset_mozilla },
+ { "cdn.mozilla.org", true, false, true, -1, &kPinset_mozilla },
+ { "chart.apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "check.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "checkout.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "chrome-devtools-frontend.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "chrome.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "chromiumcodereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "cloud.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "code.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "codereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "codereview.chromium.org", true, false, false, -1, &kPinset_google_root_pems },
+ { "crypto.cat", false, true, false, -1, &kPinset_cryptoCat },
+ { "dev.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "dist.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "dl.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "domains.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
+ { "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "dropbox.com", false, false, false, -1, &kPinset_dropbox },
+ { "encrypted.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
+ { "facebook.com", true, true, false, -1, &kPinset_facebook },
+ { "g.co", true, false, false, -1, &kPinset_google_root_pems },
+ { "glass.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "goo.gl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ac", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ad", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ae", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.af", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ag", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.am", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.as", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.at", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.az", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ba", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.be", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bf", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bs", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.by", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ca", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cat", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cc", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cd", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cf", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ch", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ci", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ao", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.bw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ck", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.cr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.hu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.id", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.il", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.im", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.in", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.je", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.jp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ke", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.kr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ls", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ma", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.mz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.nz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.th", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.tz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ug", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.uk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.uz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ve", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.vi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.za", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.zm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.zw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.af", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ag", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ai", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ar", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.au", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bd", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bo", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.br", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.by", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.cn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.co", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.cu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.cy", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.do", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ec", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.eg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.et", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.fj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ge", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.hk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.iq", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.jm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.jo", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.kh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.kw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.lb", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ly", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.mt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.mx", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.my", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.na", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.nf", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ng", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ni", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.np", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.nr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.om", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pa", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pe", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ph", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.py", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.qa", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ru", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sa", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sb", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ua", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.uy", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.vc", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ve", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.vn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.de", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ee", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.es", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.fi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.fm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.fr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ga", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ge", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gy", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ht", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ie", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.im", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.info", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.iq", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.is", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.it", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.it.ao", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.je", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.jo", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.jobs", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.jp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.kg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ki", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.kz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.la", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.li", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.md", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.me", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ml", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ms", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ne", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ne.jp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.net", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.nl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.no", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.nr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.nu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.off.ai", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ps", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ro", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.rs", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ru", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.rw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sc", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.se", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.si", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.so", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.st", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.td", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.to", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.us", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.uz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.vg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.vu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ws", true, false, false, -1, &kPinset_google_root_pems },
+ { "googleadservices.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlecode.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlecommerce.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlegroups.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "googleplex.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlesyndication.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googletagmanager.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googletagservices.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googleusercontent.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "goto.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "groups.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "gstatic.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "history.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "hostedtalkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "include-subdomains.pinning.example.com", true, false, false, -1, &kPinset_mozilla_test },
+ { "liberty.lavabit.com", true, true, false, -1, &kPinset_lavabit },
+ { "login.corp.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "mail.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "market.android.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "media.mozilla.com", true, false, true, -1, &kPinset_mozilla },
+ { "mobile.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "oauth.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "pinningtest.appspot.com", true, false, false, -1, &kPinset_test },
+ { "platform.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "play.google.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "plus.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "plus.sandbox.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "profiles.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "script.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "security.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "services.mozilla.com", true, true, false, -1, &kPinset_mozilla_services },
+ { "sites.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "spreadsheets.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "ssl.google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "talk.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "talkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
+ { "tor2web.org", true, true, false, -1, &kPinset_tor2web },
+ { "torproject.org", false, false, false, -1, &kPinset_tor },
+ { "translate.googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "twimg.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "urchin.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "w-spotlight.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wallet.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings-eu-mirror.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings-mirror-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-bigsky-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-demo-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-demo-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-dogfood-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-pentest.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-staging-hr.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-training-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-training-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-trial-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "www.dropbox.com", true, false, false, -1, &kPinset_dropbox },
+ { "www.gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "www.googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "www.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "www.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "xbrlsuccess.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "youtu.be", true, false, false, -1, &kPinset_google_root_pems },
+ { "youtube-nocookie.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
+};
+
+// Pinning Preload List Length = 331;
+
+static const int32_t kUnknownId = -1;
+
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1419674828470000);
diff --git a/security/manager/boot/src/moz.build b/security/manager/boot/src/moz.build
index b3d2127..50a345b 100644
--- a/security/manager/boot/src/moz.build
+++ b/security/manager/boot/src/moz.build
@@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
'nsSecurityHeaderParser.cpp',
'nsSecurityWarningDialogs.cpp',
'nsSiteSecurityService.cpp',
+ 'PublicKeyPinningService.cpp',
]
# nsSecureBrowserUIImpl.cpp cannot be built in unified mode because it forces NSPR logging.
@@ -17,6 +18,11 @@ SOURCES += [
'nsSecureBrowserUIImpl.cpp',
]
+
+LOCAL_INCLUDES += [
+ '../../../pkix/include',
+]
+
FAIL_ON_WARNINGS = True
MSVC_ENABLE_PGO = True
diff --git a/security/manager/ssl/src/SSLServerCertVerification.cpp b/security/manager/ssl/src/SSLServerCertVerification.cpp
index cf15586..18d9f55 100644
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -637,8 +637,9 @@ NSSDetermineCertOverrideErrors(CertVerifier& certVerifier,
// possible failure.
// XXX TODO: convert to VerifySSLServerCert
// XXX TODO: get rid of error log
- certVerifier.VerifyCert(cert, stapledOCSPResponse, certificateUsageSSLServer,
- now, infoObject, 0, nullptr, nullptr, verify_log);
+ certVerifier.VerifyCert(cert, certificateUsageSSLServer,
+ now, infoObject, infoObject->GetHostNameRaw(),
+ 0, stapledOCSPResponse, nullptr, nullptr, verify_log);
// Check the name field against the desired hostname.
if (CERT_VerifyCertName(cert, infoObject->GetHostNameRaw()) != SECSuccess) {
diff --git a/security/manager/ssl/src/SharedCertVerifier.h b/security/manager/ssl/src/SharedCertVerifier.h
index 32a92a3..c7c1936 100644
--- a/security/manager/ssl/src/SharedCertVerifier.h
+++ b/security/manager/ssl/src/SharedCertVerifier.h
@@ -24,12 +24,14 @@ public:
missing_cert_download_config ac, crl_download_config cdc,
#endif
ocsp_download_config odc, ocsp_strict_config osc,
- ocsp_get_config ogc)
+ ocsp_get_config ogc,
+ pinning_enforcement_config pinningEnforcementLevel)
: mozilla::psm::CertVerifier(ic,
#ifndef NSS_NO_LIBPKIX
ac, cdc,
#endif
- odc, osc, ogc)
+ odc, osc, ogc,
+ pinningEnforcementLevel)
{
}
};
diff --git a/security/manager/ssl/src/nsCMS.cpp b/security/manager/ssl/src/nsCMS.cpp
index 70a3c49..ee76c07 100644
--- a/security/manager/ssl/src/nsCMS.cpp
+++ b/security/manager/ssl/src/nsCMS.cpp
@@ -264,9 +264,10 @@ nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, uint32_
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
{
- SECStatus srv = certVerifier->VerifyCert(si->cert, nullptr,
+ SECStatus srv = certVerifier->VerifyCert(si->cert,
certificateUsageEmailSigner,
- PR_Now(), nullptr /*XXX pinarg*/);
+ PR_Now(), nullptr /*XXX pinarg*/,
+ nullptr /*hostname*/);
if (srv != SECSuccess) {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("nsCMSMessage::CommonVerifySignature - signing cert not trusted now\n"));
diff --git a/security/manager/ssl/src/nsNSSCertificate.cpp b/security/manager/ssl/src/nsNSSCertificate.cpp
index 88fb8ed..70b9c89 100644
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -829,10 +829,12 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
// We want to test all usages, but we start with server because most of the
// time Firefox users care about server certs.
- certVerifier->VerifyCert(mCert.get(), nullptr,
+ certVerifier->VerifyCert(mCert.get(),
certificateUsageSSLServer, PR_Now(),
nullptr, /*XXX fixme*/
+ nullptr, /* hostname */
CertVerifier::FLAG_LOCAL_ONLY,
+ nullptr, /* stapledOCSPResponse */
&nssChain);
// This is the whitelist of all non-SSLServer usages that are supported by
// verifycert.
@@ -851,10 +853,12 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("pipnss: PKIX attempting chain(%d) for '%s'\n",
usage, mCert->nickname));
- certVerifier->VerifyCert(mCert.get(), nullptr,
+ certVerifier->VerifyCert(mCert.get(),
usage, PR_Now(),
nullptr, /*XXX fixme*/
+ nullptr, /*hostname*/
CertVerifier::FLAG_LOCAL_ONLY,
+ nullptr, /* stapledOCSPResponse */
&nssChain);
}
@@ -1467,10 +1471,11 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
- SECStatus rv = certVerifier->VerifyCert(mCert.get(), nullptr,
+ SECStatus rv = certVerifier->VerifyCert(mCert.get(),
certificateUsageSSLServer, PR_Now(),
nullptr /* XXX pinarg */,
- flags, nullptr, &resultOidTag);
+ nullptr /* hostname */,
+ flags, nullptr /* stapledOCSPResponse */ , nullptr, &resultOidTag);
if (rv != SECSuccess) {
resultOidTag = SEC_OID_UNKNOWN;
diff --git a/security/manager/ssl/src/nsNSSCertificateDB.cpp b/security/manager/ssl/src/nsNSSCertificateDB.cpp
index 40b03c1..c9fd8ba 100644
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -634,9 +634,10 @@ nsNSSCertificateDB::ImportEmailCertificate(uint8_t * data, uint32_t length,
mozilla::pkix::ScopedCERTCertList certChain;
- SECStatus rv = certVerifier->VerifyCert(node->cert, nullptr,
+ SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
- now, ctx, 0, &certChain);
+ now, ctx, nullptr, 0,
+ nullptr, &certChain);
if (rv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
@@ -801,9 +802,10 @@ nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfac
!CERT_LIST_END(node,certList);
node = CERT_LIST_NEXT(node)) {
mozilla::pkix::ScopedCERTCertList certChain;
- SECStatus rv = certVerifier->VerifyCert(node->cert, nullptr,
+ SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageVerifyCA,
- PR_Now(), ctx, 0, &certChain);
+ PR_Now(), ctx, nullptr, 0, nullptr,
+ &certChain);
if (rv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
@@ -1381,9 +1383,10 @@ nsNSSCertificateDB::FindCertByEmailAddress(nsISupports *aToken, const char *aEma
!CERT_LIST_END(node, certlist);
node = CERT_LIST_NEXT(node)) {
- SECStatus srv = certVerifier->VerifyCert(node->cert, nullptr,
+ SECStatus srv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
- PR_Now(), nullptr /*XXX pinarg*/);
+ PR_Now(), nullptr /*XXX pinarg*/,
+ nullptr /*hostname*/);
if (srv == SECSuccess) {
break;
}
@@ -1772,10 +1775,12 @@ nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
SECOidTag evOidPolicy;
SECStatus srv;
- srv = certVerifier->VerifyCert(nssCert, nullptr,
+ srv = certVerifier->VerifyCert(nssCert,
aUsage, PR_Now(),
nullptr, // Assume no context
+ nullptr, // hostname
aFlags,
+ nullptr, // stapledOCSPResponse
&resultChain,
&evOidPolicy);
diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp
index 44b3808..c4bd566 100644
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -996,6 +996,13 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
}
}
+ // Default pinning enforcement level is disabled.
+ CertVerifier::pinning_enforcement_config
+ pinningEnforcementLevel =
+ static_cast<CertVerifier::pinning_enforcement_config>
+ (Preferences::GetInt("security.cert_pinning.enforcement_level",
+ CertVerifier::pinningDisabled));
+
CertVerifier::ocsp_download_config odc;
CertVerifier::ocsp_strict_config osc;
CertVerifier::ocsp_get_config ogc;
@@ -1009,7 +1016,7 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
crlDownloading ?
CertVerifier::crl_download_allowed : CertVerifier::crl_local_only,
#endif
- odc, osc, ogc);
+ odc, osc, ogc, pinningEnforcementLevel);
// mozilla::pkix has its own OCSP cache, so disable the NSS cache
// if appropriate.
@@ -1644,7 +1651,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
|| prefName.Equals("security.OCSP.GET.enabled")
|| prefName.Equals("security.ssl.enable_ocsp_stapling")
|| prefName.Equals("security.use_mozillapkix_verification")
- || prefName.Equals("security.use_libpkix_verification")) {
+ || prefName.Equals("security.use_libpkix_verification")
+ || prefName.Equals("security.cert_pinning.enforcement_level")) {
MutexAutoLock lock(mutex);
setValidationOptions(false, lock);
} else if (prefName.Equals("network.ntlm.send-lm-response")) {
diff --git a/security/manager/ssl/src/nsUsageArrayHelper.cpp b/security/manager/ssl/src/nsUsageArrayHelper.cpp
index 7f7249f..dd65ae5 100644
--- a/security/manager/ssl/src/nsUsageArrayHelper.cpp
+++ b/security/manager/ssl/src/nsUsageArrayHelper.cpp
@@ -105,8 +105,9 @@ nsUsageArrayHelper::check(uint32_t previousCheckResult,
MOZ_CRASH("unknown cert usage passed to check()");
}
- SECStatus rv = certVerifier->VerifyCert(mCert, nullptr, aCertUsage,
- time, nullptr /*XXX:wincx*/, flags);
+ SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time,
+ nullptr /*XXX:wincx*/,
+ nullptr /*hostname*/, flags);
if (rv == SECSuccess) {
typestr.Append(suffix);
diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js
index 58e838b..ae25ea9e 100644
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -52,6 +52,7 @@ const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144;
const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; // -8032
const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157;
const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
+const SEC_ERROR_APPLICATION_CALLBACK_ERROR = SEC_ERROR_BASE + 178;
const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12;
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js
index 11e14f3b..91fc77eb 100644
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -232,7 +232,7 @@ function add_distrust_override_test(certFileName, hostName,
let certToDistrust = constructCertFromFile(certFileName);
add_test(function () {
- // "pu" means the cert is actively distrusted.
+ // Add an entry to the NSS certDB that says to distrust the cert
setCertTrust(certToDistrust, "pu,,");
clearSessionCache();
run_next_test();
diff --git a/security/manager/ssl/tests/unit/test_pinning.js b/security/manager/ssl/tests/unit/test_pinning.js
new file mode 100644
index 0000000..dc779dd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -0,0 +1,182 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+// For all cases, the acceptable pinset includes only certificates pinned to
+// Test End Entity Cert (signed by issuer testCA). Other certificates
+// are issued by otherCA, which is never in the pinset but is a user-specified
+// trust anchor. This test covers multiple cases:
+//
+// Pinned domain include-subdomains.pinning.example.com includes subdomains
+// - PASS: include-subdomains.pinning.example.com serves a correct cert
+// - PASS: good.include-subdomains.pinning.example.com serves a correct cert
+// - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert
+// not in the pinset
+// - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not
+// in the pinset, but issued by a user-specified trust domain
+//
+// Pinned domain exclude-subdomains.pinning.example.com excludes subdomains
+// - PASS: exclude-subdomains.pinning.example.com serves a correct cert
+// - FAIL: exclude-subdomains.pinning.example.com services an incorrect cert
+// (TODO: test using verifyCertnow)
+// - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+
+function test_strict() {
+ // In strict mode, we always evaluate pinning data, regardless of whether the
+ // issuer is a built-in trust anchor. We only enforce pins that are not in
+ // test mode.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
+ run_next_test();
+ });
+
+ // If a host should be pinned but other errors (particularly overridable
+ // errors) like 'unknown issuer' are encountered, the pinning error takes
+ // precedence. This prevents overrides for such hosts.
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // Issued by otherCA, which is not in the pinset for pinning.example.com.
+ add_connection_test("bad.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // These domains serve certs that match the pinset.
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain serves a cert that doesn't match the pinset, but subdomains
+ // are excluded.
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain's pinset is exactly the same as
+ // include-subdomains.pinning.example.com, serves the same cert as
+ // bad.include-subdomains.pinning.example.com, but it should pass because
+ // it's in test_mode.
+ add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+}
+
+function test_mitm() {
+ // In MITM mode, we allow pinning to pass if the chain resolves to any
+ // user-specified trust anchor, even if it is not in the pinset.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1);
+ run_next_test();
+ });
+
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER));
+
+ // In this case, even though otherCA is not in the pinset, it is a
+ // user-specified trust anchor and the pinning check succeeds.
+ add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK);
+
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+};
+
+function test_disabled() {
+ // Disable pinning.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0);
+ run_next_test();
+ });
+
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER));
+}
+
+function test_enforce_test_mode() {
+ // In enforce test mode, we always enforce all pins, even test pins.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3);
+ run_next_test();
+ });
+
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // Issued by otherCA, which is not in the pinset for pinning.example.com.
+ add_connection_test("bad.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // These domains serve certs that match the pinset.
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain serves a cert that doesn't match the pinset, but subdomains
+ // are excluded.
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain's pinset is exactly the same as
+ // include-subdomains.pinning.example.com, serves the same cert as
+ // bad.include-subdomains.pinning.example.com, is in test-mode, but we are
+ // enforcing test mode pins.
+ add_connection_test("test-mode.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+}
+
+function check_pinning_telemetry() {
+ let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
+ let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS")
+ .snapshot();
+ let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS")
+ .snapshot();
+ // Because all of our test domains are pinned to user-specified trust
+ // anchors, effectively only strict mode and enforce test-mode get evaluated
+ do_check_eq(prod_histogram.counts[0], 4); // Failure count
+ do_check_eq(prod_histogram.counts[1], 4); // Success count
+ do_check_eq(test_histogram.counts[0], 2); // Failure count
+ do_check_eq(test_histogram.counts[1], 0); // Success count
+
+ let moz_prod_histogram = service.getHistogramById("CERT_PINNING_MOZ_RESULTS")
+ .snapshot();
+ let moz_test_histogram =
+ service.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot();
+ do_check_eq(moz_prod_histogram.counts[0], 0); // Failure count
+ do_check_eq(moz_prod_histogram.counts[1], 0); // Success count
+ do_check_eq(moz_test_histogram.counts[0], 0); // Failure count
+ do_check_eq(moz_test_histogram.counts[1], 0); // Success count
+
+ let per_host_histogram =
+ service.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot();
+ do_check_eq(per_host_histogram.counts[0], 0); // Failure count
+ do_check_eq(per_host_histogram.counts[1], 2); // Success count
+ run_next_test();
+}
+
+function run_test() {
+ add_tls_server_setup("BadCertServer");
+
+ // Add a user-specified trust anchor.
+ addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u");
+
+ test_strict();
+ test_mitm();
+ test_disabled();
+ test_enforce_test_mode();
+
+ add_test(function () {
+ check_pinning_telemetry();
+ });
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cert8.db b/security/manager/ssl/tests/unit/tlsserver/cert8.db
index 13c101f..7da5d7f 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/cert8.db and b/security/manager/ssl/tests/unit/tlsserver/cert8.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
index a4c59a4..5df134a 100644
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
@@ -24,6 +24,7 @@ struct BadCertHost
const char *mCertName;
};
+// Hostname, cert nickname pairs.
const BadCertHost sBadCertHosts[] =
{
{ "expired.example.com", "expired" },
@@ -42,6 +43,16 @@ const BadCertHost sBadCertHosts[] =
{ "inadequatekeyusage.example.com", "inadequatekeyusage" },
{ "selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU" },
{ "self-signed-end-entity-with-cA-true.example.com", "self-signed-EE-with-cA-true" },
+ // All of include-subdomains.pinning.example.com is pinned to End Entity
+ // Test Cert with nick localhostAndExampleCom. Any other nick will only
+ // pass pinning when security.cert_pinning.enforcement.level != strict and
+ // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h.
+ { "include-subdomains.pinning.example.com", "localhostAndExampleCom" },
+ { "good.include-subdomains.pinning.example.com", "localhostAndExampleCom" },
+ { "bad.include-subdomains.pinning.example.com", "otherIssuerEE" },
+ { "exclude-subdomains.pinning.example.com", "localhostAndExampleCom" },
+ { "sub.exclude-subdomains.pinning.example.com", "otherIssuerEE" },
+ { "test-mode.pinning.example.com", "otherIssuerEE" },
{ nullptr, nullptr }
};
diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.der b/security/manager/ssl/tests/unit/tlsserver/default-ee.der
index ac98037..9b2359d 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/default-ee.der and b/security/manager/ssl/tests/unit/tlsserver/default-ee.der differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
index ef388bb..6075a5c 100755
--- a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
+++ b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
@@ -12,7 +12,10 @@
#
# NB: This will cause the following files to be overwritten if they are in
# the output directory:
-# cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der
+# cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der, default-ee.der
+# NB: You must run genHPKPStaticPins.js after running this file, since its
+# output (StaticHPKPins.h) depends on default-ee.der
+
set -x
set -e
@@ -25,11 +28,13 @@ OBJDIR=${1}
OUTPUT_DIR=${2}
RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
CERTUTIL="$OBJDIR/dist/bin/certutil"
+# On BSD, mktemp requires either a template or a prefix.
+MKTEMP="mktemp temp.XXXX"
-NOISE_FILE=`mktemp`
+NOISE_FILE=`$MKTEMP`
# Make a good effort at putting something unique in the noise file.
date +%s%N > "$NOISE_FILE"
-PASSWORD_FILE=`mktemp`
+PASSWORD_FILE=`$MKTEMP`
function cleanup {
rm -f "$NOISE_FILE" "$PASSWORD_FILE"
@@ -134,7 +139,11 @@ function make_delegated {
make_CA testCA 'CN=Test CA' test-ca.der
make_CA otherCA 'CN=Other test CA' other-test-ca.der
-make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com"
+
+make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com"
+# Make an EE cert issued by otherCA
+make_EE otherIssuerEE 'CN=Wrong CA Pin Test End-Entity' otherCA "*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com,*.pinning.example.com"
+
$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -L -n localhostAndExampleCom -r > $OUTPUT_DIR/default-ee.der
# A cert that is like localhostAndExampleCom, but with a different serial number for
# testing the "OCSP response is from the right issuer, but it is for the wrong cert"
diff --git a/security/manager/ssl/tests/unit/tlsserver/key3.db b/security/manager/ssl/tests/unit/tlsserver/key3.db
index 283e8fb..2d4dd29 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/key3.db and b/security/manager/ssl/tests/unit/tlsserver/key3.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der b/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
index 794fb9c..742bb86 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der and b/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/secmod.db b/security/manager/ssl/tests/unit/tlsserver/secmod.db
index a5f2f60..7a0e2b5 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/secmod.db and b/security/manager/ssl/tests/unit/tlsserver/secmod.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/test-ca.der b/security/manager/ssl/tests/unit/tlsserver/test-ca.der
index f4c4863..356de5b 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/test-ca.der and b/security/manager/ssl/tests/unit/tlsserver/test-ca.der differ
diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini
index 7935c2d..24b0ffc 100644
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -78,3 +78,7 @@ requesttimeoutfactor = 4
run-sequentially = hardcoded ports
# Bug 676972: this test times out on Android and B2G
skip-if = os == "android" || buildapp == "b2g"
+[test_pinning.js]
+run-sequentially = hardcoded ports
+# Bug 676972: test fails consistently on Android and B2G
+fail-if = os == "android" || buildapp == "b2g"
\ No newline at end of file
diff --git a/security/manager/tools/PreloadedHPKPins.json b/security/manager/tools/PreloadedHPKPins.json
new file mode 100644
index 0000000..ed3b9b7
--- /dev/null
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -0,0 +1,247 @@
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// The top-level element is a dictionary with two keys: "pinsets" maps details
+// of certificate pinning to a name and "entries" contains the HPKP details for
+// each host.
+//
+// "pinsets" is a list of objects. Each object has the following members:
+// name: (string) the name of the pinset
+// sha256_hashes: (list of strings) the set of allowed SPKIs hashes
+//
+// For a given pinset, a certificate is accepted if at least one of the
+// Subject Public Key Infos (SPKIs) is found in the chain. SPKIs are specified
+// as names, which must match up with the name given in the Mozilla root store.
+//
+// "entries" is a list of objects. Each object has the following members:
+// name: (string) the DNS name of the host in question
+// include_subdomains: (optional bool) whether subdomains of |name| are also covered
+// pins: (string) the |name| member of an object in |pinsets|
+//
+// "extra_certs" is a list of base64-encoded certificates. These are used in
+// pinsets that reference certificates not in our root program (for example,
+// Facebook).
+
+// equifax -> aus3
+// Geotrust Primary -> www.mozilla.org
+// Geotrust Global -> *. addons.mozilla.org
+{
+ "chromium_data" : {
+ "cert_file_url": "https://src.chromium.org/chrome/trunk/src/net/http/transport_security_state_static.certs",
+ "json_file_url": "https://src.chromium.org/chrome/trunk/src/net/http/transport_security_state_static.json",
+ "substitute_pinsets": {
+ // Use the larger google_root_pems pinset instead of google
+ "google": "google_root_pems"
+ },
+ "production_pinsets": [
+ "google_root_pems"
+ ],
+ "production_domains": [
+ // Chrome's test domain.
+ "pinningtest.appspot.com",
+ // Dropbox
+ "dropbox.com",
+ "www.dropbox.com",
+ // Twitter
+ "api.twitter.com",
+ "business.twitter.com",
+ "dev.twitter.com",
+ "mobile.twitter.com",
+ "oauth.twitter.com",
+ "platform.twitter.com",
+ "twimg.com",
+ "www.twitter.com",
+ // Tor
+ "torproject.org",
+ "blog.torproject.org",
+ "check.torproject.org",
+ "dist.torproject.org",
+ "www.torproject.org"
+ ],
+ "exclude_domains" : [
+ // Chrome's entry for twitter.com doesn't include subdomains, so replace
+ // it with our own entry below which also uses an expanded pinset.
+ "twitter.com"
+ ]
+ },
+ "pinsets": [
+ {
+ // From bug 772756, mozilla uses GeoTrust, Digicert and Thawte. Our
+ // cdn sites use Verisign and Baltimore. We exclude 1024-bit root certs
+ // from all providers. geotrust ca info:
+ // http://www.geotrust.com/resources/root-certificates/index.html
+ "name": "mozilla",
+ "sha256_hashes": [
+ "Baltimore CyberTrust Root",
+ "DigiCert Assured ID Root CA",
+ "DigiCert Global Root CA",
+ "DigiCert High Assurance EV Root CA",
+ "GeoTrust Global CA",
+ "GeoTrust Global CA 2",
+ "GeoTrust Primary Certification Authority",
+ "GeoTrust Primary Certification Authority - G2",
+ "GeoTrust Primary Certification Authority - G3",
+ "GeoTrust Universal CA",
+ "GeoTrust Universal CA 2",
+ "thawte Primary Root CA",
+ "thawte Primary Root CA - G2",
+ "thawte Primary Root CA - G3",
+ "Verisign Class 1 Public Primary Certification Authority - G3",
+ "Verisign Class 2 Public Primary Certification Authority - G3",
+ "Verisign Class 3 Public Primary Certification Authority - G3",
+ "VeriSign Class 3 Public Primary Certification Authority - G4",
+ "VeriSign Class 3 Public Primary Certification Authority - G5",
+ "Verisign Class 4 Public Primary Certification Authority - G3",
+ "VeriSign Universal Root Certification Authority"
+ ]
+ },
+ {
+ "name": "mozilla_services",
+ "sha256_hashes": [
+ "DigiCert Global Root CA"
+ ]
+ },
+ // For pinning tests on pinning.example.com, the certificate must be 'End
+ // Entity Test Cert'
+ {
+ "name": "mozilla_test",
+ "sha256_hashes": [
+ "End Entity Test Cert"
+ ]
+ },
+ // Google's root PEMs. Chrome pins only to their intermediate certs, but
+ // they'd like us to be more liberal. For the initial list, we are using
+ // the certs from http://pki.google.com/roots.pem.
+ // We have no built-in for commented out CAs.
+ {
+ "name": "google_root_pems",
+ "sha256_hashes": [
+ "AddTrust External Root",
+ "AddTrust Low-Value Services Root",
+ "AddTrust Public Services Root",
+ "AddTrust Qualified Certificates Root",
+ "AffirmTrust Commercial",
+ "AffirmTrust Networking",
+ "AffirmTrust Premium",
+ "AffirmTrust Premium ECC",
+ "America Online Root Certification Authority 1",
+ "America Online Root Certification Authority 2",
+ "Baltimore CyberTrust Root",
+ "Comodo AAA Services root",
+ "COMODO Certification Authority",
+ "COMODO ECC Certification Authority",
+ "Comodo Secure Services root",
+ "Comodo Trusted Services root",
+ "Cybertrust Global Root",
+ "DigiCert Assured ID Root CA",
+ "DigiCert Global Root CA",
+ "DigiCert High Assurance EV Root CA",
+ "Entrust.net Premium 2048 Secure Server CA",
+ // "Entrust.net Secure Server CA",
+ "Entrust Root Certification Authority",
+ "Equifax Secure CA",
+ "Equifax Secure eBusiness CA 1",
+ // "Equifax Secure eBusiness CA 2",
+ "Equifax Secure Global eBusiness CA",
+ "GeoTrust Global CA",
+ "GeoTrust Global CA 2",
+ "GeoTrust Primary Certification Authority",
+ "GeoTrust Primary Certification Authority - G2",
+ "GeoTrust Primary Certification Authority - G3",
+ "GeoTrust Universal CA",
+ "GeoTrust Universal CA 2",
+ "GlobalSign Root CA",
+ "GlobalSign Root CA - R2",
+ "GlobalSign Root CA - R3",
+ "Go Daddy Class 2 CA",
+ "Go Daddy Root Certificate Authority - G2",
+ // "GTE CyberTrust Global Root",
+ "Network Solutions Certificate Authority",
+ // "RSA Root Certificate 1",
+ "Starfield Class 2 CA",
+ "Starfield Root Certificate Authority - G2",
+ "Starfield Services Root Certificate Authority - G2",
+ "StartCom Certification Authority",
+ "StartCom Certification Authority",
+ "StartCom Certification Authority G2",
+ "TC TrustCenter Class 2 CA II",
+ "TC TrustCenter Class 3 CA II",
+ "TC TrustCenter Universal CA I",
+ "TC TrustCenter Universal CA III",
+ "Thawte Premium Server CA",
+ "thawte Primary Root CA",
+ "thawte Primary Root CA - G2",
+ "thawte Primary Root CA - G3",
+ "Thawte Server CA",
+ "UTN DATACorp SGC Root CA",
+ "UTN USERFirst Hardware Root CA",
+ // "ValiCert Class 1 VA",
+ // "ValiCert Class 2 VA",
+ "Verisign Class 3 Public Primary Certification Authority",
+ "Verisign Class 3 Public Primary Certification Authority",
+ "Verisign Class 3 Public Primary Certification Authority - G2",
+ "Verisign Class 3 Public Primary Certification Authority - G3",
+ "VeriSign Class 3 Public Primary Certification Authority - G4",
+ "VeriSign Class 3 Public Primary Certification Authority - G5",
+ "Verisign Class 4 Public Primary Certification Authority - G3",
+ "VeriSign Universal Root Certification Authority",
+ "XRamp Global CA Root"
+ ]
+ },
+ {
+ "name": "facebook",
+ "sha256_hashes": [
+ "Verisign Class 3 Public Primary Certification Authority - G3",
+ "DigiCert High Assurance EV Root CA",
+ "DigiCert ECC Secure Server CA"
+ ]
+ }
+ ],
+
+ "entries": [
+ // Only domains that are operationally crucial to Firefox can have per-host
+ // telemetry reporting (the "id") field
+ { "name": "addons.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false, "id": 1 },
+ { "name": "addons.mozilla.net", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false, "id": 2 },
+ { "name": "aus4.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": true, "id": 3 },
+ { "name": "accounts.firefox.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 4 },
+ { "name": "api.accounts.firefox.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 5 },
+ { "name": "cdn.mozilla.net", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false },
+ { "name": "cdn.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false },
+ { "name": "media.mozilla.com", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false },
+ { "name": "services.mozilla.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": true },
+ { "name": "include-subdomains.pinning.example.com",
+ "include_subdomains": true, "pins": "mozilla_test",
+ "test_mode": false },
+ // Example domain to collect per-host stats for telemetry tests.
+ { "name": "exclude-subdomains.pinning.example.com",
+ "include_subdomains": false, "pins": "mozilla_test",
+ "test_mode": false, "id": 0 },
+ { "name": "test-mode.pinning.example.com", "include_subdomains": true,
+ "pins": "mozilla_test", "test_mode": true },
+ // Expand twitter's pinset to include all of *.twitter.com and use
+ // twitterCDN. More specific rules take precedence because we search for
+ // exact domain name first.
+ { "name": "twitter.com", "include_subdomains": true,
+ "pins": "twitterCDN", "test_mode": false },
+ // Facebook (not pinned by Chrome)
+ { "name": "facebook.com", "include_subdomains": true,
+ "pins": "facebook", "test_mode": true }
+ ],
+
+ "extra_certificates": [
+ // DigiCert ECC Secure Server CA (for Facebook)
+ "MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBTZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6gLGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/AbuiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/63qttnMe2uuzO58pzZNvfBDcKAEmzP58
mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoBUEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQdEa8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc="
+ ]
+}
diff --git a/security/manager/tools/genHPKPStaticPins.js b/security/manager/tools/genHPKPStaticPins.js
new file mode 100644
index 0000000..d16d017
--- /dev/null
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -0,0 +1,576 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// How to run this file:
+// 1. [obtain firefox source code]
+// 2. [build/obtain firefox binaries]
+// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
+// [path to]/genHPKPStaticpins.js \
+// [absolute path to]/PreloadedHPKPins.json \
+// [absolute path to]/default-ee.der \
+// [absolute path to]/StaticHPKPins.h
+
+if (arguments.length != 3) {
+ throw "Usage: genHPKPStaticPins.js " +
+ "<absolute path to PreloadedHPKPins.json> " +
+ "<absolute path to default-ee.der> " +
+ "<absolute path to StaticHPKPins.h>";
+}
+
+const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
+
+let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
+let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+let gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+
+const BUILT_IN_NICK_PREFIX = "Builtin Object Token:";
+const SHA1_PREFIX = "sha1/";
+const SHA256_PREFIX = "sha256/";
+const GOOGLE_PIN_PREFIX = "GOOGLE_PIN_";
+
+// Pins expire in 14 weeks (6 weeks on Beta + 8 weeks on stable)
+const PINNING_MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 14;
+
+const FILE_HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
+" * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
+" * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
+"\n" +
+"/*****************************************************************************/\n" +
+"/* This is an automatically generated file. If you're not */\n" +
+"/* PublicKeyPinningService.cpp, you shouldn't be #including it. */\n" +
+"/*****************************************************************************/\n" +
+"#include <stdint.h>" +
+"\n";
+
+const DOMAINHEADER = "/* Domainlist */\n" +
+ "struct TransportSecurityPreload {\n" +
+ " const char* mHost;\n" +
+ " const bool mIncludeSubdomains;\n" +
+ " const bool mTestMode;\n" +
+ " const bool mIsMoz;\n" +
+ " const int32_t mId;\n" +
+ " const StaticPinset *pinset;\n" +
+ "};\n\n";
+
+const PINSETDEF = "/* Pinsets are each an ordered list by the actual value of the fingerprint */\n" +
+ "struct StaticFingerprints {\n" +
+ " const size_t size;\n" +
+ " const char* const* data;\n" +
+ "};\n\n" +
+ "struct StaticPinset {\n" +
+ " const StaticFingerprints* sha1;\n" +
+ " const StaticFingerprints* sha256;\n" +
+ "};\n\n";
+
+// Command-line arguments
+var gStaticPins = parseJson(arguments[0]);
+var gTestCertFile = arguments[1];
+
+// Open the output file.
+let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+file.initWithPath(arguments[2]);
+let gFileOutputStream = FileUtils.openSafeFileOutputStream(file);
+
+function writeString(string) {
+ gFileOutputStream.write(string, string.length);
+}
+
+function readFileToString(filename) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ file.initWithPath(filename);
+ let stream = Cc["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Ci.nsIFileInputStream);
+ stream.init(file, -1, 0, 0);
+ let buf = NetUtil.readInputStreamToString(stream, stream.available());
+ return buf;
+}
+
+function stripComments(buf) {
+ var lines = buf.split("\n");
+ let entryRegex = /^\s*\/\//;
+ let data = "";
+ for (let i = 0; i < lines.length; ++i) {
+ let match = entryRegex.exec(lines[i]);
+ if (!match) {
+ data = data + lines[i];
+ }
+ }
+ return data;
+}
+
+function isBuiltinToken(tokenName) {
+ return tokenName == "Builtin Object Token";
+}
+
+function isCertBuiltIn(cert) {
+ let tokenNames = cert.getAllTokenNames({});
+ if (!tokenNames) {
+ return false;
+ }
+ if (tokenNames.some(isBuiltinToken)) {
+ return true;
+ }
+ return false;
+}
+
+function download(filename) {
+ var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Ci.nsIXMLHttpRequest);
+ req.open("GET", filename, false); // doing the request synchronously
+ try {
+ req.send();
+ }
+ catch (e) {
+ throw "ERROR: problem downloading '" + filename + "': " + e;
+ }
+
+ if (req.status != 200) {
+ throw("ERROR: problem downloading '" + filename + "': status " +
+ req.status);
+ }
+ return req.responseText;
+}
+
+function downloadAsJson(filename) {
+ // we have to filter out '//' comments
+ var result = download(filename).replace(/\/\/[^\n]*\n/g, "");
+ var data = null;
+ try {
+ data = JSON.parse(result);
+ }
+ catch (e) {
+ throw "ERROR: could not parse data from '" + filename + "': " + e;
+ }
+ return data;
+}
+
+// Returns a Subject Public Key Digest from the given pem, if it exists.
+function getSKDFromPem(pem) {
+ let cert = gCertDB.constructX509FromBase64(pem, pem.length);
+ return cert.sha256SubjectPublicKeyInfoDigest;
+}
+
+// Downloads the static certs file and tries to map Google Chrome nicknames
+// to Mozilla nicknames, as well as storing any hashes for pins for which we
+// don't have root PEMs. Each entry consists of a line containing the name of
+// the pin followed either by a hash in the format "sha1/" + base64(hash), or
+// a PEM encoded certificate. For certificates that we have in our database,
+// return a map of Google's nickname to ours. For ones that aren't return a
+// map of Google's nickname to sha1 values. This code is modeled after agl's
+// https://github.com/agl/transport-security-state-generate, which doesn't
+// live in the Chromium repo because go is not an official language in
+// Chromium.
+// For all of the entries in this file:
+// - If the entry has a hash format, find the Mozilla pin name (cert nickname)
+// and stick the hash into certSKDToName
+// - If the entry has a PEM format, parse the PEM, find the Mozilla pin name
+// and stick the hash in certSKDToName
+// We MUST be able to find a corresponding cert nickname for the Chrome names,
+// otherwise we skip all pinsets referring to that Chrome name.
+function downloadAndParseChromeCerts(filename, certSKDToName) {
+ // Prefixes that we care about.
+ const BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
+ const END_CERT = "-----END CERTIFICATE-----";
+
+ // Parsing states.
+ const PRE_NAME = 0;
+ const POST_NAME = 1;
+ const IN_CERT = 2;
+ let state = PRE_NAME;
+
+ let lines = download(filename).split("\n");
+ let name = "";
+ let pemCert = "";
+ let hash = "";
+ let chromeNameToHash = {};
+ let chromeNameToMozName = {}
+ for (let i = 0; i < lines.length; ++i) {
+ let line = lines[i];
+ // Skip comments and newlines.
+ if (line.length == 0 || line[0] == '#') {
+ continue;
+ }
+ switch(state) {
+ case PRE_NAME:
+ chromeName = line;
+ state = POST_NAME;
+ break;
+ case POST_NAME:
+ if (line.startsWith(SHA1_PREFIX) ||
+ line.startsWith(SHA256_PREFIX)) {
+ if (line.startsWith(SHA1_PREFIX)) {
+ hash = line.substring(SHA1_PREFIX.length);
+ } else if (line.startsWith(SHA256_PREFIX)) {
+ hash = line.substring(SHA256_PREFIX);
+ }
+ // Store the entire prefixed hash, so we can disambiguate sha1 from
+ // sha256 later.
+ chromeNameToHash[chromeName] = line;
+ certNameToSKD[chromeName] = hash;
+ certSKDToName[hash] = chromeName;
+ state = PRE_NAME;
+ } else if (line.startsWith(BEGIN_CERT)) {
+ state = IN_CERT;
+ } else {
+ throw "ERROR: couldn't parse Chrome certificate file " + line;
+ }
+ break;
+ case IN_CERT:
+ if (line.startsWith(END_CERT)) {
+ state = PRE_NAME;
+ hash = getSKDFromPem(pemCert);
+ pemCert = "";
+ if (hash in certSKDToName) {
+ mozName = certSKDToName[hash];
+ } else {
+ // Not one of our built-in certs. Prefix the name with
+ // GOOGLE_PIN_.
+ mozName = GOOGLE_PIN_PREFIX + chromeName;
+ dump("Can't find hash in builtin certs for Chrome nickname " +
+ chromeName + ", inserting " + mozName + "\n");
+ certSKDToName[hash] = mozName;
+ certNameToSKD[mozName] = hash;
+ }
+ chromeNameToMozName[chromeName] = mozName;
+ } else {
+ pemCert += line;
+ }
+ break;
+ default:
+ throw "ERROR: couldn't parse Chrome certificate file " + line;
+ }
+ }
+ return [ chromeNameToHash, chromeNameToMozName ];
+}
+
+// We can only import pinsets from chrome if for every name in the pinset:
+// - We have a hash from Chrome's static certificate file
+// - We have a builtin cert
+// If the pinset meets these requirements, we store a map array of pinset
+// objects:
+// {
+// pinset_name : {
+// // Array of names with entries in certNameToSKD
+// sha1_hashes: [],
+// sha256_hashes: []
+// }
+// }
+// and an array of imported pinset entries:
+// { name: string, include_subdomains: boolean, test_mode: boolean,
+// pins: pinset_name }
+function downloadAndParseChromePins(filename,
+ chromeNameToHash,
+ chromeNameToMozName,
+ certNameToSKD,
+ certSKDToName) {
+ let chromePreloads = downloadAsJson(filename);
+ let chromePins = chromePreloads.pinsets;
+ let chromeImportedPinsets = {};
+ let chromeImportedEntries = [];
+
+ chromePins.forEach(function(pin) {
+ let valid = true;
+ let pinset = { name: pin.name, sha1_hashes: [], sha256_hashes: [] };
+ // Translate the Chrome pinset format to ours
+ pin.static_spki_hashes.forEach(function(name) {
+ if (name in chromeNameToHash) {
+ let hash = chromeNameToHash[name];
+ if (hash.startsWith(SHA1_PREFIX)) {
+ hash = hash.substring(SHA1_PREFIX.length);
+ pinset.sha1_hashes.push(certSKDToName[hash]);
+ } else if (hash.startsWith(SHA256_PREFIX)) {
+ hash = hash.substring(SHA256_PREFIX.length);
+ pinset.sha256_hashes.push(certSKDToName[hash]);
+ } else {
+ throw("Unsupported hash type: " + chromeNameToHash[name]);
+ }
+ // We should have already added hashes for all of these when we
+ // imported the certificate file.
+ if (!certNameToSKD[name]) {
+ throw("No hash for name: " + name);
+ }
+ } else if (name in chromeNameToMozName) {
+ pinset.sha256_hashes.push(chromeNameToMozName[name]);
+ } else {
+ dump("Skipping Chrome pinset " + pinset.name + ", couldn't find " +
+ "builtin " + name + " from cert file\n");
+ valid = false;
+ }
+ });
+ if (valid) {
+ chromeImportedPinsets[pinset.name] = pinset;
+ }
+ });
+
+ // Grab the domain entry lists. Chrome's entry format is similar to
+ // ours, except theirs includes a HSTS mode.
+ const cData = gStaticPins.chromium_data;
+ let entries = chromePreloads.entries;
+ entries.forEach(function(entry) {
+ let pinsetName = cData.substitute_pinsets[entry.pins];
+ if (!pinsetName) {
+ pinsetName = entry.pins;
+ }
+ let isProductionDomain =
+ (cData.production_domains.indexOf(entry.name) != -1);
+ let isProductionPinset =
+ (cData.production_pinsets.indexOf(pinsetName) != -1);
+ let excludeDomain =
+ (cData.exclude_domains.indexOf(entry.name) != -1);
+ let isTestMode = !isProductionPinset && !isProductionDomain;
+ if (entry.pins && !excludeDomain && chromeImportedPinsets[entry.pins]) {
+ chromeImportedEntries.push({
+ name: entry.name,
+ include_subdomains: entry.include_subdomains,
+ test_mode: isTestMode,
+ is_moz: false,
+ pins: pinsetName });
+ }
+ });
+ return [ chromeImportedPinsets, chromeImportedEntries ];
+}
+
+// Returns a pair of maps [certNameToSKD, certSKDToName] between cert
+// nicknames and digests of the SPKInfo for the mozilla trust store
+function loadNSSCertinfo(derTestFile, extraCertificates) {
+ let allCerts = gCertDB.getCerts();
+ let enumerator = allCerts.getEnumerator();
+ let certNameToSKD = {};
+ let certSKDToName = {};
+ while (enumerator.hasMoreElements()) {
+ let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+ if (!isCertBuiltIn(cert)) {
+ continue;
+ }
+ let name = cert.nickname.substr(BUILT_IN_NICK_PREFIX.length);
+ let SKD = cert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+
+ for (let cert of extraCertificates) {
+ let name = cert.commonName;
+ let SKD = cert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+
+ {
+ // A certificate for *.example.com.
+ let der = readFileToString(derTestFile);
+ let testCert = gCertDB.constructX509(der, der.length);
+ // We can't include this cert in the previous loop, because it skips
+ // non-builtin certs and the nickname is not built-in to the cert.
+ let name = "End Entity Test Cert";
+ let SKD = testCert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+ return [certNameToSKD, certSKDToName];
+}
+
+function parseJson(filename) {
+ let json = stripComments(readFileToString(filename));
+ return JSON.parse(json);
+}
+
+function nameToAlias(certName) {
+ // change the name to a string valid as a c identifier
+ // remove non-ascii characters
+ certName = certName.replace( /[^[:ascii:]]/g, "_");
+ // replace non word characters
+ certName = certName.replace(/[^A-Za-z0-9]/g ,"_");
+
+ return "k" + certName + "Fingerprint";
+}
+
+function compareByName (a, b) {
+ return a.name.localeCompare(b.name);
+}
+
+function genExpirationTime() {
+ let now = new Date();
+ let nowMillis = now.getTime();
+ let expirationMillis = nowMillis + (PINNING_MINIMUM_REQUIRED_MAX_AGE * 1000);
+ let expirationMicros = expirationMillis * 1000;
+ return "static const PRTime kPreloadPKPinsExpirationTime = INT64_C(" +
+ expirationMicros +");\n";
+}
+
+function writeFullPinset(certNameToSKD, certSKDToName, pinset) {
+ // We aren't guaranteed to have sha1 hashes in our own imported pins.
+ let prefix = "kPinset_" + pinset.name;
+ let sha1Name = "nullptr";
+ let sha256Name = "nullptr";
+ if (pinset.sha1_hashes && pinset.sha1_hashes.length > 0) {
+ writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
+ pinset.sha1_hashes, "sha1");
+ sha1Name = "&" + prefix + "_sha1";
+ }
+ if (pinset.sha256_hashes && pinset.sha256_hashes.length > 0) {
+ writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
+ pinset.sha256_hashes, "sha256");
+ sha256Name = "&" + prefix + "_sha256";
+ }
+ writeString("static const StaticPinset " + prefix + " = {\n" +
+ " " + sha1Name + ",\n " + sha256Name + "\n};\n\n");
+}
+
+function writeFingerprints(certNameToSKD, certSKDToName, name, hashes, type) {
+ let varPrefix = "kPinset_" + name + "_" + type;
+ writeString("static const char* " + varPrefix + "_Data[] = {\n");
+ let SKDList = [];
+ for (let certName of hashes) {
+ if (!(certName in certNameToSKD)) {
+ throw "Can't find " + certName + " in certNameToSKD";
+ }
+ SKDList.push(certNameToSKD[certName]);
+ }
+ for (let skd of SKDList.sort()) {
+ writeString(" " + nameToAlias(certSKDToName[skd]) + ",\n");
+ }
+ if (hashes.length == 0) {
+ // ANSI C requires that an initialiser list be non-empty.
+ writeString(" 0\n");
+ }
+ writeString("};\n");
+ writeString("static const StaticFingerprints " + varPrefix + " = {\n " +
+ "sizeof(" + varPrefix + "_Data) / sizeof(const char*),\n " + varPrefix +
+ "_Data\n};\n\n");
+}
+
+function writeEntry(entry) {
+ let printVal = " { \"" + entry.name + "\",\ ";
+ if (entry.include_subdomains) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ // Default to test mode if not specified.
+ let testMode = true;
+ if (entry.hasOwnProperty("test_mode")) {
+ testMode = entry.test_mode;
+ }
+ if (testMode) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ if (entry.is_moz || (entry.pins == "mozilla")) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ if (entry.id >= 256) {
+ throw("Not enough buckets in histogram");
+ }
+ if (entry.id >= 0) {
+ printVal += entry.id + ", ";
+ } else {
+ printVal += "-1, ";
+ }
+ printVal += "&kPinset_" + entry.pins;
+ printVal += " },\n";
+ writeString(printVal);
+}
+
+function writeDomainList(chromeImportedEntries) {
+ writeString("/* Sort hostnames for binary search. */\n");
+ writeString("static const TransportSecurityPreload " +
+ "kPublicKeyPinningPreloadList[] = {\n");
+ let count = 0;
+ let sortedEntries = gStaticPins.entries;
+ sortedEntries.push.apply(sortedEntries, chromeImportedEntries);
+ for (let entry of sortedEntries.sort(compareByName)) {
+ count++;
+ writeEntry(entry);
+ }
+ writeString("};\n");
+
+ writeString("\n// Pinning Preload List Length = " + count + ";\n");
+ writeString("\nstatic const int32_t kUnknownId = -1;\n");
+}
+
+function writeFile(certNameToSKD, certSKDToName,
+ chromeImportedPinsets, chromeImportedEntries) {
+ // Compute used pins from both Chrome's and our pinsets, so we can output
+ // them later.
+ usedFingerprints = {};
+ gStaticPins.pinsets.forEach(function(pinset) {
+ // We aren't guaranteed to have sha1_hashes in our own JSON.
+ if (pinset.sha1_hashes) {
+ pinset.sha1_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ }
+ if (pinset.sha256_hashes) {
+ pinset.sha256_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ }
+ });
+ for (let key in chromeImportedPinsets) {
+ let pinset = chromeImportedPinsets[key];
+ pinset.sha1_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ pinset.sha256_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ }
+
+ writeString(FILE_HEADER);
+
+ // Write actual fingerprints.
+ Object.keys(usedFingerprints).sort().forEach(function(certName) {
+ if (certName) {
+ writeString("/* " + certName + " */\n");
+ writeString("static const char " + nameToAlias(certName) + "[] =\n");
+ writeString(" \"" + certNameToSKD[certName] + "\";\n");
+ writeString("\n");
+ }
+ });
+
+ // Write the pinsets
+ writeString(PINSETDEF);
+ writeString("/* PreloadedHPKPins.json pinsets */\n");
+ gStaticPins.pinsets.sort(compareByName).forEach(function(pinset) {
+ writeFullPinset(certNameToSKD, certSKDToName, pinset);
+ });
+ writeString("/* Chrome static pinsets */\n");
+ for (let key in chromeImportedPinsets) {
+ writeFullPinset(certNameToSKD, certSKDToName, chromeImportedPinsets[key]);
+ }
+
+ // Write the domainlist entries.
+ writeString(DOMAINHEADER);
+ writeDomainList(chromeImportedEntries);
+ writeString("\n");
+ writeString(genExpirationTime());
+}
+
+function loadExtraCertificates(certStringList) {
+ let constructedCerts = [];
+ for (let certString of certStringList) {
+ constructedCerts.push(gCertDB.constructX509FromBase64(certString));
+ }
+ return constructedCerts;
+}
+
+let extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates);
+let [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(gTestCertFile,
+ extraCertificates);
+let [ chromeNameToHash, chromeNameToMozName ] = downloadAndParseChromeCerts(
+ gStaticPins.chromium_data.cert_file_url, certSKDToName);
+let [ chromeImportedPinsets, chromeImportedEntries ] =
+ downloadAndParseChromePins(gStaticPins.chromium_data.json_file_url,
+ chromeNameToHash, chromeNameToMozName, certNameToSKD, certSKDToName);
+
+writeFile(certNameToSKD, certSKDToName, chromeImportedPinsets,
+ chromeImportedEntries);
+
+FileUtils.closeSafeFileOutputStream(gFileOutputStream);
diff --git a/security/pkix/include/pkix/Result.h b/security/pkix/include/pkix/Result.h
new file mode 100644
index 0000000..e82e6428
--- /dev/null
+++ b/security/pkix/include/pkix/Result.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mozilla_pkix__Result_h
+#define mozilla_pkix__Result_h
+
+#include <cassert>
+
+#include "pkix/enumclass.h"
+
+namespace mozilla { namespace pkix {
+
+static const unsigned int FATAL_ERROR_FLAG = 0x800;
+
+// The first argument to MOZILLA_PKIX_MAP() is used for building the mapping
+// from error code to error name in MapResultToName.
+//
+// The second argument is for defining the value for the enum literal in the
+// Result enum class.
+//
+// The third argument to MOZILLA_PKIX_MAP() is used, along with the first
+// argument, for maintaining the mapping of mozilla::pkix error codes to
+// NSS/NSPR error codes in pkixnss.cpp.
+#define MOZILLA_PKIX_MAP_LIST \
+ MOZILLA_PKIX_MAP(Success, 0, 0) \
+ MOZILLA_PKIX_MAP(ERROR_BAD_DER, 1, \
+ SEC_ERROR_BAD_DER) \
+ MOZILLA_PKIX_MAP(ERROR_CA_CERT_INVALID, 2, \
+ SEC_ERROR_CA_CERT_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_BAD_SIGNATURE, 3, \
+ SEC_ERROR_BAD_SIGNATURE) \
+ MOZILLA_PKIX_MAP(ERROR_CERT_BAD_ACCESS_LOCATION, 4, \
+ SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \
+ MOZILLA_PKIX_MAP(ERROR_CERT_NOT_IN_NAME_SPACE, 5, \
+ SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \
+ MOZILLA_PKIX_MAP(ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 6, \
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \
+ MOZILLA_PKIX_MAP(ERROR_CONNECT_REFUSED, 7, \
+ PR_CONNECT_REFUSED_ERROR) \
+ MOZILLA_PKIX_MAP(ERROR_EXPIRED_CERTIFICATE, 8, \
+ SEC_ERROR_EXPIRED_CERTIFICATE) \
+ MOZILLA_PKIX_MAP(ERROR_EXTENSION_VALUE_INVALID, 9, \
+ SEC_ERROR_EXTENSION_VALUE_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_INADEQUATE_CERT_TYPE, 10, \
+ SEC_ERROR_INADEQUATE_CERT_TYPE) \
+ MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_USAGE, 11, \
+ SEC_ERROR_INADEQUATE_KEY_USAGE) \
+ MOZILLA_PKIX_MAP(ERROR_INVALID_ALGORITHM, 12, \
+ SEC_ERROR_INVALID_ALGORITHM) \
+ MOZILLA_PKIX_MAP(ERROR_INVALID_TIME, 13, \
+ SEC_ERROR_INVALID_TIME) \
+ MOZILLA_PKIX_MAP(ERROR_KEY_PINNING_FAILURE, 14, \
+ MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \
+ MOZILLA_PKIX_MAP(ERROR_PATH_LEN_CONSTRAINT_INVALID, 15, \
+ SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_POLICY_VALIDATION_FAILED, 16, \
+ SEC_ERROR_POLICY_VALIDATION_FAILED) \
+ MOZILLA_PKIX_MAP(ERROR_REVOKED_CERTIFICATE, 17, \
+ SEC_ERROR_REVOKED_CERTIFICATE) \
+ MOZILLA_PKIX_MAP(ERROR_UNKNOWN_CRITICAL_EXTENSION, 18, \
+ SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \
+ MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ERROR, 19, \
+ PR_UNKNOWN_ERROR) \
+ MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ISSUER, 20, \
+ SEC_ERROR_UNKNOWN_ISSUER) \
+ MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_CERT, 21, \
+ SEC_ERROR_UNTRUSTED_CERT) \
+ MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_ISSUER, 22, \
+ SEC_ERROR_UNTRUSTED_ISSUER) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_BAD_SIGNATURE, 23, \
+ SEC_ERROR_OCSP_BAD_SIGNATURE) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_INVALID_SIGNING_CERT, 24, \
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_REQUEST, 25, \
+ SEC_ERROR_OCSP_MALFORMED_REQUEST) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_RESPONSE, 26, \
+ SEC_ERROR_OCSP_MALFORMED_RESPONSE) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_OLD_RESPONSE, 27, \
+ SEC_ERROR_OCSP_OLD_RESPONSE) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_REQUEST_NEEDS_SIG, 28, \
+ SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONDER_CERT_INVALID, 29, \
+ SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_SERVER_ERROR, 30, \
+ SEC_ERROR_OCSP_SERVER_ERROR) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_TRY_SERVER_LATER, 31, \
+ SEC_ERROR_OCSP_TRY_SERVER_LATER) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_UNAUTHORIZED_REQUEST, 32, \
+ SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, 33, \
+ SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_CERT, 34, \
+ SEC_ERROR_OCSP_UNKNOWN_CERT) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_FUTURE_RESPONSE, 35, \
+ SEC_ERROR_OCSP_FUTURE_RESPONSE) \
+ MOZILLA_PKIX_MAP(ERROR_INVALID_KEY, 36, \
+ SEC_ERROR_INVALID_KEY) \
+ MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_KEYALG, 37, \
+ SEC_ERROR_UNSUPPORTED_KEYALG) \
+ MOZILLA_PKIX_MAP(ERROR_EXPIRED_ISSUER_CERTIFICATE, 38, \
+ SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE) \
+ MOZILLA_PKIX_MAP(ERROR_CA_CERT_USED_AS_END_ENTITY, 39, \
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY) \
+ MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_SIZE, 40, \
+ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \
+ SEC_ERROR_INVALID_ARGS) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \
+ PR_INVALID_STATE_ERROR) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \
+ SEC_ERROR_LIBRARY_FAILURE) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \
+ SEC_ERROR_NO_MEMORY) \
+ /* nothing here */
+
+MOZILLA_PKIX_ENUM_CLASS Result
+{
+#define MOZILLA_PKIX_MAP(name, value, nss_name) name = value,
+ MOZILLA_PKIX_MAP_LIST
+#undef MOZILLA_PKIX_MAP
+};
+
+// Returns the stringified name of the given result, e.g. "Result::Success",
+// or nullptr if result is unknown (invalid).
+const char* MapResultToName(Result result);
+
+// We write many comparisons as (x != Success), and this shortened name makes
+// those comparisons clearer, especially because the shortened name often
+// results in less line wrapping.
+//
+// Visual Studio before VS2013 does not support "enum class," so
+// Result::Success will already be visible in this scope, and compilation will
+// fail if we try to define a variable with that name here.
+#if !defined(_MSC_VER) || (_MSC_VER >= 1700)
+static const Result Success = Result::Success;
+#endif
+
+inline bool
+IsFatalError(Result rv)
+{
+ return (static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG) != 0;
+}
+
+inline Result
+NotReached(const char* /*explanation*/, Result result)
+{
+ assert(false);
+ return result;
+}
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__Result_h
diff --git a/security/pkix/include/pkix/Time.h b/security/pkix/include/pkix/Time.h
new file mode 100644
index 0000000..b8d6ee9
--- /dev/null
+++ b/security/pkix/include/pkix/Time.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/* Copyright 2014 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mozilla_pkix__Time_h
+#define mozilla_pkix__Time_h
+
+#include <ctime>
+#include <limits>
+#include <stdint.h>
+
+#include "pkix/Result.h"
+
+namespace mozilla { namespace pkix {
+
+// Time with a range from the first second of year 0 (AD) through at least the
+// last second of year 9999, which is the range of legal times in X.509 and
+// OCSP. This type has second-level precision. The time zone is always UTC.
+//
+// Pass by value, not by reference.
+class Time
+{
+public:
+ // Construct an uninitilized instance.
+ //
+ // This will fail to compile because there is no default constructor:
+ // Time x;
+ //
+ // This will succeed, leaving the time uninitialized:
+ // Time x(Time::uninitialized);
+ enum Uninitialized { uninitialized };
+ explicit Time(Uninitialized) { }
+
+ bool operator==(const Time& other) const
+ {
+ return elapsedSecondsAD == other.elapsedSecondsAD;
+ }
+ bool operator>(const Time& other) const
+ {
+ return elapsedSecondsAD > other.elapsedSecondsAD;
+ }
+ bool operator>=(const Time& other) const
+ {
+ return elapsedSecondsAD >= other.elapsedSecondsAD;
+ }
+ bool operator<(const Time& other) const
+ {
+ return elapsedSecondsAD < other.elapsedSecondsAD;
+ }
+ bool operator<=(const Time& other) const
+ {
+ return elapsedSecondsAD <= other.elapsedSecondsAD;
+ }
+
+ Result AddSeconds(uint64_t seconds)
+ {
+ if (std::numeric_limits<uint64_t>::max() - elapsedSecondsAD
+ < seconds) {
+ return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+ }
+ elapsedSecondsAD += seconds;
+ return Success;
+ }
+
+ Result SubtractSeconds(uint64_t seconds)
+ {
+ if (seconds > elapsedSecondsAD) {
+ return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+ }
+ elapsedSecondsAD -= seconds;
+ return Success;
+ }
+
+ static const uint64_t ONE_DAY_IN_SECONDS
+ = UINT64_C(24) * UINT64_C(60) * UINT64_C(60);
+
+private:
+ // This constructor is hidden to prevent accidents like this:
+ //
+ // Time foo(time_t t)
+ // {
+ // // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)!
+ // return Time(t);
+ // }
+ explicit Time(uint64_t elapsedSecondsAD)
+ : elapsedSecondsAD(elapsedSecondsAD)
+ {
+ }
+ friend Time TimeFromElapsedSecondsAD(uint64_t);
+
+ uint64_t elapsedSecondsAD;
+};
+
+inline Time TimeFromElapsedSecondsAD(uint64_t elapsedSecondsAD)
+{
+ return Time(elapsedSecondsAD);
+}
+
+Time Now();
+
+// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970)
+Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch);
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__Time_h
diff --git a/security/pkix/include/pkix/pkixtypes.h b/security/pkix/include/pkix/pkixtypes.h
index 0a64b9d..d38603b 100644
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -120,6 +120,11 @@ public:
PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse) = 0;
+ // Called as soon as we think we have a valid chain but before revocation
+ // checks are done. Called to compute additional chain level checks, by the
+ // TrustDomain.
+ virtual SECStatus IsChainValid(const CERTCertList* certChain) = 0;
+
protected:
TrustDomain() { }
diff --git a/security/pkix/lib/pkixbuild.cpp b/security/pkix/lib/pkixbuild.cpp
index 22e3f61..c078eda 100644
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -225,6 +225,30 @@ BuildForward(TrustDomain& trustDomain,
}
if (trustLevel == TrustDomain::TrustAnchor) {
+ ScopedCERTCertList certChain(CERT_NewCertList());
+ if (!certChain) {
+ PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+ return MapSECStatus(SECFailure);
+ }
+
+ rv = subject.PrependNSSCertToList(certChain.get());
+ if (rv != Success) {
+ return rv;
+ }
+ BackCert* child = subject.childCert;
+ while (child) {
+ rv = child->PrependNSSCertToList(certChain.get());
+ if (rv != Success) {
+ return rv;
+ }
+ child = child->childCert;
+ }
+
+ SECStatus srv = trustDomain.IsChainValid(certChain.get());
+ if (srv != SECSuccess) {
+ return MapSECStatus(srv);
+ }
+
// End of the recursion. Create the result list and add the trust anchor to
// it.
results = CERT_NewCertList();
diff --git a/security/pkix/lib/pkixtime.cpp b/security/pkix/lib/pkixtime.cpp
new file mode 100644
index 0000000..499784e
--- /dev/null
+++ b/security/pkix/lib/pkixtime.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/* Copyright 2014 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pkix/Time.h"
+#include "pkixutil.h"
+#ifdef WIN32
+#include "windows.h"
+#else
+#include "sys/time.h"
+#endif
+
+namespace mozilla { namespace pkix {
+
+Time
+Now()
+{
+ uint64_t seconds;
+
+#ifdef WIN32
+ // "Contains a 64-bit value representing the number of 100-nanosecond
+ // intervals since January 1, 1601 (UTC)."
+ // - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ uint64_t ft64 = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) |
+ ft.dwLowDateTime;
+ seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) +
+ ft64 / (1000u * 1000u * 1000u / 100u);
+#else
+ // "The gettimeofday() function shall obtain the current time, expressed as
+ // seconds and microseconds since the Epoch."
+ // - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html
+ timeval tv;
+ (void) gettimeofday(&tv, nullptr);
+ seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + tv.tv_sec;
+#endif
+
+ return TimeFromElapsedSecondsAD(seconds);
+}
+
+Time
+TimeFromEpochInSeconds(uint64_t secondsSinceEpoch)
+{
+ uint64_t seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) +
+ secondsSinceEpoch;
+ return TimeFromElapsedSecondsAD(seconds);
+}
+
+} } // namespace mozilla::pkix
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index 01a27a1..bf96ccd 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -5929,5 +5929,37 @@
"high": "5000",
"n_buckets": 10,
"extended_statistics_ok": true
+ },
+ "CERT_PINNING_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning results (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_TEST_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning test results (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_MOZ_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning results for Mozilla sites (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_MOZ_TEST_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning test results for Mozilla sites (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_MOZ_RESULTS_BY_HOST": {
+ "expires_in_version": "never",
+ "kind": "enumerated",
+ "n_values": 512,
+ "description": "Certificate pinning results by host for Mozilla operational sites"
+ },
+ "CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST": {
+ "expires_in_version": "never",
+ "kind": "enumerated",
+ "n_values": 512,
+ "description": "Certificate pinning test results by host for Mozilla operational sites"
}
}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits