Skip to content

Commit

Permalink
Refresh the built-in certificate blacklist
Browse files Browse the repository at this point in the history
This change updates the built-in blacklist to be largely aligned
with the upstream CRLSets. This updates the blacklist to be based
on SHA-256, and includes the certificates (and documentation), so
that future changes are easier to make.

The blacklisting algorithm is tweaked to use a binary_search rather
than a linear search, for that little bit of oomph.

It moves from blacklisting the misissued Comodo certificates by
serial to blacklisting them by key, which fixes a bug in that the
serial blacklist was being applied to all CAs rather than just the
single Comodo CA. It also deprecates the associated histogram, which
was only ever used for the Comodo serials, rather than the overall
blacklisting.

This change is part of shifting the blacklist processing from being
within the CertVerifyProc to being something that can be fed into the
platform-native verifiers, so that they can influence path building
with these results (for blacklisting intermediates).

BUG=579824, 570909, 570908
TBR=isherman@chromium.org

Review URL: https://codereview.chromium.org/1612853002

Cr-Commit-Position: refs/heads/master@{#371152}
  • Loading branch information
sleevi authored and Commit bot committed Jan 23, 2016
1 parent 0536dbb commit d7eeca2
Show file tree
Hide file tree
Showing 44 changed files with 4,205 additions and 183 deletions.
209 changes: 30 additions & 179 deletions net/cert/cert_verify_proc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include <stdint.h>

#include <algorithm>

#include "base/metrics/histogram.h"
#include "base/metrics/histogram_macros.h"
#include "base/sha1.h"
Expand Down Expand Up @@ -179,6 +181,25 @@ bool IsPastSHA1DeprecationDate(const X509Certificate& cert) {
return start >= kSHA1DeprecationDate;
}

// Comparison functor used for binary searching whether a given HashValue,
// which MUST be a SHA-256 hash, is contained with an array of SHA-256
// hashes.
struct HashToArrayComparator {
template <size_t N>
bool operator()(const uint8_t(&lhs)[N], const HashValue& rhs) const {
static_assert(N == crypto::kSHA256Length,
"Only SHA-256 hashes are supported");
return memcmp(lhs, rhs.data(), crypto::kSHA256Length) < 0;
}

template <size_t N>
bool operator()(const HashValue& lhs, const uint8_t(&rhs)[N]) const {
static_assert(N == crypto::kSHA256Length,
"Only SHA-256 hashes are supported");
return memcmp(lhs.data(), rhs, crypto::kSHA256Length) < 0;
}
};

} // namespace

