1
0
mirror of https://github.com/roytam1/UXP.git synced 2026-05-26 13:58:49 +00:00

Issue #1280 - Part 1: Remove HPKP components.

This also removes leftover plumbing for storing preload information
in SiteSecurityService since no service still uses it.
This commit is contained in:
wolfbeast
2020-03-28 01:06:56 +01:00
committed by Roy Tam
parent 6aa52af7ac
commit cf5f069080
11 changed files with 37 additions and 2662 deletions
-21
View File
@@ -120,27 +120,6 @@ pref("security.webauth.u2f_enable_usbtoken", false);
// OCSP must-staple
pref("security.ssl.enable_ocsp_must_staple", true);
// HPKP settings
// Enable pinning checks by default.
pref("security.cert_pinning.enforcement_level", 2);
// Do not process hpkp headers rooted by not built in roots by default.
// This is to prevent accidental pinning from MITM devices and is used
// for tests.
pref("security.cert_pinning.process_headers_from_non_builtin_roots", false);
// Impose a maximum age on HPKP headers, to avoid sites getting permanently
// blacking themselves out by setting a bad pin. (60 days by default)
// https://tools.ietf.org/html/rfc7469#section-4.1
pref("security.cert_pinning.max_max_age_seconds", 5184000);
// Controls whether or not HPKP (the HTTP Public Key Pinning header) is enabled.
// If true, the header is processed and collected HPKP information is consulted
// when looking for pinning information.
// If false, the header is not processed and collected HPKP information is not
// consulted when looking for pinning information. Preloaded pins are not
// affected by this preference.
// Default: false
pref("security.cert_pinning.hpkp.enabled", false);
// If a request is mixed-content, send an HSTS priming request to attempt to
// see if it is available over HTTPS.
pref("security.mixed_content.send_hsts_priming", true);
@@ -12,7 +12,6 @@
#include "NSSErrorsService.h"
#include "OCSPRequestor.h"
#include "OCSPVerificationTrustDomain.h"
#include "PublicKeyPinningService.h"
#include "cert.h"
#include "certdb.h"
#include "mozilla/Assertions.h"
@@ -862,24 +861,6 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time)
if (rv != Success) {
return rv;
}
bool skipPinningChecksBecauseOfMITMMode =
(!isBuiltInRoot && mPinningMode == CertVerifier::pinningAllowUserCAMITM);
// If mHostname isn't set, we're not verifying in the context of a TLS
// handshake, so don't verify HPKP in those cases.
if (mHostname && (mPinningMode != CertVerifier::pinningDisabled) &&
!skipPinningChecksBecauseOfMITMMode) {
bool enforceTestMode =
(mPinningMode == CertVerifier::pinningEnforceTestMode);
bool chainHasValidPins;
nsresult nsrv = PublicKeyPinningService::ChainHasValidPins(
certList, mHostname, time, enforceTestMode, chainHasValidPins);
if (NS_FAILED(nsrv)) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
if (!chainHasValidPins) {
return Result::ERROR_KEY_PINNING_FAILURE;
}
}
mBuiltChain = Move(certList);
@@ -1,333 +0,0 @@
/* 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 "mozilla/Base64.h"
#include "mozilla/Casting.h"
#include "mozilla/Logging.h"
#include "nsISiteSecurityService.h"
#include "nsServiceManagerUtils.h"
#include "nsSiteSecurityService.h"
#include "nssb64.h"
#include "pkix/pkixtypes.h"
#include "seccomon.h"
#include "sechash.h"
#include "StaticHPKPins.h" // autogenerated by genHPKPStaticpins.js
using namespace mozilla;
using namespace mozilla::pkix;
using namespace mozilla::psm;
LazyLogModule gPublicKeyPinningLog("PublicKeyPinningService");
/**
Computes in the location specified by base64Out the SHA256 digest
of the DER Encoded subject Public Key Info for the given cert
*/
static nsresult
GetBase64HashSPKI(const CERTCertificate* cert, nsACString& hashSPKIDigest)
{
hashSPKIDigest.Truncate();
Digest digest;
nsresult rv = digest.DigestBuf(SEC_OID_SHA256, cert->derPublicKey.data,
cert->derPublicKey.len);
if (NS_FAILED(rv)) {
return rv;
}
return Base64Encode(nsDependentCSubstring(
BitwiseCast<char*, unsigned char*>(digest.get().data),
digest.get().len),
hashSPKIDigest);
}
/*
* Sets certMatchesPinset to true if a given cert matches any fingerprints from
* the given pinset or the dynamicFingerprints array, or to false otherwise.
*/
static nsresult
EvalCert(const CERTCertificate* cert, const StaticFingerprints* fingerprints,
const nsTArray<nsCString>* dynamicFingerprints,
/*out*/ bool& certMatchesPinset)
{
certMatchesPinset = false;
if (!fingerprints && !dynamicFingerprints) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: No hashes found\n"));
return NS_ERROR_INVALID_ARG;
}
nsAutoCString base64Out;
nsresult rv = GetBase64HashSPKI(cert, base64Out);
if (NS_FAILED(rv)) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: GetBase64HashSPKI failed!\n"));
return rv;
}
if (fingerprints) {
for (size_t i = 0; i < fingerprints->size; i++) {
if (base64Out.Equals(fingerprints->data[i])) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: found pin base_64 ='%s'\n", base64Out.get()));
certMatchesPinset = true;
return NS_OK;
}
}
}
if (dynamicFingerprints) {
for (size_t i = 0; i < dynamicFingerprints->Length(); i++) {
if (base64Out.Equals((*dynamicFingerprints)[i])) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: found pin base_64 ='%s'\n", base64Out.get()));
certMatchesPinset = true;
return NS_OK;
}
}
}
return NS_OK;
}
/*
* Sets certListIntersectsPinset to true if a given chain matches any
* fingerprints from the given static fingerprints or the
* dynamicFingerprints array, or to false otherwise.
*/
static nsresult
EvalChain(const UniqueCERTCertList& certList,
const StaticFingerprints* fingerprints,
const nsTArray<nsCString>* dynamicFingerprints,
/*out*/ bool& certListIntersectsPinset)
{
certListIntersectsPinset = false;
CERTCertificate* currentCert;
if (!fingerprints && !dynamicFingerprints) {
MOZ_ASSERT(false, "Must pass in at least one type of pinset");
return NS_ERROR_FAILURE;
}
CERTCertListNode* node;
for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
node = CERT_LIST_NEXT(node)) {
currentCert = node->cert;
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: certArray subject: '%s'\n", currentCert->subjectName));
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: certArray issuer: '%s'\n", currentCert->issuerName));
nsresult rv = EvalCert(currentCert, fingerprints, dynamicFingerprints,
certListIntersectsPinset);
if (NS_FAILED(rv)) {
return rv;
}
if (certListIntersectsPinset) {
return NS_OK;
}
}
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, ("pkpin: no matches found\n"));
return NS_OK;
}
/**
Comparator for the is public key pinned host.
*/
static int
TransportSecurityPreloadCompare(const void* key, const void* entry) {
auto keyStr = static_cast<const char*>(key);
auto preloadEntry = static_cast<const TransportSecurityPreload*>(entry);
return strcmp(keyStr, preloadEntry->mHost);
}
nsresult
PublicKeyPinningService::ChainMatchesPinset(const UniqueCERTCertList& certList,
const nsTArray<nsCString>& aSHA256keys,
/*out*/ bool& chainMatchesPinset)
{
return EvalChain(certList, nullptr, &aSHA256keys, chainMatchesPinset);
}
// Returns via one of the output parameters the most relevant pinning
// information that is valid for the given host at the given time.
// Dynamic pins are prioritized over static pins.
static nsresult
FindPinningInformation(const char* hostname, mozilla::pkix::Time time,
/*out*/ nsTArray<nsCString>& dynamicFingerprints,
/*out*/ TransportSecurityPreload*& staticFingerprints)
{
if (!hostname || hostname[0] == 0) {
return NS_ERROR_INVALID_ARG;
}
staticFingerprints = nullptr;
dynamicFingerprints.Clear();
nsCOMPtr<nsISiteSecurityService> sssService =
do_GetService(NS_SSSERVICE_CONTRACTID);
if (!sssService) {
return NS_ERROR_FAILURE;
}
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, '.'))) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: Querying pinsets for host: '%s'\n", evalHost));
// Attempt dynamic pins first
nsresult rv;
bool found;
bool includeSubdomains;
nsTArray<nsCString> pinArray;
rv = sssService->GetKeyPinsForHostname(evalHost, time, pinArray,
&includeSubdomains, &found);
if (NS_FAILED(rv)) {
return rv;
}
if (found && (evalHost == hostname || includeSubdomains)) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: Found dyn match for host: '%s'\n", evalHost));
dynamicFingerprints = pinArray;
return NS_OK;
}
foundEntry = (TransportSecurityPreload *)bsearch(evalHost,
kPublicKeyPinningPreloadList,
sizeof(kPublicKeyPinningPreloadList) / sizeof(TransportSecurityPreload),
sizeof(TransportSecurityPreload),
TransportSecurityPreloadCompare);
if (foundEntry) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::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 {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: Didn't find pinset for host: '%s'\n", evalHost));
}
// Add one for '.'
evalHost = evalPart + 1;
}
if (foundEntry && foundEntry->pinset) {
if (time > TimeFromEpochInSeconds(kPreloadPKPinsExpirationTime /
PR_USEC_PER_SEC)) {
return NS_OK;
}
staticFingerprints = foundEntry;
}
return NS_OK;
}
// Returns true via the output parameter if the given certificate list meets
// pinning requirements for the given host at the given time. It must be the
// case that either there is an intersection between the set of hashes of
// subject public key info data in the list and the most relevant non-expired
// pinset for the host or there is no pinning information for the host.
static nsresult
CheckPinsForHostname(const UniqueCERTCertList& certList, const char* hostname,
bool enforceTestMode, mozilla::pkix::Time time,
/*out*/ bool& chainHasValidPins)
{
chainHasValidPins = false;
if (!certList) {
return NS_ERROR_INVALID_ARG;
}
if (!hostname || hostname[0] == 0) {
return NS_ERROR_INVALID_ARG;
}
nsTArray<nsCString> dynamicFingerprints;
TransportSecurityPreload* staticFingerprints = nullptr;
nsresult rv = FindPinningInformation(hostname, time, dynamicFingerprints,
staticFingerprints);
// If we have no pinning information, the certificate chain trivially
// validates with respect to pinning.
if (dynamicFingerprints.Length() == 0 && !staticFingerprints) {
chainHasValidPins = true;
return NS_OK;
}
if (dynamicFingerprints.Length() > 0) {
return EvalChain(certList, nullptr, &dynamicFingerprints, chainHasValidPins);
}
if (staticFingerprints) {
bool enforceTestModeResult;
rv = EvalChain(certList, staticFingerprints->pinset, nullptr,
enforceTestModeResult);
if (NS_FAILED(rv)) {
return rv;
}
chainHasValidPins = enforceTestModeResult;
if (staticFingerprints->mTestMode) {
if (!enforceTestMode) {
chainHasValidPins = true;
}
}
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: Pin check %s for %s host '%s' (mode=%s)\n",
enforceTestModeResult ? "passed" : "failed",
staticFingerprints->mIsMoz ? "mozilla" : "non-mozilla",
hostname, staticFingerprints->mTestMode ? "test" : "production"));
}
return NS_OK;
}
nsresult
PublicKeyPinningService::ChainHasValidPins(const UniqueCERTCertList& certList,
const char* hostname,
mozilla::pkix::Time time,
bool enforceTestMode,
/*out*/ bool& chainHasValidPins)
{
chainHasValidPins = false;
if (!certList) {
return NS_ERROR_INVALID_ARG;
}
if (!hostname || hostname[0] == 0) {
return NS_ERROR_INVALID_ARG;
}
nsAutoCString canonicalizedHostname(CanonicalizeHostname(hostname));
return CheckPinsForHostname(certList, canonicalizedHostname.get(),
enforceTestMode, time, chainHasValidPins);
}
nsresult
PublicKeyPinningService::HostHasPins(const char* hostname,
mozilla::pkix::Time time,
bool enforceTestMode,
/*out*/ bool& hostHasPins)
{
hostHasPins = false;
nsAutoCString canonicalizedHostname(CanonicalizeHostname(hostname));
nsTArray<nsCString> dynamicFingerprints;
TransportSecurityPreload* staticFingerprints = nullptr;
nsresult rv = FindPinningInformation(canonicalizedHostname.get(), time,
dynamicFingerprints, staticFingerprints);
if (NS_FAILED(rv)) {
return rv;
}
if (dynamicFingerprints.Length() > 0) {
hostHasPins = true;
} else if (staticFingerprints) {
hostHasPins = !staticFingerprints->mTestMode || enforceTestMode;
}
return NS_OK;
}
nsAutoCString
PublicKeyPinningService::CanonicalizeHostname(const char* hostname)
{
nsAutoCString canonicalizedHostname(hostname);
ToLowerCase(canonicalizedHostname);
while (canonicalizedHostname.Length() > 0 &&
canonicalizedHostname.Last() == '.') {
canonicalizedHostname.Truncate(canonicalizedHostname.Length() - 1);
}
return canonicalizedHostname;
}
@@ -1,64 +0,0 @@
/* 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 "CertVerifier.h"
#include "ScopedNSSTypes.h"
#include "cert.h"
#include "nsString.h"
#include "nsTArray.h"
#include "pkix/Time.h"
namespace mozilla {
namespace psm {
class PublicKeyPinningService
{
public:
/**
* Sets chainHasValidPins to true if the given (host, certList) passes pinning
* checks, or to false otherwise. If the host is pinned, returns true via
* chainHasValidPins if one of the keys in the given certificate chain matches
* the pin set specified by the hostname. 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 nsresult ChainHasValidPins(const UniqueCERTCertList& certList,
const char* hostname,
mozilla::pkix::Time time,
bool enforceTestMode,
/*out*/ bool& chainHasValidPins);
/**
* Sets chainMatchesPinset to true if there is any intersection between the
* certificate list and the pins specified in the aSHA256keys array.
* Values passed in are assumed to be in base64 encoded form.
*/
static nsresult ChainMatchesPinset(const UniqueCERTCertList& certList,
const nsTArray<nsCString>& aSHA256keys,
/*out*/ bool& chainMatchesPinset);
/**
* Returns true via the output parameter hostHasPins if there is pinning
* information for the given host that is valid at the given time, and false
* otherwise.
*/
static nsresult HostHasPins(const char* hostname,
mozilla::pkix::Time time,
bool enforceTestMode,
/*out*/ bool& hostHasPins);
/**
* Given a hostname of potentially mixed case with potentially multiple
* trailing '.' (see bug 1118522), canonicalizes it to lowercase with no
* trailing '.'.
*/
static nsAutoCString CanonicalizeHostname(const char* hostname);
};
}} // namespace mozilla::psm
#endif // PublicKeyPinningService_h
-712
View File
@@ -1,712 +0,0 @@
/* 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=";
/* 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 RSA Certification Authority */
static const char kCOMODO_RSA_Certification_AuthorityFingerprint[] =
"grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=";
/* 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=";
/* DST Root CA X3 */
static const char kDST_Root_CA_X3Fingerprint[] =
"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=";
/* DigiCert Assured ID Root CA */
static const char kDigiCert_Assured_ID_Root_CAFingerprint[] =
"I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=";
/* DigiCert Assured ID Root G2 */
static const char kDigiCert_Assured_ID_Root_G2Fingerprint[] =
"8ca6Zwz8iOTfUpc8rkIPCgid1HQUT+WAbEIAZOFZEik=";
/* DigiCert Assured ID Root G3 */
static const char kDigiCert_Assured_ID_Root_G3Fingerprint[] =
"Fe7TOVlLME+M+Ee0dzcdjW/sYfTbKwGvWJ58U7Ncrkw=";
/* DigiCert Global Root CA */
static const char kDigiCert_Global_Root_CAFingerprint[] =
"r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=";
/* DigiCert Global Root G2 */
static const char kDigiCert_Global_Root_G2Fingerprint[] =
"i7WTqTvh0OioIruIfFR4kMPnBqrS2rdiVPl/s2uC/CY=";
/* DigiCert Global Root G3 */
static const char kDigiCert_Global_Root_G3Fingerprint[] =
"uUwZgwDOxcBXrQcntwu+kYFpkiVkOaezL0WYEZ3anJc=";
/* DigiCert High Assurance EV Root CA */
static const char kDigiCert_High_Assurance_EV_Root_CAFingerprint[] =
"WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=";
/* DigiCert Trusted Root G4 */
static const char kDigiCert_Trusted_Root_G4Fingerprint[] =
"Wd8xe/qfTwq3ylFNd3IpaqLHZbh2ZNCLluVzmeNkcpw=";
/* End Entity Test Cert */
static const char kEnd_Entity_Test_CertFingerprint[] =
"VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
/* Entrust Root Certification Authority */
static const char kEntrust_Root_Certification_AuthorityFingerprint[] =
"bb+uANN7nNc/j7R95lkXrwDg3d9C286sIMF8AnXuIJU=";
/* Entrust Root Certification Authority - EC1 */
static const char kEntrust_Root_Certification_Authority___EC1Fingerprint[] =
"/qK31kX7pz11PB7Jp4cMQOH3sMVh6Se5hb9xGGbjbyI=";
/* Entrust Root Certification Authority - G2 */
static const char kEntrust_Root_Certification_Authority___G2Fingerprint[] =
"du6FkDdMcVQ3u8prumAo6t3i3G27uMP2EOhR8R0at/U=";
/* Entrust.net Premium 2048 Secure Server CA */
static const char kEntrust_net_Premium_2048_Secure_Server_CAFingerprint[] =
"HqPF5D7WbC2imDpCpKebHpBnhs6fG1hiFBmgBGOofTg=";
/* FacebookBackup */
static const char kFacebookBackupFingerprint[] =
"q4PO2G2cbkZhZ82+JgmRUyGMoAeozA+BSXVXQWB8XWQ=";
/* GOOGLE_PIN_COMODORSADomainValidationSecureServerCA */
static const char kGOOGLE_PIN_COMODORSADomainValidationSecureServerCAFingerprint[] =
"klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=";
/* GOOGLE_PIN_DigiCertECCSecureServerCA */
static const char kGOOGLE_PIN_DigiCertECCSecureServerCAFingerprint[] =
"PZXN3lRAy+8tBKk2Ox6F7jIlnzr2Yzmwqc3JnyfXoCw=";
/* GOOGLE_PIN_DigiCertSHA2HighAssuranceServerCA */
static const char kGOOGLE_PIN_DigiCertSHA2HighAssuranceServerCAFingerprint[] =
"k2v657xBsOVe1PQRwOsHsw3bsGT2VzIqz5K+59sNQws=";
/* GOOGLE_PIN_Entrust_SSL */
static const char kGOOGLE_PIN_Entrust_SSLFingerprint[] =
"nsxRNo6G40YPZsKV5JQt1TCA8nseQQr/LRqp1Oa8fnw=";
/* GOOGLE_PIN_GTECyberTrustGlobalRoot */
static const char kGOOGLE_PIN_GTECyberTrustGlobalRootFingerprint[] =
"EGn6R6CqT4z3ERscrqNl7q7RC//zJmDe9uBhS/rnCHU=";
/* GOOGLE_PIN_GoDaddySecure */
static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
"MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
/* GOOGLE_PIN_GoogleG2 */
static const char kGOOGLE_PIN_GoogleG2Fingerprint[] =
"7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=";
/* GOOGLE_PIN_LetsEncryptAuthorityBackup_X2_X4 */
static const char kGOOGLE_PIN_LetsEncryptAuthorityBackup_X2_X4Fingerprint[] =
"sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=";
/* GOOGLE_PIN_LetsEncryptAuthorityPrimary_X1_X3 */
static const char kGOOGLE_PIN_LetsEncryptAuthorityPrimary_X1_X3Fingerprint[] =
"YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=";
/* GOOGLE_PIN_RapidSSL */
static const char kGOOGLE_PIN_RapidSSLFingerprint[] =
"lT09gPUeQfbYrlxRtpsHrjDblj9Rpz+u7ajfCrg4qDM=";
/* GOOGLE_PIN_SymantecClass3EVG3 */
static const char kGOOGLE_PIN_SymantecClass3EVG3Fingerprint[] =
"gMxWOrX4PMQesK9qFNbYBxjBfjUvlkn/vN1n+L9lE5E=";
/* GOOGLE_PIN_UTNDATACorpSGC */
static const char kGOOGLE_PIN_UTNDATACorpSGCFingerprint[] =
"QAL80xHQczFWfnG82XHkYEjI3OjRZZcRdTs9qiommvo=";
/* GOOGLE_PIN_VeriSignClass1 */
static const char kGOOGLE_PIN_VeriSignClass1Fingerprint[] =
"LclHC+Y+9KzxvYKGCUArt7h72ZY4pkOTTohoLRvowwg=";
/* GOOGLE_PIN_VeriSignClass2_G2 */
static const char kGOOGLE_PIN_VeriSignClass2_G2Fingerprint[] =
"2oALgLKofTmeZvoZ1y/fSZg7R9jPMix8eVA6DH4o/q8=";
/* GOOGLE_PIN_VeriSignClass3_G2 */
static const char kGOOGLE_PIN_VeriSignClass3_G2Fingerprint[] =
"AjyBzOjnxk+pQtPBUEhwfTXZu1uH9PVExb8bxWQ68vo=";
/* GOOGLE_PIN_VeriSignClass4_G3 */
static const char kGOOGLE_PIN_VeriSignClass4_G3Fingerprint[] =
"VnuCEf0g09KD7gzXzgZyy52ZvFtIeljJ1U7Gf3fUqPU=";
/* 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 ECC Root CA - R4 */
static const char kGlobalSign_ECC_Root_CA___R4Fingerprint[] =
"CLOmM1/OXvSPjw5UOYbAf9GKOxImEp9hhku9W90fHMk=";
/* GlobalSign ECC Root CA - R5 */
static const char kGlobalSign_ECC_Root_CA___R5Fingerprint[] =
"fg6tdrtoGdwvVFEahDVPboswe53YIFjqbABPAdndpd8=";
/* 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[] =
"IPMbDAjLVSGntGO3WP53X/zilCVndez5YJ2+vJvhJsA=";
/* SpiderOak2 */
static const char kSpiderOak2Fingerprint[] =
"7Y3UnxbffL8aFPXsOJBpGasgpDmngpIhAxGKdQRklQQ=";
/* SpiderOak3 */
static const char kSpiderOak3Fingerprint[] =
"LkER54vOdlygpTsbYvlpMq1CE/lDAG1AP9xmdtwvV2A=";
/* 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=";
/* Swehack */
static const char kSwehackFingerprint[] =
"FdaffE799rVb3oyAuhJ2mBW/XJwD07Uajb2G6YwSAEw=";
/* SwehackBackup */
static const char kSwehackBackupFingerprint[] =
"z6cuswA6E1vgFkCjUsbEYo0Lf3aP8M8YOvwkoiGzDCo=";
/* TestSPKI */
static const char kTestSPKIFingerprint[] =
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
/* Tor1 */
static const char kTor1Fingerprint[] =
"bYz9JTDk89X3qu3fgswG+lBQso5vI0N1f0Rx4go4nLo=";
/* Tor2 */
static const char kTor2Fingerprint[] =
"xXCxhTdn7uxXneJSbQCqoAvuW3ZtQl2pDVTf2sewS8w=";
/* Tor3 */
static const char kTor3Fingerprint[] =
"CleC1qwUR8JPgH1nXvSe2VHxDe5/KfNs96EusbfSOfo=";
/* TumblrBackup */
static const char kTumblrBackupFingerprint[] =
"avlD96PLERV78IN1fD+ab5cupkUDD9wTZWJjHX6VC9w=";
/* Twitter1 */
static const char kTwitter1Fingerprint[] =
"vU9M48LzD/CF34wE5PPf4nBwRyosy06X21J0ap8yS5s=";
/* USERTrust ECC Certification Authority */
static const char kUSERTrust_ECC_Certification_AuthorityFingerprint[] =
"ICGRfpgmOUXIWcQ/HXPLQTkFPEFPoDyjvH7ohhQpjzs=";
/* USERTrust RSA Certification Authority */
static const char kUSERTrust_RSA_Certification_AuthorityFingerprint[] =
"x4QzPSC810K5/cMjb05Qm4k3Bw5zBn4lTdO/nEW/Td4=";
/* 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 - G3 */
static const char kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint[] =
"IgduWu9Eu5pBaii30cRDItcFn2D+/6XK9sW+hEeJEwM=";
/* 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 - G3 */
static const char kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint[] =
"SVqWumuteCQHvVIaALrOZXuzVVVeS7f4FGxxu6V+es4=";
/* YahooBackup1 */
static const char kYahooBackup1Fingerprint[] =
"2fRAUXyxl4A1/XHrKNBmc8bTkzA7y4FB/GLJuNAzCqY=";
/* YahooBackup2 */
static const char kYahooBackup2Fingerprint[] =
"dolnbtzEBnELx/9lOEQ22e6OZO/QNb6VSSX2XHA3E7A=";
/* 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;
};
/* PreloadedHPKPins.json pinsets */
static const char* const kPinset_google_root_pems_Data[] = {
kEntrust_Root_Certification_Authority___EC1Fingerprint,
kComodo_Trusted_Services_rootFingerprint,
kCOMODO_ECC_Certification_AuthorityFingerprint,
kDigiCert_Assured_ID_Root_G2Fingerprint,
kCOMODO_Certification_AuthorityFingerprint,
kAddTrust_Low_Value_Services_RootFingerprint,
kGlobalSign_ECC_Root_CA___R4Fingerprint,
kGeoTrust_Global_CA_2Fingerprint,
kDigiCert_Assured_ID_Root_G3Fingerprint,
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,
kUSERTrust_ECC_Certification_AuthorityFingerprint,
kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
kGlobalSign_Root_CAFingerprint,
kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
kAffirmTrust_Premium_ECCFingerprint,
kAddTrust_Public_Services_RootFingerprint,
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,
kDigiCert_Trusted_Root_G4Fingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kBaltimore_CyberTrust_RootFingerprint,
kthawte_Primary_Root_CA___G2Fingerprint,
kAffirmTrust_CommercialFingerprint,
kEntrust_Root_Certification_AuthorityFingerprint,
kGlobalSign_Root_CA___R3Fingerprint,
kEntrust_Root_Certification_Authority___G2Fingerprint,
kGeoTrust_Universal_CA_2Fingerprint,
kGlobalSign_ECC_Root_CA___R5Fingerprint,
kCybertrust_Global_RootFingerprint,
kStarfield_Root_Certificate_Authority___G2Fingerprint,
kCOMODO_RSA_Certification_AuthorityFingerprint,
kGeoTrust_Global_CAFingerprint,
kDigiCert_Global_Root_G2Fingerprint,
kGlobalSign_Root_CA___R2Fingerprint,
kAffirmTrust_NetworkingFingerprint,
kAddTrust_External_RootFingerprint,
kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
kGeoTrust_Universal_CAFingerprint,
kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
kDigiCert_Global_Root_CAFingerprint,
kDigiCert_Global_Root_G3Fingerprint,
kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
kComodo_AAA_Services_rootFingerprint,
kAffirmTrust_PremiumFingerprint,
kUSERTrust_RSA_Certification_AuthorityFingerprint,
kAddTrust_Qualified_Certificates_RootFingerprint,
};
static const StaticFingerprints kPinset_google_root_pems = {
sizeof(kPinset_google_root_pems_Data) / sizeof(const char*),
kPinset_google_root_pems_Data
};
static const char* const kPinset_mozilla_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,
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 = {
sizeof(kPinset_mozilla_Data) / sizeof(const char*),
kPinset_mozilla_Data
};
static const char* const kPinset_mozilla_services_Data[] = {
kDigiCert_Global_Root_CAFingerprint,
};
static const StaticFingerprints kPinset_mozilla_services = {
sizeof(kPinset_mozilla_services_Data) / sizeof(const char*),
kPinset_mozilla_services_Data
};
static const char* const kPinset_mozilla_test_Data[] = {
kEnd_Entity_Test_CertFingerprint,
};
static const StaticFingerprints kPinset_mozilla_test = {
sizeof(kPinset_mozilla_test_Data) / sizeof(const char*),
kPinset_mozilla_test_Data
};
/* Chrome static pinsets */
static const char* const kPinset_test_Data[] = {
kTestSPKIFingerprint,
};
static const StaticFingerprints kPinset_test = {
sizeof(kPinset_test_Data) / sizeof(const char*),
kPinset_test_Data
};
static const char* const kPinset_google_Data[] = {
kGOOGLE_PIN_GoogleG2Fingerprint,
kGoogleBackup2048Fingerprint,
kGeoTrust_Global_CAFingerprint,
kGlobalSign_Root_CA___R2Fingerprint,
};
static const StaticFingerprints kPinset_google = {
sizeof(kPinset_google_Data) / sizeof(const char*),
kPinset_google_Data
};
static const char* const kPinset_tor_Data[] = {
kTor3Fingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kGOOGLE_PIN_LetsEncryptAuthorityPrimary_X1_X3Fingerprint,
kTor1Fingerprint,
kGOOGLE_PIN_RapidSSLFingerprint,
kGOOGLE_PIN_LetsEncryptAuthorityBackup_X2_X4Fingerprint,
kTor2Fingerprint,
};
static const StaticFingerprints kPinset_tor = {
sizeof(kPinset_tor_Data) / sizeof(const char*),
kPinset_tor_Data
};
static const char* const kPinset_twitterCom_Data[] = {
kGOOGLE_PIN_VeriSignClass2_G2Fingerprint,
kGOOGLE_PIN_VeriSignClass3_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,
kGOOGLE_PIN_VeriSignClass1Fingerprint,
kGeoTrust_Primary_Certification_AuthorityFingerprint,
kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
kGOOGLE_PIN_VeriSignClass4_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,
kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
kTwitter1Fingerprint,
};
static const StaticFingerprints kPinset_twitterCom = {
sizeof(kPinset_twitterCom_Data) / sizeof(const char*),
kPinset_twitterCom_Data
};
static const char* const kPinset_twitterCDN_Data[] = {
kGOOGLE_PIN_VeriSignClass2_G2Fingerprint,
kComodo_Trusted_Services_rootFingerprint,
kCOMODO_Certification_AuthorityFingerprint,
kGOOGLE_PIN_VeriSignClass3_G2Fingerprint,
kAddTrust_Low_Value_Services_RootFingerprint,
kUTN_USERFirst_Object_Root_CAFingerprint,
kGOOGLE_PIN_GTECyberTrustGlobalRootFingerprint,
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,
kGOOGLE_PIN_VeriSignClass1Fingerprint,
kAddTrust_Public_Services_RootFingerprint,
kGOOGLE_PIN_UTNDATACorpSGCFingerprint,
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,
kGOOGLE_PIN_VeriSignClass4_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,
kEntrust_Root_Certification_Authority___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,
kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
kComodo_AAA_Services_rootFingerprint,
kTwitter1Fingerprint,
kAddTrust_Qualified_Certificates_RootFingerprint,
};
static const StaticFingerprints kPinset_twitterCDN = {
sizeof(kPinset_twitterCDN_Data) / sizeof(const char*),
kPinset_twitterCDN_Data
};
static const char* const kPinset_dropbox_Data[] = {
kEntrust_Root_Certification_Authority___EC1Fingerprint,
kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
kDigiCert_Assured_ID_Root_CAFingerprint,
kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
kGOOGLE_PIN_GoDaddySecureFingerprint,
kGo_Daddy_Class_2_CAFingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kEntrust_Root_Certification_AuthorityFingerprint,
kEntrust_Root_Certification_Authority___G2Fingerprint,
kDigiCert_Global_Root_CAFingerprint,
};
static const StaticFingerprints kPinset_dropbox = {
sizeof(kPinset_dropbox_Data) / sizeof(const char*),
kPinset_dropbox_Data
};
static const char* const kPinset_facebook_Data[] = {
kGOOGLE_PIN_DigiCertECCSecureServerCAFingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kGOOGLE_PIN_SymantecClass3EVG3Fingerprint,
kFacebookBackupFingerprint,
};
static const StaticFingerprints kPinset_facebook = {
sizeof(kPinset_facebook_Data) / sizeof(const char*),
kPinset_facebook_Data
};
static const char* const kPinset_spideroak_Data[] = {
kSpiderOak2Fingerprint,
kSpiderOak3Fingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kGeoTrust_Global_CAFingerprint,
};
static const StaticFingerprints kPinset_spideroak = {
sizeof(kPinset_spideroak_Data) / sizeof(const char*),
kPinset_spideroak_Data
};
static const char* const kPinset_yahoo_Data[] = {
kYahooBackup1Fingerprint,
kGOOGLE_PIN_VeriSignClass2_G2Fingerprint,
kDigiCert_Assured_ID_Root_CAFingerprint,
kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
kDigiCert_Trusted_Root_G4Fingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
kYahooBackup2Fingerprint,
kDigiCert_Global_Root_G2Fingerprint,
kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
kDigiCert_Global_Root_CAFingerprint,
kDigiCert_Global_Root_G3Fingerprint,
};
static const StaticFingerprints kPinset_yahoo = {
sizeof(kPinset_yahoo_Data) / sizeof(const char*),
kPinset_yahoo_Data
};
static const char* const kPinset_swehackCom_Data[] = {
kSwehackFingerprint,
kDST_Root_CA_X3Fingerprint,
kGOOGLE_PIN_LetsEncryptAuthorityPrimary_X1_X3Fingerprint,
kGOOGLE_PIN_COMODORSADomainValidationSecureServerCAFingerprint,
kGOOGLE_PIN_LetsEncryptAuthorityBackup_X2_X4Fingerprint,
kSwehackBackupFingerprint,
};
static const StaticFingerprints kPinset_swehackCom = {
sizeof(kPinset_swehackCom_Data) / sizeof(const char*),
kPinset_swehackCom_Data
};
static const char* const kPinset_ncsccs_Data[] = {
kCOMODO_ECC_Certification_AuthorityFingerprint,
kDigiCert_Assured_ID_Root_CAFingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kBaltimore_CyberTrust_RootFingerprint,
kGOOGLE_PIN_LetsEncryptAuthorityPrimary_X1_X3Fingerprint,
kCOMODO_RSA_Certification_AuthorityFingerprint,
kAddTrust_External_RootFingerprint,
kDigiCert_Global_Root_CAFingerprint,
kGOOGLE_PIN_LetsEncryptAuthorityBackup_X2_X4Fingerprint,
};
static const StaticFingerprints kPinset_ncsccs = {
sizeof(kPinset_ncsccs_Data) / sizeof(const char*),
kPinset_ncsccs_Data
};
static const char* const kPinset_tumblr_Data[] = {
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kTumblrBackupFingerprint,
kGOOGLE_PIN_DigiCertSHA2HighAssuranceServerCAFingerprint,
};
static const StaticFingerprints kPinset_tumblr = {
sizeof(kPinset_tumblr_Data) / sizeof(const char*),
kPinset_tumblr_Data
};
/* Domainlist */
struct TransportSecurityPreload {
const char* mHost;
const bool mIncludeSubdomains;
const bool mTestMode;
const bool mIsMoz;
const int32_t mId;
const StaticFingerprints* pinset;
};
/* Sort hostnames for binary search. */
static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
{ "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
{ "include-subdomains.pinning.example.com", true, false, false, -1, &kPinset_mozilla_test },
{ "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
};
// Pinning Preload List Length = 3;
static const int32_t kUnknownId = -1;
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1609459199000000);
-1
View File
@@ -132,7 +132,6 @@ UNIFIED_SOURCES += [
'nsTLSSocketProvider.cpp',
'PSMContentListener.cpp',
'PSMRunnable.cpp',
'PublicKeyPinningService.cpp',
'SecretDecoderRing.cpp',
'SharedSSLState.cpp',
'SSLServerCertVerification.cpp',
@@ -27,7 +27,7 @@ namespace mozilla
interface nsISiteSecurityService : nsISupports
{
const uint32_t HEADER_HSTS = 0;
const uint32_t HEADER_HPKP = 1;
const uint32_t HEADER_HPKP = 1; /* no longer used */
const uint32_t HEADER_OMS = 2;
const uint32_t Success = 0;
@@ -39,10 +39,10 @@ interface nsISiteSecurityService : nsISupports
const uint32_t ERROR_INVALID_MAX_AGE = 6;
const uint32_t ERROR_MULTIPLE_INCLUDE_SUBDOMAINS = 7;
const uint32_t ERROR_INVALID_INCLUDE_SUBDOMAINS = 8;
const uint32_t ERROR_INVALID_PIN = 9;
const uint32_t ERROR_MULTIPLE_REPORT_URIS = 10;
const uint32_t ERROR_PINSET_DOES_NOT_MATCH_CHAIN = 11;
const uint32_t ERROR_NO_BACKUP_PIN = 12;
const uint32_t ERROR_INVALID_PIN = 9; /* no longer used */
const uint32_t ERROR_MULTIPLE_REPORT_URIS = 10; /* no longer used */
const uint32_t ERROR_PINSET_DOES_NOT_MATCH_CHAIN = 11; /* no longer used */
const uint32_t ERROR_NO_BACKUP_PIN = 12; /* no longer used */
const uint32_t ERROR_COULD_NOT_SAVE_STATE = 13;
const uint32_t ERROR_ROOT_NOT_BUILT_IN = 14;
@@ -150,42 +150,6 @@ interface nsISiteSecurityService : nsISupports
* settings.
*/
void clearAll();
/**
* Returns an array of sha256-hashed key pins for the given domain, if any.
* If these pins also apply to subdomains of the given domain,
* aIncludeSubdomains will be true. Pins returned are only for non-built-in
* pin entries.
*
* @param aHostname the hosname (punycode) to be queried about
* @param the time at which the pins should be valid. This is in
mozilla::pkix::Time which uses internally seconds since 0 AD.
* @param aPinArray the set of sha256-hashed key pins for the given domain
* @param aIncludeSubdomains true if the pins apply to subdomains of the
* given domain
*/
[noscript] boolean getKeyPinsForHostname(in string aHostname,
in mozillaPkixTime evalTime,
out nsCStringTArrayRef aPinArray,
out boolean aIncludeSubdomains);
/**
* Set public-key pins for a host. The resulting pins will be permanent
* and visible from private and non-private contexts. These pins replace
* any already set by this mechanism or those built-in to Gecko.
*
* @param aHost the hostname (punycode) that pins will apply to
* @param aIncludeSubdomains whether these pins also apply to subdomains
* @param aExpires the time this pin should expire (millis since epoch)
* @param aPinCount number of keys being pinnned
* @param aSha256Pins array of hashed key fingerprints (SHA-256, base64)
* @param aIsPreload are these key pins for a preload entry? (false by
* default)
*/
boolean setKeyPins(in string aHost, in boolean aIncludeSubdomains,
in int64_t aExpires, in unsigned long aPinCount,
[array, size_is(aPinCount)] in string aSha256Pins,
[optional] in boolean aIsPreload);
};
%{C++
+32 -575
View File
@@ -25,7 +25,6 @@
#include "mozilla/Logging.h"
#include "prnetdb.h"
#include "prprf.h"
#include "PublicKeyPinningService.h"
#include "ScopedNSSTypes.h"
#include "SharedCertVerifier.h"
@@ -86,123 +85,31 @@ SiteHSTSState::ToString(nsCString& aString)
aString.AppendInt(static_cast<uint32_t>(mHSTSIncludeSubdomains));
}
////////////////////////////////////////////////////////////////////////////////
static bool
stringIsBase64EncodingOf256bitValue(nsCString& encodedString) {
nsAutoCString binaryValue;
nsresult rv = mozilla::Base64Decode(encodedString, binaryValue);
if (NS_FAILED(rv)) {
return false;
}
if (binaryValue.Length() != SHA256_LENGTH) {
return false;
}
return true;
}
SiteHPKPState::SiteHPKPState()
: mExpireTime(0)
, mState(SecurityPropertyUnset)
, mIncludeSubdomains(false)
{
}
SiteHPKPState::SiteHPKPState(nsCString& aStateString)
: mExpireTime(0)
, mState(SecurityPropertyUnset)
, mIncludeSubdomains(false)
{
uint32_t hpkpState = 0;
uint32_t hpkpIncludeSubdomains = 0; // PR_sscanf doesn't handle bools.
const uint32_t MaxMergedHPKPPinSize = 1024;
char mergedHPKPins[MaxMergedHPKPPinSize];
memset(mergedHPKPins, 0, MaxMergedHPKPPinSize);
if (aStateString.Length() >= MaxMergedHPKPPinSize) {
SSSLOG(("SSS: Cannot parse PKPState string, too large\n"));
return;
}
int32_t matches = PR_sscanf(aStateString.get(), "%lld,%lu,%lu,%s",
&mExpireTime, &hpkpState,
&hpkpIncludeSubdomains, mergedHPKPins);
bool valid = (matches == 4 &&
(hpkpIncludeSubdomains == 0 || hpkpIncludeSubdomains == 1) &&
((SecurityPropertyState)hpkpState == SecurityPropertyUnset ||
(SecurityPropertyState)hpkpState == SecurityPropertySet ||
(SecurityPropertyState)hpkpState == SecurityPropertyKnockout));
SSSLOG(("SSS: loading SiteHPKPState matches=%d\n", matches));
const uint32_t SHA256Base64Len = 44;
if (valid && (SecurityPropertyState)hpkpState == SecurityPropertySet) {
// try to expand the merged PKPins
const char* cur = mergedHPKPins;
nsAutoCString pin;
uint32_t collectedLen = 0;
mergedHPKPins[MaxMergedHPKPPinSize - 1] = 0;
size_t totalLen = strlen(mergedHPKPins);
while (collectedLen + SHA256Base64Len <= totalLen) {
pin.Assign(cur, SHA256Base64Len);
if (stringIsBase64EncodingOf256bitValue(pin)) {
mSHA256keys.AppendElement(pin);
}
cur += SHA256Base64Len;
collectedLen += SHA256Base64Len;
}
if (mSHA256keys.IsEmpty()) {
valid = false;
}
}
if (valid) {
mState = (SecurityPropertyState)hpkpState;
mIncludeSubdomains = (hpkpIncludeSubdomains == 1);
} else {
SSSLOG(("%s is not a valid SiteHPKPState", aStateString.get()));
mExpireTime = 0;
mState = SecurityPropertyUnset;
mIncludeSubdomains = false;
if (!mSHA256keys.IsEmpty()) {
mSHA256keys.Clear();
}
}
}
SiteHPKPState::SiteHPKPState(PRTime aExpireTime,
SecurityPropertyState aState,
bool aIncludeSubdomains,
nsTArray<nsCString>& aSHA256keys)
: mExpireTime(aExpireTime)
, mState(aState)
, mIncludeSubdomains(aIncludeSubdomains)
, mSHA256keys(aSHA256keys)
{
}
void
SiteHPKPState::ToString(nsCString& aString)
{
aString.Truncate();
aString.AppendInt(mExpireTime);
aString.Append(',');
aString.AppendInt(mState);
aString.Append(',');
aString.AppendInt(static_cast<uint32_t>(mIncludeSubdomains));
aString.Append(',');
for (unsigned int i = 0; i < mSHA256keys.Length(); i++) {
aString.Append(mSHA256keys[i]);
}
}
////////////////////////////////////////////////////////////////////////////////
const uint64_t kSixtyDaysInSeconds = 60 * 24 * 60 * 60;
static bool
HostIsIPAddress(const char *hostname)
{
PRNetAddr hostAddr;
return (PR_StringToNetAddr(hostname, &hostAddr) == PR_SUCCESS);
}
nsAutoCString CanonicalizeHostname(const char* hostname)
{
nsAutoCString canonicalizedHostname(hostname);
ToLowerCase(canonicalizedHostname);
while (canonicalizedHostname.Length() > 0 &&
canonicalizedHostname.Last() == '.') {
canonicalizedHostname.Truncate(canonicalizedHostname.Length() - 1);
}
return canonicalizedHostname;
}
nsSiteSecurityService::nsSiteSecurityService()
: mMaxMaxAge(kSixtyDaysInSeconds)
, mUseStsService(true)
: mUseStsService(true)
, mPreloadListTimeOffset(0)
, mHPKPEnabled(false)
{
}
@@ -223,30 +130,16 @@ nsSiteSecurityService::Init()
return NS_ERROR_NOT_SAME_THREAD;
}
mMaxMaxAge = mozilla::Preferences::GetInt(
"security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
mozilla::Preferences::AddStrongObserver(this,
"security.cert_pinning.max_max_age_seconds");
mHPKPEnabled = mozilla::Preferences::GetBool(
"security.cert_pinning.hpkp.enabled", false);
mozilla::Preferences::AddStrongObserver(this,
"security.cert_pinning.hpkp.enabled");
mUseStsService = mozilla::Preferences::GetBool(
"network.stricttransportsecurity.enabled", true);
mozilla::Preferences::AddStrongObserver(this,
"network.stricttransportsecurity.enabled");
mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
"security.cert_pinning.process_headers_from_non_builtin_roots", false);
mozilla::Preferences::AddStrongObserver(this,
"security.cert_pinning.process_headers_from_non_builtin_roots");
mPreloadListTimeOffset = mozilla::Preferences::GetInt(
"test.currentTimeOffsetSeconds", 0);
mozilla::Preferences::AddStrongObserver(this,
"test.currentTimeOffsetSeconds");
mSiteStateStorage =
mozilla::DataStorage::Get(NS_LITERAL_STRING("SiteSecurityServiceState.txt"));
mPreloadStateStorage =
mozilla::DataStorage::Get(NS_LITERAL_STRING("SecurityPreloadState.txt"));
bool storageWillPersist = false;
nsresult rv = mSiteStateStorage->Init(storageWillPersist);
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -276,7 +169,7 @@ nsSiteSecurityService::GetHost(nsIURI* aURI, nsACString& aResult)
return rv;
}
aResult.Assign(PublicKeyPinningService::CanonicalizeHostname(host.get()));
aResult.Assign(CanonicalizeHostname(host.get()));
if (aResult.IsEmpty()) {
return NS_ERROR_UNEXPECTED;
}
@@ -292,9 +185,6 @@ SetStorageKey(nsAutoCString& storageKey, nsCString& hostname, uint32_t aType)
case nsISiteSecurityService::HEADER_HSTS:
storageKey.AppendLiteral(":HSTS");
break;
case nsISiteSecurityService::HEADER_HPKP:
storageKey.AppendLiteral(":HPKP");
break;
default:
NS_ASSERTION(false, "SSS:SetStorageKey got invalid type");
}
@@ -359,8 +249,7 @@ nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags
}
// Only HSTS is supported at the moment.
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
aType == nsISiteSecurityService::HEADER_HPKP,
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
NS_ERROR_NOT_IMPLEMENTED);
nsAutoCString hostname;
@@ -379,13 +268,6 @@ nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags
return NS_OK;
}
static bool
HostIsIPAddress(const char *hostname)
{
PRNetAddr hostAddr;
return (PR_StringToNetAddr(hostname, &hostAddr) == PR_SUCCESS);
}
NS_IMETHODIMP
nsSiteSecurityService::ProcessHeader(uint32_t aType,
nsIURI* aSourceURI,
@@ -396,11 +278,6 @@ nsSiteSecurityService::ProcessHeader(uint32_t aType,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ProcessHeader");
}
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
}
@@ -422,11 +299,6 @@ nsSiteSecurityService::UnsafeProcessHeader(uint32_t aType,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::UnsafeProcessHeader");
}
return ProcessHeaderInternal(aType, aSourceURI, aHeader, nullptr, aFlags,
aMaxAge, aIncludeSubdomains, aFailureResult);
}
@@ -444,9 +316,8 @@ nsSiteSecurityService::ProcessHeaderInternal(uint32_t aType,
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
}
// Only HSTS and HPKP are supported at the moment.
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
aType == nsISiteSecurityService::HEADER_HPKP,
// Only HSTS is supported at the moment.
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
NS_ERROR_NOT_IMPLEMENTED);
if (aMaxAge != nullptr) {
@@ -494,10 +365,6 @@ nsSiteSecurityService::ProcessHeaderInternal(uint32_t aType,
rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aMaxAge,
aIncludeSubdomains, aFailureResult);
break;
case nsISiteSecurityService::HEADER_HPKP:
rv = ProcessPKPHeader(aSourceURI, aHeader, aSSLStatus, aFlags, aMaxAge,
aIncludeSubdomains, aFailureResult);
break;
default:
MOZ_CRASH("unexpected header type");
}
@@ -513,9 +380,6 @@ ParseSSSHeaders(uint32_t aType,
uint64_t& maxAge,
nsTArray<nsCString>& sha256keys)
{
// Strict transport security and Public Key Pinning have very similar
// Header formats.
// "Strict-Transport-Security" ":" OWS
// STS-d *( OWS ";" OWS STS-d OWS)
//
@@ -527,26 +391,6 @@ ParseSSSHeaders(uint32_t aType,
// includeSubDomains = [ "includeSubDomains" ]
//
// "Public-Key-Pins ":" OWS
// PKP-d *( OWS ";" OWS PKP-d OWS)
//
// ; PKP directive
// PKP-d = maxAge / includeSubDomains / reportUri / pin-directive
//
// maxAge = "max-age" "=" delta-seconds v-ext
//
// includeSubDomains = [ "includeSubDomains" ]
//
// reportURi = "report-uri" "=" quoted-string
//
// pin-directive = "pin-" token "=" quoted-string
//
// the only valid token currently specified is sha256
// the quoted string for a pin directive is the base64 encoding
// of the hash of the public key of the fingerprint
//
// The order of the directives is not significant.
// All directives must appear only once.
// Directive names are case-insensitive.
// The entire header is invalid if a directive not conforming to the
@@ -558,8 +402,6 @@ ParseSSSHeaders(uint32_t aType,
NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
NS_NAMED_LITERAL_CSTRING(pin_sha256_var, "pin-sha256");
NS_NAMED_LITERAL_CSTRING(report_uri_var, "report-uri");
nsSecurityHeaderParser parser(aHeader);
nsresult rv = parser.Parse();
@@ -614,29 +456,7 @@ ParseSSSHeaders(uint32_t aType,
directive->mValue.get()));
return nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS;
}
} else if (aType == nsISiteSecurityService::HEADER_HPKP &&
directive->mName.Length() == pin_sha256_var.Length() &&
directive->mName.EqualsIgnoreCase(pin_sha256_var.get(),
pin_sha256_var.Length())) {
SSSLOG(("SSS: found pinning entry '%s' length=%d",
directive->mValue.get(), directive->mValue.Length()));
if (!stringIsBase64EncodingOf256bitValue(directive->mValue)) {
return nsISiteSecurityService::ERROR_INVALID_PIN;
}
sha256keys.AppendElement(directive->mValue);
} else if (aType == nsISiteSecurityService::HEADER_HPKP &&
directive->mName.Length() == report_uri_var.Length() &&
directive->mName.EqualsIgnoreCase(report_uri_var.get(),
report_uri_var.Length())) {
// We don't support the report-uri yet, but to avoid unrecognized
// directive warnings, we still have to handle its presence
if (foundReportURI) {
SSSLOG(("SSS: found two report-uri directives"));
return nsISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS;
}
SSSLOG(("SSS: found report-uri directive"));
foundReportURI = true;
} else {
} else {
SSSLOG(("SSS: ignoring unrecognized directive '%s'",
directive->mName.get()));
foundUnrecognizedDirective = true;
@@ -645,194 +465,6 @@ ParseSSSHeaders(uint32_t aType,
return nsISiteSecurityService::Success;
}
nsresult
nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
const char* aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
{
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
}
if (!mHPKPEnabled) {
SSSLOG(("SSS: HPKP disabled: not processing header '%s'", aHeader));
if (aMaxAge) {
*aMaxAge = 0;
}
if (aIncludeSubdomains) {
*aIncludeSubdomains = false;
}
return NS_OK;
}
SSSLOG(("SSS: processing HPKP header '%s'", aHeader));
NS_ENSURE_ARG(aSSLStatus);
const uint32_t aType = nsISiteSecurityService::HEADER_HPKP;
bool foundMaxAge = false;
bool foundIncludeSubdomains = false;
bool foundUnrecognizedDirective = false;
uint64_t maxAge = 0;
nsTArray<nsCString> sha256keys;
uint32_t sssrv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains,
foundMaxAge, foundUnrecognizedDirective,
maxAge, sha256keys);
if (sssrv != nsISiteSecurityService::Success) {
if (aFailureResult) {
*aFailureResult = sssrv;
}
return NS_ERROR_FAILURE;
}
// after processing all the directives, make sure we came across max-age
// somewhere.
if (!foundMaxAge) {
SSSLOG(("SSS: did not encounter required max-age directive"));
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_NO_MAX_AGE;
}
return NS_ERROR_FAILURE;
}
// before we add the pin we need to ensure it will not break the site as
// currently visited so:
// 1. recompute a valid chain (no external ocsp)
// 2. use this chain to check if things would have broken!
nsAutoCString host;
nsresult rv = GetHost(aSourceURI, host);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIX509Cert> cert;
rv = aSSLStatus->GetServerCert(getter_AddRefs(cert));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(cert, NS_ERROR_FAILURE);
UniqueCERTCertificate nssCert(cert->GetCert());
NS_ENSURE_TRUE(nssCert, NS_ERROR_FAILURE);
mozilla::pkix::Time now(mozilla::pkix::Now());
UniqueCERTCertList certList;
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
// We don't want this verification to cause any network traffic that would
// block execution. Also, since we don't have access to the original stapled
// OCSP response, we can't enforce this aspect of the TLS Feature extension.
// This is ok, because it will have been enforced when we originally connected
// to the site (or it's disabled, in which case we wouldn't want to enforce it
// anyway).
CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY |
CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
if (certVerifier->VerifySSLServerCert(nssCert,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
now, nullptr, // pinarg
host.get(), // hostname
certList,
false, // don't store intermediates
flags)
!= mozilla::pkix::Success) {
return NS_ERROR_FAILURE;
}
CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
if (CERT_LIST_END(rootNode, certList)) {
return NS_ERROR_FAILURE;
}
bool isBuiltIn = false;
mozilla::pkix::Result result = IsCertBuiltInRoot(rootNode->cert, isBuiltIn);
if (result != mozilla::pkix::Success) {
return NS_ERROR_FAILURE;
}
if (!isBuiltIn && !mProcessPKPHeadersFromNonBuiltInRoots) {
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN;
}
return NS_ERROR_FAILURE;
}
// If maxAge == 0, we remove dynamic HPKP state for this host. Due to
// architectural constraints, if this host was preloaded, any future lookups
// will use the preloaded state (i.e. we can't store a "this host is not HPKP"
// entry like we can for HSTS).
if (maxAge == 0) {
return RemoveState(aType, aSourceURI, aFlags);
}
// clamp maxAge to the maximum set by pref
if (maxAge > mMaxMaxAge) {
maxAge = mMaxMaxAge;
}
bool chainMatchesPinset;
rv = PublicKeyPinningService::ChainMatchesPinset(certList, sha256keys,
chainMatchesPinset);
if (NS_FAILED(rv)) {
return rv;
}
if (!chainMatchesPinset) {
// is invalid
SSSLOG(("SSS: Pins provided by %s are invalid no match with certList\n", host.get()));
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN;
}
return NS_ERROR_FAILURE;
}
// finally we need to ensure that there is a "backup pin" ie. There must be
// at least one fingerprint hash that does NOT validate against the verified
// chain (Section 2.5 of the spec)
bool hasBackupPin = false;
for (uint32_t i = 0; i < sha256keys.Length(); i++) {
nsTArray<nsCString> singlePin;
singlePin.AppendElement(sha256keys[i]);
rv = PublicKeyPinningService::ChainMatchesPinset(certList, singlePin,
chainMatchesPinset);
if (NS_FAILED(rv)) {
return rv;
}
if (!chainMatchesPinset) {
hasBackupPin = true;
}
}
if (!hasBackupPin) {
// is invalid
SSSLOG(("SSS: Pins provided by %s are invalid no backupPin\n", host.get()));
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_NO_BACKUP_PIN;
}
return NS_ERROR_FAILURE;
}
int64_t expireTime = ExpireTimeFromMaxAge(maxAge);
SiteHPKPState dynamicEntry(expireTime, SecurityPropertySet,
foundIncludeSubdomains, sha256keys);
SSSLOG(("SSS: about to set pins for %s, expires=%ld now=%ld maxAge=%lu\n",
host.get(), expireTime, PR_Now() / PR_USEC_PER_MSEC, maxAge));
rv = SetHPKPState(host.get(), dynamicEntry, aFlags, false);
if (NS_FAILED(rv)) {
SSSLOG(("SSS: failed to set pins for %s\n", host.get()));
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE;
}
return rv;
}
if (aMaxAge != nullptr) {
*aMaxAge = maxAge;
}
if (aIncludeSubdomains != nullptr) {
*aIncludeSubdomains = foundIncludeSubdomains;
}
return foundUnrecognizedDirective
? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
: NS_OK;
}
nsresult
nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI,
const char* aHeader,
@@ -902,17 +534,11 @@ nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
uint32_t aFlags, bool* aCached,
bool* aResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureURI for non-HSTS entries");
}
NS_ENSURE_ARG(aURI);
NS_ENSURE_ARG(aResult);
// Only HSTS and HPKP are supported at the moment.
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
aType == nsISiteSecurityService::HEADER_HPKP,
// Only HSTS is supported at the moment.
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
NS_ERROR_NOT_IMPLEMENTED);
nsAutoCString hostname;
@@ -939,17 +565,11 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
uint32_t aFlags, bool* aCached,
bool* aResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureHost for non-HSTS entries");
}
NS_ENSURE_ARG(aHost);
NS_ENSURE_ARG(aResult);
// Only HSTS and HPKP are supported at the moment.
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
aType == nsISiteSecurityService::HEADER_HPKP,
// Only HSTS is supported at the moment.
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
NS_ERROR_NOT_IMPLEMENTED);
// set default in case if we can't find any STS information
@@ -963,36 +583,14 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
return NS_OK;
}
/* An IP address never qualifies as a secure URI. */
// An IP address never qualifies as a secure URI.
if (HostIsIPAddress(aHost)) {
return NS_OK;
}
if (aType == nsISiteSecurityService::HEADER_HPKP) {
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
if (!certVerifier) {
return NS_ERROR_FAILURE;
}
if (certVerifier->mPinningMode ==
CertVerifier::PinningMode::pinningDisabled) {
return NS_OK;
}
bool enforceTestMode = certVerifier->mPinningMode ==
CertVerifier::PinningMode::pinningEnforceTestMode;
return PublicKeyPinningService::HostHasPins(aHost, mozilla::pkix::Now(),
enforceTestMode, *aResult);
}
// Holepunch chart.apis.google.com and subdomains.
nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHost));
if (host.EqualsLiteral("chart.apis.google.com") ||
StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
if (aCached) {
*aCached = true;
}
return NS_OK;
}
// Canonicalize the passed host name
nsAutoCString host(CanonicalizeHostname(aHost));
// First check the exact host. This involves first checking for an entry in
// site security storage. If that entry exists, we don't want to check
// in the preload list. We only want to use the stored value if it is not a
@@ -1088,144 +686,9 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
NS_IMETHODIMP
nsSiteSecurityService::ClearAll()
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ClearAll");
}
return mSiteStateStorage->Clear();
}
bool entryStateNotOK(SiteHPKPState& state, mozilla::pkix::Time& aEvalTime) {
return state.mState != SecurityPropertySet || state.IsExpired(aEvalTime) ||
state.mSHA256keys.Length() < 1;
}
NS_IMETHODIMP
nsSiteSecurityService::GetKeyPinsForHostname(const char* aHostname,
mozilla::pkix::Time& aEvalTime,
/*out*/ nsTArray<nsCString>& pinArray,
/*out*/ bool* aIncludeSubdomains,
/*out*/ bool* aFound) {
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::GetKeyPinsForHostname");
}
NS_ENSURE_ARG(aFound);
NS_ENSURE_ARG(aHostname);
if (!mHPKPEnabled) {
SSSLOG(("HPKP disabled - returning 'pins not found' for %s",
aHostname));
*aFound = false;
return NS_OK;
}
SSSLOG(("Top of GetKeyPinsForHostname for %s", aHostname));
*aFound = false;
*aIncludeSubdomains = false;
pinArray.Clear();
nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHostname));
nsAutoCString storageKey;
SetStorageKey(storageKey, host, nsISiteSecurityService::HEADER_HPKP);
SSSLOG(("storagekey '%s'\n", storageKey.get()));
mozilla::DataStorageType storageType = mozilla::DataStorage_Persistent;
nsCString value = mSiteStateStorage->Get(storageKey, storageType);
// decode now
SiteHPKPState foundEntry(value);
if (entryStateNotOK(foundEntry, aEvalTime)) {
// not in permanent storage, try now private
value = mSiteStateStorage->Get(storageKey, mozilla::DataStorage_Private);
SiteHPKPState privateEntry(value);
if (entryStateNotOK(privateEntry, aEvalTime)) {
// not in private storage, try dynamic preload
value = mPreloadStateStorage->Get(storageKey,
mozilla::DataStorage_Persistent);
SiteHPKPState preloadEntry(value);
if (entryStateNotOK(preloadEntry, aEvalTime)) {
return NS_OK;
}
foundEntry = preloadEntry;
} else {
foundEntry = privateEntry;
}
}
pinArray = foundEntry.mSHA256keys;
*aIncludeSubdomains = foundEntry.mIncludeSubdomains;
*aFound = true;
return NS_OK;
}
NS_IMETHODIMP
nsSiteSecurityService::SetKeyPins(const char* aHost, bool aIncludeSubdomains,
int64_t aExpires, uint32_t aPinCount,
const char** aSha256Pins,
bool aIsPreload,
/*out*/ bool* aResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::SetKeyPins");
}
NS_ENSURE_ARG_POINTER(aHost);
NS_ENSURE_ARG_POINTER(aResult);
NS_ENSURE_ARG_POINTER(aSha256Pins);
if (!mHPKPEnabled) {
SSSLOG(("SSS: HPKP disabled: not setting pins"));
*aResult = false;
return NS_OK;
}
SSSLOG(("Top of SetPins"));
nsTArray<nsCString> sha256keys;
for (unsigned int i = 0; i < aPinCount; i++) {
nsAutoCString pin(aSha256Pins[i]);
SSSLOG(("SetPins pin=%s\n", pin.get()));
if (!stringIsBase64EncodingOf256bitValue(pin)) {
return NS_ERROR_INVALID_ARG;
}
sha256keys.AppendElement(pin);
}
SiteHPKPState dynamicEntry(aExpires, SecurityPropertySet,
aIncludeSubdomains, sha256keys);
// we always store data in permanent storage (ie no flags)
nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHost));
return SetHPKPState(host.get(), dynamicEntry, 0, aIsPreload);
}
nsresult
nsSiteSecurityService::SetHPKPState(const char* aHost, SiteHPKPState& entry,
uint32_t aFlags, bool aIsPreload)
{
SSSLOG(("Top of SetPKPState"));
nsAutoCString host(aHost);
nsAutoCString storageKey;
SetStorageKey(storageKey, host, nsISiteSecurityService::HEADER_HPKP);
bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
mozilla::DataStorageType storageType = isPrivate
? mozilla::DataStorage_Private
: mozilla::DataStorage_Persistent;
nsAutoCString stateString;
entry.ToString(stateString);
nsresult rv;
if (aIsPreload) {
rv = mPreloadStateStorage->Put(storageKey, stateString, storageType);
} else {
rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
}
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
//------------------------------------------------------------
// nsSiteSecurityService::nsIObserver
//------------------------------------------------------------
@@ -1246,12 +709,6 @@ nsSiteSecurityService::Observe(nsISupports *subject,
"network.stricttransportsecurity.enabled", true);
mPreloadListTimeOffset =
mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
mHPKPEnabled = mozilla::Preferences::GetBool(
"security.cert_pinning.hpkp.enabled", false);
mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
"security.cert_pinning.process_headers_from_non_builtin_roots", false);
mMaxMaxAge = mozilla::Preferences::GetInt(
"security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
}
return NS_OK;
@@ -38,40 +38,6 @@ enum SecurityPropertyState {
SecurityPropertyNegative = 3,
};
/**
* SiteHPKPState: A utility class that encodes/decodes a string describing
* the public key pins of a site.
* HPKP state consists of:
* - Expiry time (PRTime (aka int64_t) in milliseconds)
* - A state flag (SecurityPropertyState, default SecurityPropertyUnset)
* - An include subdomains flag (bool, default false)
* - An array of sha-256 hashed base 64 encoded fingerprints of required keys
*/
class SiteHPKPState
{
public:
SiteHPKPState();
explicit SiteHPKPState(nsCString& aStateString);
SiteHPKPState(PRTime aExpireTime, SecurityPropertyState aState,
bool aIncludeSubdomains, nsTArray<nsCString>& SHA256keys);
PRTime mExpireTime;
SecurityPropertyState mState;
bool mIncludeSubdomains;
nsTArray<nsCString> mSHA256keys;
bool IsExpired(mozilla::pkix::Time aTime)
{
if (aTime > mozilla::pkix::TimeFromEpochInSeconds(mExpireTime /
PR_MSEC_PER_SEC)) {
return true;
}
return false;
}
void ToString(nsCString& aString);
};
/**
* SiteHSTSState: A utility class that encodes/decodes a string describing
* the security state of a site. Currently only handles HSTS.
@@ -137,20 +103,10 @@ private:
nsresult ProcessSTSHeader(nsIURI* aSourceURI, const char* aHeader,
uint32_t flags, uint64_t* aMaxAge,
bool* aIncludeSubdomains, uint32_t* aFailureResult);
nsresult ProcessPKPHeader(nsIURI* aSourceURI, const char* aHeader,
nsISSLStatus* aSSLStatus, uint32_t flags,
uint64_t* aMaxAge, bool* aIncludeSubdomains,
uint32_t* aFailureResult);
nsresult SetHPKPState(const char* aHost, SiteHPKPState& entry, uint32_t flags,
bool aIsPreload);
uint64_t mMaxMaxAge;
bool mUseStsService;
int64_t mPreloadListTimeOffset;
bool mHPKPEnabled;
bool mProcessPKPHeadersFromNonBuiltInRoots;
RefPtr<mozilla::DataStorage> mSiteStateStorage;
RefPtr<mozilla::DataStorage> mPreloadStateStorage;
};
#endif // __nsSiteSecurityService_h__
@@ -1,222 +0,0 @@
// -*- 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://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.pins?format=TEXT",
"json_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT",
"substitute_pinsets": {
// Use the larger google_root_pems pinset instead of google
"google": "google_root_pems"
},
"production_pinsets": [
"google_root_pems",
"facebook"
],
"production_domains": [
// Chrome's test domains.
"pinningtest.appspot.com",
"pinning-test.badssl.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",
// SpiderOak
"spideroak.com"
],
"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",
"Baltimore CyberTrust Root",
"Comodo AAA Services root",
"COMODO Certification Authority",
"COMODO ECC Certification Authority",
"COMODO RSA Certification Authority",
"Comodo Secure Services root",
"Comodo Trusted Services root",
"Cybertrust Global Root",
"DigiCert Assured ID Root CA",
"DigiCert Assured ID Root G2",
"DigiCert Assured ID Root G3",
"DigiCert Global Root CA",
"DigiCert Global Root G2",
"DigiCert Global Root G3",
"DigiCert High Assurance EV Root CA",
"DigiCert Trusted Root G4",
"Entrust Root Certification Authority",
"Entrust Root Certification Authority - EC1",
"Entrust Root Certification Authority - G2",
"Entrust.net Premium 2048 Secure Server CA",
// "Equifax Secure Certificate Authority",
"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 ECC Root CA - R4",
"GlobalSign ECC Root CA - R5",
"GlobalSign Root CA",
"GlobalSign Root CA - R2",
"GlobalSign Root CA - R3",
"Go Daddy Class 2 CA",
"Go Daddy Root Certificate Authority - G2",
"Starfield Class 2 CA",
"Starfield Root Certificate Authority - G2",
"thawte Primary Root CA",
"thawte Primary Root CA - G2",
"thawte Primary Root CA - G3",
"USERTrust ECC Certification Authority",
"USERTrust RSA Certification Authority",
"UTN USERFirst Hardware Root CA",
"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 Universal Root Certification Authority"
]
}
],
"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": "services.mozilla.com", "include_subdomains": true,
"pins": "mozilla_services", "test_mode": false, "id": 6 },
{ "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 },
{ "name": "aus5.mozilla.org", "include_subdomains": true,
"pins": "mozilla", "test_mode": true, "id": 7 }
],
"extra_certificates": []
}
-630
View File
@@ -1,630 +0,0 @@
/* 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 \
// [an unused argument - see bug 1205406] \
// [absolute path to]/StaticHPKPins.h
"use strict";
if (arguments.length != 3) {
throw new Error("Usage: genHPKPStaticPins.js " +
"<absolute path to PreloadedHPKPins.json> " +
"<an unused argument - see bug 1205406> " +
"<absolute path to StaticHPKPins.h>");
}
var { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
var { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
var gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
const BUILT_IN_NICK_PREFIX = "Builtin Object Token:";
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 StaticFingerprints* 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";
// Command-line arguments
var gStaticPins = parseJson(arguments[0]);
// arguments[1] is ignored for now. See bug 1205406.
// Open the output file.
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(arguments[2]);
var 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) {
let 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) {
let 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 new Error(`ERROR: problem downloading '${filename}': ${e}`);
}
if (req.status != 200) {
throw new Error("ERROR: problem downloading '" + filename + "': status " +
req.status);
}
let resultDecoded;
try {
resultDecoded = atob(req.responseText);
}
catch (e) {
throw new Error("ERROR: could not decode data as base64 from '" + filename +
"': " + e);
}
return resultDecoded;
}
function downloadAsJson(filename) {
// we have to filter out '//' comments, while not mangling the json
let result = download(filename).replace(/^(\s*)?\/\/[^\n]*\n/mg, "");
let data = null;
try {
data = JSON.parse(result);
}
catch (e) {
throw new Error("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;
}
/**
* Hashes |input| using the SHA-256 algorithm in the following manner:
* btoa(sha256(atob(input)))
*
* @argument {String} input Base64 string to decode and return the hash of.
* @returns {String} Base64 encoded SHA-256 hash.
*/
function sha256Base64(input) {
let decodedValue;
try {
decodedValue = atob(input);
}
catch (e) {
throw new Error(`ERROR: could not decode as base64: '${input}': ${e}`);
}
// Convert |decodedValue| to an array so that it can be hashed by the
// nsICryptoHash instance below.
// In most cases across the code base, convertToByteArray() of
// nsIScriptableUnicodeConverter is used to do this, but the method doesn't
// seem to work here.
let data = [];
for (let i = 0; i < decodedValue.length; i++) {
data[i] = decodedValue.charCodeAt(i);
}
let hasher = Cc["@mozilla.org/security/hash;1"]
.createInstance(Ci.nsICryptoHash);
hasher.init(hasher.SHA256);
hasher.update(data, data.length);
// true is passed so that the hasher returns a Base64 encoded string.
return hasher.finish(true);
}
// 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 "sha256/" + base64(hash),
// a PEM encoded public key, 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 SHA-256 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, certNameToSKD, certSKDToName) {
// Prefixes that we care about.
const BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
const END_CERT = "-----END CERTIFICATE-----";
const BEGIN_PUB_KEY = "-----BEGIN PUBLIC KEY-----";
const END_PUB_KEY = "-----END PUBLIC KEY-----";
// Parsing states.
const PRE_NAME = 0;
const POST_NAME = 1;
const IN_CERT = 2;
const IN_PUB_KEY = 3;
let state = PRE_NAME;
let lines = download(filename).split("\n");
let name = "";
let pemCert = "";
let pemPubKey = "";
let hash = "";
let chromeNameToHash = {};
let chromeNameToMozName = {};
let chromeName;
for (let line of lines) {
// 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(SHA256_PREFIX)) {
hash = line.substring(SHA256_PREFIX.length);
chromeNameToHash[chromeName] = hash;
certNameToSKD[chromeName] = hash;
certSKDToName[hash] = chromeName;
state = PRE_NAME;
} else if (line.startsWith(BEGIN_CERT)) {
state = IN_CERT;
} else if (line.startsWith(BEGIN_PUB_KEY)) {
state = IN_PUB_KEY;
} else {
throw new Error("ERROR: couldn't parse Chrome certificate file " +
"line: " + line);
}
break;
case IN_CERT:
if (line.startsWith(END_CERT)) {
state = PRE_NAME;
hash = getSKDFromPem(pemCert);
pemCert = "";
let mozName;
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;
case IN_PUB_KEY:
if (line.startsWith(END_PUB_KEY)) {
state = PRE_NAME;
hash = sha256Base64(pemPubKey);
pemPubKey = "";
chromeNameToHash[chromeName] = hash;
certNameToSKD[chromeName] = hash;
certSKDToName[hash] = chromeName;
} else {
pemPubKey += line;
}
break;
default:
throw new Error("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
// 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, sha256_hashes: [] };
// Translate the Chrome pinset format to ours
pin.static_spki_hashes.forEach(function(name) {
if (name in chromeNameToHash) {
let hash = chromeNameToHash[name];
pinset.sha256_hashes.push(certSKDToName[hash]);
// We should have already added hashes for all of these when we
// imported the certificate file.
if (!certNameToSKD[name]) {
throw new Error("ERROR: 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) {
// HSTS entry only
if (!entry.pins) {
return;
}
let pinsetName = cData.substitute_pinsets[entry.pins];
if (!pinsetName) {
pinsetName = entry.pins;
}
// We trim the entry name here to avoid breaking hostname comparisons in the
// HPKP implementation.
entry.name = entry.name.trim();
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(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;
}
{
// This is the pinning test certificate. The key hash identifies the
// default RSA key from pykey.
let name = "End Entity Test Cert";
let SKD = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
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) {
let prefix = "kPinset_" + pinset.name;
if (!pinset.sha256_hashes || pinset.sha256_hashes.length == 0) {
throw new Error(`ERROR: Pinset ${pinset.name} does not contain any hashes`);
}
writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
pinset.sha256_hashes);
}
function writeFingerprints(certNameToSKD, certSKDToName, name, hashes) {
let varPrefix = "kPinset_" + name;
writeString("static const char* const " + varPrefix + "_Data[] = {\n");
let SKDList = [];
for (let certName of hashes) {
if (!(certName in certNameToSKD)) {
throw new Error(`ERROR: 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.indexOf("mozilla") != -1 &&
entry.pins != "mozilla_test")) {
printVal += "true, ";
} else {
printVal += "false, ";
}
if ("id" in entry) {
if (entry.id >= 256) {
throw new Error("ERROR: 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 mozillaDomains = {};
gStaticPins.entries.forEach(function(entry) {
mozillaDomains[entry.name] = true;
});
// For any domain for which we have set pins, exclude them from
// chromeImportedEntries.
for (let i = chromeImportedEntries.length - 1; i >= 0; i--) {
if (mozillaDomains[chromeImportedEntries[i].name]) {
dump("Skipping duplicate pinset for domain " +
JSON.stringify(chromeImportedEntries[i], undefined, 2) + "\n");
chromeImportedEntries.splice(i, 1);
}
}
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.
let usedFingerprints = {};
let mozillaPins = {};
gStaticPins.pinsets.forEach(function(pinset) {
mozillaPins[pinset.name] = true;
pinset.sha256_hashes.forEach(function (name) {
usedFingerprints[name] = true;
});
});
for (let key in chromeImportedPinsets) {
let pinset = chromeImportedPinsets[key];
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) {
if (mozillaPins[key]) {
dump("Skipping duplicate pinset " + key + "\n");
} else {
dump("Writing pinset " + key + "\n");
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;
}
var extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates);
var [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(extraCertificates);
var [ chromeNameToHash, chromeNameToMozName ] = downloadAndParseChromeCerts(
gStaticPins.chromium_data.cert_file_url, certNameToSKD, certSKDToName);
var [ chromeImportedPinsets, chromeImportedEntries ] =
downloadAndParseChromePins(gStaticPins.chromium_data.json_file_url,
chromeNameToHash, chromeNameToMozName, certNameToSKD, certSKDToName);
writeFile(certNameToSKD, certSKDToName, chromeImportedPinsets,
chromeImportedEntries);
FileUtils.closeSafeFileOutputStream(gFileOutputStream);