// static
Expand Down Expand Up @@ -320,70 +341,6 @@ int CertVerifyProc::Verify(X509Certificate* cert,

// static
bool CertVerifyProc::IsBlacklisted(X509Certificate* cert) {
static const unsigned kComodoSerialBytes = 16;
static const uint8_t kComodoSerials[][kComodoSerialBytes] = {
// Not a real certificate. For testing only.
{0x07,0x7a,0x59,0xbc,0xd5,0x34,0x59,0x60,0x1c,0xa6,0x90,0x72,0x67,0xa6,0xdd,0x1c},

// The next nine certificates all expire on Fri Mar 14 23:59:59 2014.
// Some serial numbers actually have a leading 0x00 byte required to
// encode a positive integer in DER if the most significant bit is 0.
// We omit the leading 0x00 bytes to make all serial numbers 16 bytes.

// Subject: CN=mail.google.com
// subjectAltName dNSName: mail.google.com, www.mail.google.com
{0x04,0x7e,0xcb,0xe9,0xfc,0xa5,0x5f,0x7b,0xd0,0x9e,0xae,0x36,0xe1,0x0c,0xae,0x1e},
// Subject: CN=global trustee
// subjectAltName dNSName: global trustee
// Note: not a CA certificate.
{0xd8,0xf3,0x5f,0x4e,0xb7,0x87,0x2b,0x2d,0xab,0x06,0x92,0xe3,0x15,0x38,0x2f,0xb0},
// Subject: CN=login.live.com
// subjectAltName dNSName: login.live.com, www.login.live.com
{0xb0,0xb7,0x13,0x3e,0xd0,0x96,0xf9,0xb5,0x6f,0xae,0x91,0xc8,0x74,0xbd,0x3a,0xc0},
// Subject: CN=addons.mozilla.org
// subjectAltName dNSName: addons.mozilla.org, www.addons.mozilla.org
{0x92,0x39,0xd5,0x34,0x8f,0x40,0xd1,0x69,0x5a,0x74,0x54,0x70,0xe1,0xf2,0x3f,0x43},
// Subject: CN=login.skype.com
// subjectAltName dNSName: login.skype.com, www.login.skype.com
{0xe9,0x02,0x8b,0x95,0x78,0xe4,0x15,0xdc,0x1a,0x71,0x0a,0x2b,0x88,0x15,0x44,0x47},
// Subject: CN=login.yahoo.com
// subjectAltName dNSName: login.yahoo.com, www.login.yahoo.com
{0xd7,0x55,0x8f,0xda,0xf5,0xf1,0x10,0x5b,0xb2,0x13,0x28,0x2b,0x70,0x77,0x29,0xa3},
// Subject: CN=www.google.com
// subjectAltName dNSName: www.google.com, google.com
{0xf5,0xc8,0x6a,0xf3,0x61,0x62,0xf1,0x3a,0x64,0xf5,0x4f,0x6d,0xc9,0x58,0x7c,0x06},
// Subject: CN=login.yahoo.com
// subjectAltName dNSName: login.yahoo.com
{0x39,0x2a,0x43,0x4f,0x0e,0x07,0xdf,0x1f,0x8a,0xa3,0x05,0xde,0x34,0xe0,0xc2,0x29},
// Subject: CN=login.yahoo.com
// subjectAltName dNSName: login.yahoo.com
{0x3e,0x75,0xce,0xd4,0x6b,0x69,0x30,0x21,0x21,0x88,0x30,0xae,0x86,0xa8,0x2a,0x71},
};

const std::string& serial_number = cert->serial_number();
if (!serial_number.empty() && (serial_number[0] & 0x80) != 0) {
// This is a negative serial number, which isn't technically allowed but
// which probably happens. In order to avoid confusing a negative serial
// number with a positive one once the leading zeros have been removed, we
// disregard it.
return false;
}

base::StringPiece serial(serial_number);
// Remove leading zeros.
while (serial.size() > 1 && serial[0] == 0)
serial.remove_prefix(1);

if (serial.size() == kComodoSerialBytes) {
for (unsigned i = 0; i < arraysize(kComodoSerials); i++) {
if (memcmp(kComodoSerials[i], serial.data(), kComodoSerialBytes) == 0) {
UMA_HISTOGRAM_ENUMERATION("Net.SSLCertBlacklisted", i,
arraysize(kComodoSerials) + 1);
return true;
}
}
}

// CloudFlare revoked all certificates issued prior to April 2nd, 2014. Thus
// all certificates where the CN ends with ".cloudflare.com" with a prior
// issuance date are rejected.
Expand All @@ -407,125 +364,19 @@ bool CertVerifyProc::IsBlacklisted(X509Certificate* cert) {
}

// static
// NOTE: This implementation assumes and enforces that the hashes are SHA1.
bool CertVerifyProc::IsPublicKeyBlacklisted(
const HashValueVector& public_key_hashes) {
static const unsigned kNumSHA1Hashes = 14;
static const uint8_t kSHA1Hashes[kNumSHA1Hashes][base::kSHA1Length] = {
// Subject: CN=DigiNotar Root CA
// Issuer: CN=Entrust.net x2 and self-signed
{0x41, 0x0f, 0x36, 0x36, 0x32, 0x58, 0xf3, 0x0b, 0x34, 0x7d,
0x12, 0xce, 0x48, 0x63, 0xe4, 0x33, 0x43, 0x78, 0x06, 0xa8},
// Subject: CN=DigiNotar Cyber CA
// Issuer: CN=GTE CyberTrust Global Root
{0xc4, 0xf9, 0x66, 0x37, 0x16, 0xcd, 0x5e, 0x71, 0xd6, 0x95,
0x0b, 0x5f, 0x33, 0xce, 0x04, 0x1c, 0x95, 0xb4, 0x35, 0xd1},
// Subject: CN=DigiNotar Services 1024 CA
// Issuer: CN=Entrust.net
{0xe2, 0x3b, 0x8d, 0x10, 0x5f, 0x87, 0x71, 0x0a, 0x68, 0xd9,
0x24, 0x80, 0x50, 0xeb, 0xef, 0xc6, 0x27, 0xbe, 0x4c, 0xa6},
// Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2
// Issuer: CN=Staat der Nederlanden Organisatie CA - G2
{0x7b, 0x2e, 0x16, 0xbc, 0x39, 0xbc, 0xd7, 0x2b, 0x45, 0x6e,
0x9f, 0x05, 0x5d, 0x1d, 0xe6, 0x15, 0xb7, 0x49, 0x45, 0xdb},
// Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven
// Issuer: CN=Staat der Nederlanden Overheid CA
{0xe8, 0xf9, 0x12, 0x00, 0xc6, 0x5c, 0xee, 0x16, 0xe0, 0x39,
0xb9, 0xf8, 0x83, 0x84, 0x16, 0x61, 0x63, 0x5f, 0x81, 0xc5},
// Issuer: CN=Trustwave Organization Issuing CA, Level 2
// Covers two certificates, the latter of which expires Apr 15 21:09:30
// 2021 GMT.
{0xe1, 0x2d, 0x89, 0xf5, 0x6d, 0x22, 0x76, 0xf8, 0x30, 0xe6,
0xce, 0xaf, 0xa6, 0x6c, 0x72, 0x5c, 0x0b, 0x41, 0xa9, 0x32},
// Cyberoam CA certificate. Private key leaked, but this certificate would
// only have been installed by Cyberoam customers. The certificate expires
// in 2036, but we can probably remove in a couple of years (2014).
{0xd9, 0xf5, 0xc6, 0xce, 0x57, 0xff, 0xaa, 0x39, 0xcc, 0x7e,
0xd1, 0x72, 0xbd, 0x53, 0xe0, 0xd3, 0x07, 0x83, 0x4b, 0xd1},
// Win32/Sirefef.gen!C generates fake certificates with this public key.
{0xa4, 0xf5, 0x6e, 0x9e, 0x1d, 0x9a, 0x3b, 0x7b, 0x1a, 0xc3,
0x31, 0xcf, 0x64, 0xfc, 0x76, 0x2c, 0xd0, 0x51, 0xfb, 0xa4},
// Three retired intermediate certificates from Symantec. No compromise;
// just for robustness. All expire May 17 23:59:59 2018.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=966060
{0x68, 0x5e, 0xec, 0x0a, 0x39, 0xf6, 0x68, 0xae, 0x8f, 0xd8,
0x96, 0x4f, 0x98, 0x74, 0x76, 0xb4, 0x50, 0x4f, 0xd2, 0xbe},
{0x0e, 0x50, 0x2d, 0x4d, 0xd1, 0xe1, 0x60, 0x36, 0x8a, 0x31,
0xf0, 0x6a, 0x81, 0x04, 0x31, 0xba, 0x6f, 0x72, 0xc0, 0x41},
{0x93, 0xd1, 0x53, 0x22, 0x29, 0xcc, 0x2a, 0xbd, 0x21, 0xdf,
0xf5, 0x97, 0xee, 0x32, 0x0f, 0xe4, 0x24, 0x6f, 0x3d, 0x0c},
// C=IN, O=National Informatics Centre, CN=NIC CA 2011. Issued by
// C=IN, O=India PKI, CN=CCA India 2011.
// Expires March 11th 2016.
{0x07, 0x7a, 0xc7, 0xde, 0x8d, 0xa5, 0x58, 0x64, 0x3a, 0x06,
0xc5, 0x36, 0x9e, 0x55, 0x4f, 0xae, 0xb3, 0xdf, 0xa1, 0x66},
// C=IN, O=National Informatics Centre, CN=NIC CA 2014. Issued by
// C=IN, O=India PKI, CN=CCA India 2014.
// Expires: March 5th, 2024.
{0xe5, 0x8e, 0x31, 0x5b, 0xaa, 0xee, 0xaa, 0xc6, 0xe7, 0x2e,
0xc9, 0x57, 0x36, 0x70, 0xca, 0x2f, 0x25, 0x4e, 0xc3, 0x47},
// C=DE, O=Fraunhofer, OU=Fraunhofer Corporate PKI,
// CN=Fraunhofer Service CA 2007.
// Expires: Jun 30 2019.
// No compromise, just for robustness. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=1076940
{0x38, 0x4d, 0x0c, 0x1d, 0xc4, 0x77, 0xa7, 0xb3, 0xf8, 0x67,
0x86, 0xd0, 0x18, 0x51, 0x9f, 0x58, 0x9f, 0x1e, 0x9e, 0x25},
};

static const unsigned kNumSHA256Hashes = 4;
static const uint8_t kSHA256Hashes[kNumSHA256Hashes][crypto::kSHA256Length] =
{
// Two intermediates issued by TurkTrust to *.ego.gov.tr and
// e-islem.kktcmerkezbankasi.org. Expires July 6 2021 and August 5 2021,
// respectively.
// See http://googleonlinesecurity.blogspot.com/2013/01/enhancing-digital-certificate-security.html
{0x3e, 0xdb, 0xd9, 0xac, 0xe6, 0x39, 0xba, 0x1a,
0x2d, 0x4a, 0xd0, 0x47, 0x18, 0x71, 0x1f, 0xda,
0x23, 0xe8, 0x59, 0xb2, 0xfb, 0xf5, 0xd1, 0x37,
0xd4, 0x24, 0x04, 0x5e, 0x79, 0x19, 0xdf, 0xb9},
{0xc1, 0x73, 0xf0, 0x62, 0x64, 0x56, 0xca, 0x85,
0x4f, 0xf2, 0xa7, 0xf0, 0xb1, 0x33, 0xa7, 0xcf,
0x4d, 0x02, 0x11, 0xe5, 0x52, 0xf2, 0x4b, 0x3e,
0x33, 0xad, 0xe8, 0xc5, 0x9f, 0x0a, 0x42, 0x4c},

// xs4all certificate. Expires March 19 2016.
// https://raymii.org/s/blog/How_I_got_a_valid_SSL_certificate_for_my_ISPs_main_website.html
{0xf2, 0xbb, 0xe0, 0x4c, 0x5d, 0xc7, 0x0d, 0x76,
0x3e, 0x89, 0xc5, 0xa0, 0x52, 0x70, 0x48, 0xcd,
0x9e, 0xcd, 0x39, 0xeb, 0x62, 0x1e, 0x20, 0x72,
0xff, 0x9a, 0x5f, 0x84, 0x32, 0x57, 0x1a, 0xa0},

// Japanese National Institute of Informatics intermediate. No suggestion
// of compromise, it's just being discontinued.
// Expires March 27th 2019
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1188582
{0x5c, 0x72, 0x2c, 0xb7, 0x0f, 0xb3, 0x11, 0xf2,
0x1e, 0x0d, 0xa0, 0xe7, 0xd1, 0x2e, 0xbc, 0x8e,
0x05, 0xf6, 0x07, 0x96, 0xbc, 0x49, 0xcf, 0x51,
0x18, 0x49, 0xd5, 0xbc, 0x62, 0x03, 0x03, 0x82},
};

for (unsigned i = 0; i < kNumSHA1Hashes; i++) {
for (HashValueVector::const_iterator j = public_key_hashes.begin();
j != public_key_hashes.end(); ++j) {
if (j->tag == HASH_VALUE_SHA1 &&
memcmp(j->data(), kSHA1Hashes[i], base::kSHA1Length) == 0) {
return true;
}
}
}

for (unsigned i = 0; i < kNumSHA256Hashes; i++) {
for (HashValueVector::const_iterator j = public_key_hashes.begin();
j != public_key_hashes.end(); ++j) {
if (j->tag == HASH_VALUE_SHA256 &&
memcmp(j->data(), kSHA256Hashes[i], crypto::kSHA256Length) == 0) {
return true;
}
// Defines kBlacklistedSPKIs.
#include "net/cert/cert_verify_proc_blacklist.inc"
for (const auto& hash : public_key_hashes) {
if (hash.tag != HASH_VALUE_SHA256)
continue;
if (std::binary_search(std::begin(kBlacklistedSPKIs),
std::end(kBlacklistedSPKIs), hash,
HashToArrayComparator())) {
return true;
}
}

return false;
}

Expand Down
Loading

0 comments on commit d7eeca2

Please sign in to comment.