summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorpalmer@chromium.org <palmer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-01 19:39:48 +0000
committerpalmer@chromium.org <palmer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-01 19:39:48 +0000
commitf43b89f30c817107bc595f45098d908f84bf9baa (patch)
tree9b606ccecd8b30b1f72c52576c2cb034bb6c7244 /net
parent9eedb4825fd27cec7086f9be09a08eb8248ca868 (diff)
downloadchromium_src-f43b89f30c817107bc595f45098d908f84bf9baa.zip
chromium_src-f43b89f30c817107bc595f45098d908f84bf9baa.tar.gz
chromium_src-f43b89f30c817107bc595f45098d908f84bf9baa.tar.bz2
Refactor TransportSecurityState.
Do some minor "gcl lint" cleanup while here. BUG=113280, 120373 TEST=net_unittests, browser_tests, unit_tests TransportSecurityPersisterTest.* Review URL: http://codereview.chromium.org/9415040 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134754 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/transport_security_state.cc678
-rw-r--r--net/base/transport_security_state.h375
-rw-r--r--net/base/transport_security_state_unittest.cc987
-rw-r--r--net/base/x509_cert_types.h7
-rw-r--r--net/socket/ssl_client_socket_nss.cc50
-rw-r--r--net/socket_stream/socket_stream.cc6
-rw-r--r--net/socket_stream/socket_stream_job.cc2
-rw-r--r--net/url_request/url_request_context_builder.cc2
-rw-r--r--net/url_request/url_request_http_job.cc50
-rw-r--r--net/url_request/url_request_unittest.cc30
-rw-r--r--net/websockets/websocket_job_spdy2_unittest.cc9
-rw-r--r--net/websockets/websocket_job_spdy3_unittest.cc9
12 files changed, 719 insertions, 1486 deletions
diff --git a/net/base/transport_security_state.cc b/net/base/transport_security_state.cc
index b5fce7b..b2450dd 100644
--- a/net/base/transport_security_state.cc
+++ b/net/base/transport_security_state.cc
@@ -19,8 +19,6 @@
#include <utility>
#include "base/base64.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
@@ -33,7 +31,6 @@
#include "base/values.h"
#include "crypto/sha2.h"
#include "googleurl/src/gurl.h"
-#include "net/base/asn1_util.h"
#include "net/base/dns_util.h"
#include "net/base/ssl_info.h"
#include "net/base/x509_certificate.h"
@@ -47,20 +44,16 @@ namespace net {
const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year
-TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts)
- : delegate_(NULL) {
- if (!hsts_hosts.empty()) {
- bool dirty;
- Deserialise(hsts_hosts, &dirty, &forced_hosts_);
- }
-}
-
static std::string HashHost(const std::string& canonicalized_host) {
char hashed[crypto::kSHA256Length];
crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
return std::string(hashed, sizeof(hashed));
}
+TransportSecurityState::TransportSecurityState()
+ : delegate_(NULL) {
+}
+
void TransportSecurityState::SetDelegate(
TransportSecurityState::Delegate* delegate) {
delegate_ = delegate;
@@ -74,24 +67,19 @@ void TransportSecurityState::EnableHost(const std::string& host,
if (canonicalized_host.empty())
return;
- // Only override a preloaded state if the new state describes a more strict
- // policy. TODO(palmer): Reconsider this?
DomainState existing_state;
- if (IsPreloadedSTS(canonicalized_host, true, &existing_state) &&
- canonicalized_host == CanonicalizeHost(existing_state.domain) &&
- existing_state.IsMoreStrict(state)) {
- return;
- }
- // Use the original creation date if we already have this host.
+ // Use the original creation date if we already have this host. (But note
+ // that statically-defined states have no |created| date. Therefore, we do
+ // not bother to search the SNI-only static states.)
DomainState state_copy(state);
- if (GetDomainState(&existing_state, host, true) &&
+ if (GetDomainState(host, false /* sni_enabled */, &existing_state) &&
!existing_state.created.is_null()) {
state_copy.created = existing_state.created;
}
- // We don't store these values.
- state_copy.preloaded = false;
+ // No need to store this value since it is redundant. (|canonicalized_host|
+ // is the map key.)
state_copy.domain.clear();
enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
@@ -115,27 +103,9 @@ bool TransportSecurityState::DeleteHost(const std::string& host) {
return false;
}
-bool TransportSecurityState::HasPinsForHost(DomainState* result,
- const std::string& host,
- bool sni_available) {
- DCHECK(CalledOnValidThread());
-
- return HasMetadata(result, host, sni_available) &&
- (!result->dynamic_spki_hashes.empty() ||
- !result->preloaded_spki_hashes.empty());
-}
-
-bool TransportSecurityState::GetDomainState(DomainState* result,
- const std::string& host,
- bool sni_available) {
- DCHECK(CalledOnValidThread());
-
- return HasMetadata(result, host, sni_available);
-}
-
-bool TransportSecurityState::HasMetadata(DomainState* result,
- const std::string& host,
- bool sni_available) {
+bool TransportSecurityState::GetDomainState(const std::string& host,
+ bool sni_enabled,
+ DomainState* result) {
DCHECK(CalledOnValidThread());
DomainState state;
@@ -143,7 +113,8 @@ bool TransportSecurityState::HasMetadata(DomainState* result,
if (canonicalized_host.empty())
return false;
- bool has_preload = IsPreloadedSTS(canonicalized_host, sni_available, &state);
+ bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled,
+ &state);
std::string canonicalized_preload = CanonicalizeHost(state.domain);
base::Time current_time(base::Time::Now());
@@ -162,7 +133,7 @@ bool TransportSecurityState::HasMetadata(DomainState* result,
if (j == enabled_hosts_.end())
continue;
- if (current_time > j->second.expiry &&
+ if (current_time > j->second.upgrade_expiry &&
current_time > j->second.dynamic_spki_hashes_expiry) {
enabled_hosts_.erase(j);
DirtyNotify();
@@ -290,23 +261,6 @@ static bool ParseAndAppendPin(const std::string& value,
return true;
}
-// static
-bool TransportSecurityState::GetPublicKeyHash(
- const X509Certificate& cert, SHA1Fingerprint* spki_hash) {
- std::string der_bytes;
- if (!X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der_bytes))
- return false;
-
- base::StringPiece spki;
- if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki))
- return false;
-
- base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()),
- spki.size(), spki_hash->data);
-
- return true;
-}
-
struct FingerprintsEqualPredicate {
explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) :
fingerprint_(fingerprint) {}
@@ -368,13 +322,12 @@ static bool IsPinListValid(const FingerprintVector& pins,
// "Public-Key-Pins" ":"
// "max-age" "=" delta-seconds ";"
// "pin-" algo "=" base64 [ ";" ... ]
-//
-// static
-bool TransportSecurityState::ParsePinsHeader(const std::string& value,
- const SSLInfo& ssl_info,
- DomainState* state) {
+bool TransportSecurityState::DomainState::ParsePinsHeader(
+ const base::Time& now,
+ const std::string& value,
+ const SSLInfo& ssl_info) {
bool parsed_max_age = false;
- int max_age = 0;
+ int max_age_candidate = 0;
FingerprintVector pins;
std::string source = value;
@@ -389,11 +342,12 @@ bool TransportSecurityState::ParsePinsHeader(const std::string& value,
if (LowerCaseEqualsASCII(equals.first, "max-age")) {
if (equals.second.empty() ||
- !MaxAgeToInt(equals.second.begin(), equals.second.end(), &max_age)) {
+ !MaxAgeToInt(equals.second.begin(), equals.second.end(),
+ &max_age_candidate)) {
return false;
}
- if (max_age > kMaxHSTSAgeSecs)
- max_age = kMaxHSTSAgeSecs;
+ if (max_age_candidate > kMaxHSTSAgeSecs)
+ max_age_candidate = kMaxHSTSAgeSecs;
parsed_max_age = true;
} else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) {
if (!ParseAndAppendPin(equals.second, &pins))
@@ -410,15 +364,14 @@ bool TransportSecurityState::ParsePinsHeader(const std::string& value,
if (!parsed_max_age || !IsPinListValid(pins, ssl_info))
return false;
- state->max_age = max_age;
- state->dynamic_spki_hashes_expiry =
- base::Time::Now() + base::TimeDelta::FromSeconds(max_age);
+ dynamic_spki_hashes_expiry =
+ now + base::TimeDelta::FromSeconds(max_age_candidate);
- state->dynamic_spki_hashes.clear();
- if (max_age > 0) {
+ dynamic_spki_hashes.clear();
+ if (max_age_candidate > 0) {
for (FingerprintVector::const_iterator i = pins.begin();
- i != pins.end(); i++) {
- state->dynamic_spki_hashes.push_back(*i);
+ i != pins.end(); ++i) {
+ dynamic_spki_hashes.push_back(*i);
}
}
@@ -427,14 +380,9 @@ bool TransportSecurityState::ParsePinsHeader(const std::string& value,
// "Strict-Transport-Security" ":"
// "max-age" "=" delta-seconds [ ";" "includeSubDomains" ]
-//
-// static
-bool TransportSecurityState::ParseHeader(const std::string& value,
- int* max_age,
- bool* include_subdomains) {
- DCHECK(max_age);
- DCHECK(include_subdomains);
-
+bool TransportSecurityState::DomainState::ParseSTSHeader(
+ const base::Time& now,
+ const std::string& value) {
int max_age_candidate = 0;
enum ParserState {
@@ -511,14 +459,18 @@ bool TransportSecurityState::ParseHeader(const std::string& value,
case AFTER_MAX_AGE_EQUALS:
return false;
case AFTER_MAX_AGE:
- *max_age = max_age_candidate;
- *include_subdomains = false;
+ upgrade_expiry =
+ now + base::TimeDelta::FromSeconds(max_age_candidate);
+ include_subdomains = false;
+ upgrade_mode = MODE_FORCE_HTTPS;
return true;
case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER:
return false;
case AFTER_INCLUDE_SUBDOMAINS:
- *max_age = max_age_candidate;
- *include_subdomains = true;
+ upgrade_expiry =
+ now + base::TimeDelta::FromSeconds(max_age_candidate);
+ include_subdomains = true;
+ upgrade_mode = MODE_FORCE_HTTPS;
return true;
default:
NOTREACHED();
@@ -526,370 +478,6 @@ bool TransportSecurityState::ParseHeader(const std::string& value,
}
}
-// Side pinning and superfluous certificates:
-//
-// In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a
-// Subject of CN=meta. When we find one we'll currently try and parse side
-// pinned key from it.
-//
-// A side pin is a key which can be pinned to, but also can be kept offline and
-// still held by the site owner. The CN=meta certificate is just a backwards
-// compatiable method of carrying a lump of bytes to the client. (We could use
-// a TLS extension just as well, but it's a lot easier for admins to add extra
-// certificates to the chain.)
-
-// A TagMap represents the simple key-value structure that we use. Keys are
-// 32-bit ints. Values are byte strings.
-typedef std::map<uint32, base::StringPiece> TagMap;
-
-// ParseTags parses a list of key-value pairs from |in| to |out| and advances
-// |in| past the data. The key-value pair data is:
-// u16le num_tags
-// u32le tag[num_tags]
-// u16le lengths[num_tags]
-// ...data...
-static bool ParseTags(base::StringPiece* in, TagMap *out) {
- // Many part of Chrome already assume little-endian. This is just to help
- // anyone who should try to port it in the future.
-#if defined(__BYTE_ORDER)
- // Linux check
- COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian);
-#elif defined(__BIG_ENDIAN__)
- // Mac check
- #error assumes little endian
-#endif
-
- uint16 num_tags_16;
- if (in->size() < sizeof(num_tags_16))
- return false;
-
- memcpy(&num_tags_16, in->data(), sizeof(num_tags_16));
- in->remove_prefix(sizeof(num_tags_16));
- unsigned num_tags = num_tags_16;
-
- if (in->size() < 6 * num_tags)
- return false;
-
- const uint32* tags = reinterpret_cast<const uint32*>(in->data());
- const uint16* lens = reinterpret_cast<const uint16*>(
- in->data() + 4*num_tags);
- in->remove_prefix(6*num_tags);
-
- uint32 prev_tag = 0;
- for (unsigned i = 0; i < num_tags; i++) {
- size_t len = lens[i];
- uint32 tag = tags[i];
-
- if (in->size() < len)
- return false;
- // tags must be in ascending order.
- if (i > 0 && prev_tag >= tag)
- return false;
- (*out)[tag] = base::StringPiece(in->data(), len);
- in->remove_prefix(len);
- prev_tag = tag;
- }
-
- return true;
-}
-
-// GetTag extracts the data associated with |tag| in |tags|.
-static bool GetTag(uint32 tag, const TagMap& tags, base::StringPiece* out) {
- TagMap::const_iterator i = tags.find(tag);
- if (i == tags.end())
- return false;
-
- *out = i->second;
- return true;
-}
-
-// kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve
-// point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The
-// ASN.1 interpretation of these bytes is:
-//
-// 0:d=0 hl=2 l= 89 cons: SEQUENCE
-// 2:d=1 hl=2 l= 19 cons: SEQUENCE
-// 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
-// 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1
-// 23:d=1 hl=2 l= 66 prim: BIT STRING
-static const uint8 kP256SubjectPublicKeyInfoPrefix[] = {
- 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
- 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
- 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
- 0x42, 0x00,
-};
-
-// VerifySignature returns true iff |sig| is a valid signature of
-// |hash| by |pubkey|. The actual implementation is crypto library
-// specific.
-static bool VerifySignature(const base::StringPiece& pubkey,
- const base::StringPiece& sig,
- const base::StringPiece& hash);
-
-#if defined(USE_OPENSSL)
-
-static EVP_PKEY* DecodeX962P256PublicKey(
- const base::StringPiece& pubkey_bytes) {
- // The public key is an X9.62 encoded P256 point.
- if (pubkey_bytes.size() != 1 + 2*32)
- return NULL;
-
- std::string pubkey_spki(
- reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix),
- sizeof(kP256SubjectPublicKeyInfoPrefix));
- pubkey_spki += pubkey_bytes.as_string();
-
- EVP_PKEY* ret = NULL;
- const unsigned char* der_pubkey =
- reinterpret_cast<const unsigned char*>(pubkey_spki.data());
- d2i_PUBKEY(&ret, &der_pubkey, pubkey_spki.size());
- return ret;
-}
-
-static bool VerifySignature(const base::StringPiece& pubkey,
- const base::StringPiece& sig,
- const base::StringPiece& hash) {
- crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> secpubkey(
- DecodeX962P256PublicKey(pubkey));
- if (!secpubkey.get())
- return false;
-
-
- crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ec_key(
- EVP_PKEY_get1_EC_KEY(secpubkey.get()));
- if (!ec_key.get())
- return false;
-
- return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash.data()),
- hash.size(),
- reinterpret_cast<const unsigned char*>(sig.data()),
- sig.size(), ec_key.get()) == 1;
-}
-
-#else
-
-// DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic
-// curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey.
-static SECKEYPublicKey* DecodeX962P256PublicKey(
- const base::StringPiece& pubkey_bytes) {
- // The public key is an X9.62 encoded P256 point.
- if (pubkey_bytes.size() != 1 + 2*32)
- return NULL;
-
- std::string pubkey_spki(
- reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix),
- sizeof(kP256SubjectPublicKeyInfoPrefix));
- pubkey_spki += pubkey_bytes.as_string();
-
- SECItem der;
- memset(&der, 0, sizeof(der));
- der.data = reinterpret_cast<uint8*>(const_cast<char*>(pubkey_spki.data()));
- der.len = pubkey_spki.size();
-
- CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der);
- if (!spki)
- return NULL;
- SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki);
- SECKEY_DestroySubjectPublicKeyInfo(spki);
-
- return public_key;
-}
-
-static bool VerifySignature(const base::StringPiece& pubkey,
- const base::StringPiece& sig,
- const base::StringPiece& hash) {
- SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey);
- if (!secpubkey)
- return false;
-
- SECItem sigitem;
- memset(&sigitem, 0, sizeof(sigitem));
- sigitem.data = reinterpret_cast<uint8*>(const_cast<char*>(sig.data()));
- sigitem.len = sig.size();
-
- // |decoded_sigitem| is newly allocated, as is the data that it points to.
- SECItem* decoded_sigitem = DSAU_DecodeDerSigToLen(
- &sigitem, SECKEY_SignatureLen(secpubkey));
-
- if (!decoded_sigitem) {
- SECKEY_DestroyPublicKey(secpubkey);
- return false;
- }
-
- SECItem hashitem;
- memset(&hashitem, 0, sizeof(hashitem));
- hashitem.data = reinterpret_cast<unsigned char*>(
- const_cast<char*>(hash.data()));
- hashitem.len = hash.size();
-
- SECStatus rv = PK11_Verify(secpubkey, decoded_sigitem, &hashitem, NULL);
- SECKEY_DestroyPublicKey(secpubkey);
- SECITEM_FreeItem(decoded_sigitem, PR_TRUE);
- return rv == SECSuccess;
-}
-
-#endif // !defined(USE_OPENSSL)
-
-// These are the tag values that we use. Tags are little-endian on the wire and
-// these values correspond to the ASCII of the name.
-static const uint32 kTagALGO = 0x4f474c41;
-static const uint32 kTagP256 = 0x36353250;
-static const uint32 kTagPUBK = 0x4b425550;
-static const uint32 kTagSIG = 0x474953;
-static const uint32 kTagSPIN = 0x4e495053;
-
-// static
-bool TransportSecurityState::ParseSidePin(
- const base::StringPiece& leaf_spki,
- const base::StringPiece& in_side_info,
- FingerprintVector* out_pub_key_hash) {
- base::StringPiece side_info(in_side_info);
-
- TagMap outer;
- if (!ParseTags(&side_info, &outer))
- return false;
- // trailing data is not allowed
- if (side_info.size())
- return false;
-
- base::StringPiece side_pin_bytes;
- if (!GetTag(kTagSPIN, outer, &side_pin_bytes))
- return false;
-
- bool have_parsed_a_key = false;
- uint8 leaf_spki_hash[crypto::kSHA256Length];
- bool have_leaf_spki_hash = false;
-
- while (side_pin_bytes.size() > 0) {
- TagMap side_pin;
- if (!ParseTags(&side_pin_bytes, &side_pin))
- return false;
-
- base::StringPiece algo, pubkey, sig;
- if (!GetTag(kTagALGO, side_pin, &algo) ||
- !GetTag(kTagPUBK, side_pin, &pubkey) ||
- !GetTag(kTagSIG, side_pin, &sig)) {
- return false;
- }
-
- if (algo.size() != sizeof(kTagP256) ||
- 0 != memcmp(algo.data(), &kTagP256, sizeof(kTagP256))) {
- // We don't support anything but P256 at the moment.
- continue;
- }
-
- if (!have_leaf_spki_hash) {
- crypto::SHA256HashString(
- leaf_spki.as_string(), leaf_spki_hash, sizeof(leaf_spki_hash));
- have_leaf_spki_hash = true;
- }
-
- if (VerifySignature(pubkey, sig, base::StringPiece(
- reinterpret_cast<const char*>(leaf_spki_hash),
- sizeof(leaf_spki_hash)))) {
- SHA1Fingerprint fpr;
- base::SHA1HashBytes(
- reinterpret_cast<const uint8*>(pubkey.data()),
- pubkey.size(),
- fpr.data);
- out_pub_key_hash->push_back(fpr);
- have_parsed_a_key = true;
- }
- }
-
- return have_parsed_a_key;
-}
-
-// This function converts the binary hashes, which we store in
-// |enabled_hosts_|, to a base64 string which we can include in a JSON file.
-static std::string HashedDomainToExternalString(const std::string& hashed) {
- std::string out;
- CHECK(base::Base64Encode(hashed, &out));
- return out;
-}
-
-// This inverts |HashedDomainToExternalString|, above. It turns an external
-// string (from a JSON file) into an internal (binary) string.
-static std::string ExternalStringToHashedDomain(const std::string& external) {
- std::string out;
- if (!base::Base64Decode(external, &out) ||
- out.size() != crypto::kSHA256Length) {
- return std::string();
- }
-
- return out;
-}
-
-static ListValue* SPKIHashesToListValue(const FingerprintVector& hashes) {
- ListValue* pins = new ListValue;
-
- for (FingerprintVector::const_iterator i = hashes.begin();
- i != hashes.end(); ++i) {
- std::string hash_str(reinterpret_cast<const char*>(i->data),
- sizeof(i->data));
- std::string b64;
- base::Base64Encode(hash_str, &b64);
- pins->Append(new StringValue("sha1/" + b64));
- }
-
- return pins;
-}
-
-bool TransportSecurityState::Serialise(std::string* output) {
- DCHECK(CalledOnValidThread());
-
- DictionaryValue toplevel;
- base::Time now = base::Time::Now();
- for (std::map<std::string, DomainState>::const_iterator
- i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) {
- DictionaryValue* state = new DictionaryValue;
- state->SetBoolean("include_subdomains", i->second.include_subdomains);
- state->SetDouble("created", i->second.created.ToDoubleT());
- state->SetDouble("expiry", i->second.expiry.ToDoubleT());
- state->SetDouble("dynamic_spki_hashes_expiry",
- i->second.dynamic_spki_hashes_expiry.ToDoubleT());
-
- switch (i->second.mode) {
- case DomainState::MODE_STRICT:
- state->SetString("mode", "strict");
- break;
- case DomainState::MODE_SPDY_ONLY:
- state->SetString("mode", "spdy-only");
- break;
- case DomainState::MODE_PINNING_ONLY:
- state->SetString("mode", "pinning-only");
- break;
- default:
- NOTREACHED() << "DomainState with unknown mode";
- delete state;
- continue;
- }
-
- state->Set("preloaded_spki_hashes",
- SPKIHashesToListValue(i->second.preloaded_spki_hashes));
-
- if (now < i->second.dynamic_spki_hashes_expiry) {
- state->Set("dynamic_spki_hashes",
- SPKIHashesToListValue(i->second.dynamic_spki_hashes));
- }
-
- toplevel.Set(HashedDomainToExternalString(i->first), state);
- }
-
- base::JSONWriter::WriteWithOptions(&toplevel,
- base::JSONWriter::OPTIONS_PRETTY_PRINT,
- output);
- return true;
-}
-
-bool TransportSecurityState::LoadEntries(const std::string& input,
- bool* dirty) {
- DCHECK(CalledOnValidThread());
-
- enabled_hosts_.clear();
- return Deserialise(input, dirty, &enabled_hosts_);
-}
-
static bool AddHash(const std::string& type_and_base64,
FingerprintVector* out) {
SHA1Fingerprint hash;
@@ -901,116 +489,7 @@ static bool AddHash(const std::string& type_and_base64,
return true;
}
-static void SPKIHashesFromListValue(FingerprintVector* hashes,
- const ListValue& pins) {
- size_t num_pins = pins.GetSize();
- for (size_t i = 0; i < num_pins; ++i) {
- std::string type_and_base64;
- if (pins.GetString(i, &type_and_base64))
- AddHash(type_and_base64, hashes);
- }
-}
-
-// static
-bool TransportSecurityState::Deserialise(
- const std::string& input,
- bool* dirty,
- std::map<std::string, DomainState>* out) {
- scoped_ptr<Value> value(base::JSONReader::Read(input));
- if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
- return false;
-
- DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get());
- const base::Time current_time(base::Time::Now());
- bool dirtied = false;
-
- for (DictionaryValue::key_iterator i = dict_value->begin_keys();
- i != dict_value->end_keys(); ++i) {
- DictionaryValue* state;
- if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &state))
- continue;
-
- bool include_subdomains;
- std::string mode_string;
- double created;
- double expiry;
- double dynamic_spki_hashes_expiry = 0.0;
-
- if (!state->GetBoolean("include_subdomains", &include_subdomains) ||
- !state->GetString("mode", &mode_string) ||
- !state->GetDouble("expiry", &expiry)) {
- continue;
- }
-
- // Don't fail if this key is not present.
- (void) state->GetDouble("dynamic_spki_hashes_expiry",
- &dynamic_spki_hashes_expiry);
-
- ListValue* pins_list = NULL;
- FingerprintVector preloaded_spki_hashes;
- if (state->GetList("preloaded_spki_hashes", &pins_list))
- SPKIHashesFromListValue(&preloaded_spki_hashes, *pins_list);
-
- FingerprintVector dynamic_spki_hashes;
- if (state->GetList("dynamic_spki_hashes", &pins_list))
- SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list);
-
- DomainState::Mode mode;
- if (mode_string == "strict") {
- mode = DomainState::MODE_STRICT;
- } else if (mode_string == "spdy-only") {
- mode = DomainState::MODE_SPDY_ONLY;
- } else if (mode_string == "pinning-only") {
- mode = DomainState::MODE_PINNING_ONLY;
- } else {
- LOG(WARNING) << "Unknown TransportSecurityState mode string found: "
- << mode_string;
- continue;
- }
-
- base::Time expiry_time = base::Time::FromDoubleT(expiry);
- base::Time dynamic_spki_hashes_expiry_time =
- base::Time::FromDoubleT(dynamic_spki_hashes_expiry);
- base::Time created_time;
- if (state->GetDouble("created", &created)) {
- created_time = base::Time::FromDoubleT(created);
- } else {
- // We're migrating an old entry with no creation date. Make sure we
- // write the new date back in a reasonable time frame.
- dirtied = true;
- created_time = base::Time::Now();
- }
-
- if (expiry_time <= current_time &&
- dynamic_spki_hashes_expiry_time <= current_time) {
- // Make sure we dirty the state if we drop an entry.
- dirtied = true;
- continue;
- }
-
- std::string hashed = ExternalStringToHashedDomain(*i);
- if (hashed.empty()) {
- dirtied = true;
- continue;
- }
-
- DomainState new_state;
- new_state.mode = mode;
- new_state.created = created_time;
- new_state.expiry = expiry_time;
- new_state.include_subdomains = include_subdomains;
- new_state.preloaded_spki_hashes = preloaded_spki_hashes;
- new_state.dynamic_spki_hashes = dynamic_spki_hashes;
- new_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time;
- (*out)[hashed] = new_state;
- }
-
- *dirty = dirtied;
- return true;
-}
-
-TransportSecurityState::~TransportSecurityState() {
-}
+TransportSecurityState::~TransportSecurityState() {}
void TransportSecurityState::DirtyNotify() {
DCHECK(CalledOnValidThread());
@@ -1122,11 +601,11 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
out->include_subdomains = entries[j].include_subdomains;
*ret = true;
if (!entries[j].https_required)
- out->mode = TransportSecurityState::DomainState::MODE_PINNING_ONLY;
+ out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT;
if (entries[j].pins.required_hashes) {
const char* const* hash = entries[j].pins.required_hashes;
while (*hash) {
- bool ok = AddHash(*hash, &out->preloaded_spki_hashes);
+ bool ok = AddHash(*hash, &out->static_spki_hashes);
DCHECK(ok) << " failed to parse " << *hash;
hash++;
}
@@ -1134,7 +613,7 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
if (entries[j].pins.excluded_hashes) {
const char* const* hash = entries[j].pins.excluded_hashes;
while (*hash) {
- bool ok = AddHash(*hash, &out->bad_preloaded_spki_hashes);
+ bool ok = AddHash(*hash, &out->bad_static_spki_hashes);
DCHECK(ok) << " failed to parse " << *hash;
hash++;
}
@@ -1177,7 +656,7 @@ static const struct HSTSPreload* GetHSTSPreload(
// static
bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host,
- bool sni_available) {
+ bool sni_enabled) {
std::string canonicalized_host = CanonicalizeHost(host);
const struct HSTSPreload* entry =
GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS);
@@ -1185,7 +664,7 @@ bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host,
if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts)
return true;
- if (sni_available) {
+ if (sni_enabled) {
entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS,
kNumPreloadedSNISTS);
if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts)
@@ -1215,16 +694,13 @@ void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) {
entry->second_level_domain_name, DOMAIN_NUM_EVENTS);
}
-// IsPreloadedSTS returns true if the canonicalized hostname should always be
-// considered to have STS enabled.
-bool TransportSecurityState::IsPreloadedSTS(
+bool TransportSecurityState::GetStaticDomainState(
const std::string& canonicalized_host,
- bool sni_available,
+ bool sni_enabled,
DomainState* out) {
DCHECK(CalledOnValidThread());
- out->preloaded = true;
- out->mode = DomainState::MODE_STRICT;
+ out->upgrade_mode = DomainState::MODE_FORCE_HTTPS;
out->include_subdomains = false;
for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
@@ -1235,7 +711,6 @@ bool TransportSecurityState::IsPreloadedSTS(
if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) {
*out = forced_hosts_[hashed_host];
out->domain = DNSDomainToString(host_sub_chunk);
- out->preloaded = true;
return true;
}
bool ret;
@@ -1243,7 +718,7 @@ bool TransportSecurityState::IsPreloadedSTS(
&ret)) {
return ret;
}
- if (sni_available &&
+ if (sni_enabled &&
HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i,
out, &ret)) {
return ret;
@@ -1253,6 +728,16 @@ bool TransportSecurityState::IsPreloadedSTS(
return false;
}
+void TransportSecurityState::AddOrUpdateEnabledHosts(std::string hashed_host,
+ const DomainState& state) {
+ enabled_hosts_[hashed_host] = state;
+}
+
+void TransportSecurityState::AddOrUpdateForcedHosts(std::string hashed_host,
+ const DomainState& state) {
+ forced_hosts_[hashed_host] = state;
+}
+
static std::string HashesToBase64String(
const FingerprintVector& hashes) {
std::vector<std::string> hashes_strs;
@@ -1269,33 +754,31 @@ static std::string HashesToBase64String(
}
TransportSecurityState::DomainState::DomainState()
- : mode(MODE_STRICT),
+ : upgrade_mode(MODE_FORCE_HTTPS),
created(base::Time::Now()),
- include_subdomains(false),
- preloaded(false) {
+ include_subdomains(false) {
}
TransportSecurityState::DomainState::~DomainState() {
}
bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted(
- const FingerprintVector& hashes) {
-
- if (HashesIntersect(bad_preloaded_spki_hashes, hashes)) {
+ const FingerprintVector& hashes) const {
+ if (HashesIntersect(bad_static_spki_hashes, hashes)) {
LOG(ERROR) << "Rejecting public key chain for domain " << domain
<< ". Validated chain: " << HashesToBase64String(hashes)
<< ", matches one or more bad hashes: "
- << HashesToBase64String(bad_preloaded_spki_hashes);
+ << HashesToBase64String(bad_static_spki_hashes);
return false;
}
- if (!(dynamic_spki_hashes.empty() && preloaded_spki_hashes.empty()) &&
+ if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) &&
!HashesIntersect(dynamic_spki_hashes, hashes) &&
- !HashesIntersect(preloaded_spki_hashes, hashes)) {
+ !HashesIntersect(static_spki_hashes, hashes)) {
LOG(ERROR) << "Rejecting public key chain for domain " << domain
<< ". Validated chain: " << HashesToBase64String(hashes)
<< ", expected: " << HashesToBase64String(dynamic_spki_hashes)
- << " or: " << HashesToBase64String(preloaded_spki_hashes);
+ << " or: " << HashesToBase64String(static_spki_hashes);
return false;
}
@@ -1303,20 +786,21 @@ bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted(
return true;
}
-bool TransportSecurityState::DomainState::IsMoreStrict(
- const TransportSecurityState::DomainState& other) {
- if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty())
- return false;
-
- if (!this->include_subdomains && other.include_subdomains)
- return false;
+bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const {
+ return upgrade_mode == MODE_FORCE_HTTPS;
+}
+bool TransportSecurityState::DomainState::Equals(
+ const DomainState& other) const {
+ // TODO(palmer): Implement this
+ (void) other;
return true;
}
-bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS()
- const {
- return mode == MODE_STRICT;
+bool TransportSecurityState::DomainState::HasPins() const {
+ return static_spki_hashes.size() > 0 ||
+ bad_static_spki_hashes.size() > 0 ||
+ dynamic_spki_hashes.size() > 0;
}
} // namespace
diff --git a/net/base/transport_security_state.h b/net/base/transport_security_state.h
index d2c8e2b..b013170 100644
--- a/net/base/transport_security_state.h
+++ b/net/base/transport_security_state.h
@@ -8,6 +8,7 @@
#include <map>
#include <string>
+#include <utility>
#include <vector>
#include "base/basictypes.h"
@@ -22,255 +23,271 @@ namespace net {
class SSLInfo;
-typedef std::vector<SHA1Fingerprint> FingerprintVector;
-
-// TransportSecurityState
+// Tracks which hosts have enabled strict transport security and/or public
+// key pins.
+//
+// This object manages the in-memory store. Register a Delegate with
+// |SetDelegate| to persist the state to disk.
//
-// Tracks which hosts have enabled *-Transport-Security. This object manages
-// the in-memory store. A separate object must register itself with this object
-// in order to persist the state to disk.
+// HTTP strict transport security (HSTS) is defined in
+// http://tools.ietf.org/html/ietf-websec-strict-transport-sec, and
+// HTTP-based dynamic public key pinning (HPKP) is defined in
+// http://tools.ietf.org/html/ietf-websec-key-pinning.
class NET_EXPORT TransportSecurityState
: NON_EXPORTED_BASE(public base::NonThreadSafe) {
public:
- // If non-empty, |hsts_hosts| is a JSON-formatted string to treat as if it
- // were a built-in entry (same format as persisted metadata in the
- // TransportSecurityState file).
- explicit TransportSecurityState(const std::string& hsts_hosts);
+ class Delegate {
+ public:
+ // This function may not block and may be called with internal locks held.
+ // Thus it must not reenter the TransportSecurityState object.
+ virtual void StateIsDirty(TransportSecurityState* state) = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ TransportSecurityState();
~TransportSecurityState();
- // A DomainState is the information that we persist about a given domain.
- struct NET_EXPORT DomainState {
- enum Mode {
- // Strict mode implies:
- // * We generate internal redirects from HTTP -> HTTPS.
- // * Certificate issues are fatal.
- MODE_STRICT = 0,
- // This used to be opportunistic HTTPS, but we removed support.
- MODE_OPPORTUNISTIC_REMOVED = 1,
- // SPDY_ONLY (aka X-Bodge-Transport-Security) is a hopefully temporary
- // measure. It implies:
- // * We'll request HTTP URLs over HTTPS iff we have SPDY support.
- // * Certificate issues are fatal.
- MODE_SPDY_ONLY = 2,
- // Pinning means there are no HTTP -> HTTPS redirects, however certificate
- // issues are still fatal and there may be public key pins.
- MODE_PINNING_ONLY = 3,
+ // A DomainState describes the transport security state (required upgrade
+ // to HTTPS, and/or any public key pins).
+ class NET_EXPORT DomainState {
+ public:
+ enum UpgradeMode {
+ // These numbers must match those in hsts_view.js, function modeToString.
+ MODE_FORCE_HTTPS = 0,
+ MODE_DEFAULT = 1,
};
DomainState();
~DomainState();
+ // Parses |value| as a Public-Key-Pins header. If successful, returns true
+ // and updates the |dynamic_spki_hashes| and |dynamic_spki_hashes_expiry|
+ // fields; otherwise, returns false without updating any fields.
+ // Interprets the max-age directive relative to |now|.
+ bool ParsePinsHeader(const base::Time& now,
+ const std::string& value,
+ const SSLInfo& ssl_info);
+
+ // Parses |value| as a Strict-Transport-Security header. If successful,
+ // returns true and updates the |upgrade_mode|, |upgrade_expiry| and
+ // |include_subdomains| fields; otherwise, returns false without updating
+ // any fields. Interprets the max-age directive relative to |now|.
+ bool ParseSTSHeader(const base::Time& now, const std::string& value);
+
// Takes a set of SubjectPublicKeyInfo |hashes| and returns true if:
- // 1) |bad_preloaded_spki_hashes| does not intersect |hashes|; AND
- // 2) Both |preloaded_spki_hashes| and |dynamic_spki_hashes| are empty
+ // 1) |bad_static_spki_hashes| does not intersect |hashes|; AND
+ // 2) Both |static_spki_hashes| and |dynamic_spki_hashes| are empty
// or at least one of them intersects |hashes|.
//
- // |{dynamic,preloaded}_spki_hashes| contain trustworthy public key
- // hashes, any one of which is sufficient to validate the certificate
- // chain in question. The public keys could be of a root CA, intermediate
- // CA, or leaf certificate, depending on the security vs. disaster
- // recovery tradeoff selected. (Pinning only to leaf certifiates increases
+ // |{dynamic,static}_spki_hashes| contain trustworthy public key hashes,
+ // any one of which is sufficient to validate the certificate chain in
+ // question. The public keys could be of a root CA, intermediate CA, or
+ // leaf certificate, depending on the security vs. disaster recovery
+ // tradeoff selected. (Pinning only to leaf certifiates increases
// security because you no longer trust any CAs, but it hampers disaster
// recovery because you can't just get a new certificate signed by the
// CA.)
//
- // |bad_preloaded_spki_hashes| contains public keys that we don't want to
+ // |bad_static_spki_hashes| contains public keys that we don't want to
// trust.
- bool IsChainOfPublicKeysPermitted(const FingerprintVector& hashes);
+ bool IsChainOfPublicKeysPermitted(const FingerprintVector& hashes) const;
- // Returns true if |this| describes a more strict policy than |other|.
- // Used to see if a dynamic DomainState should override a preloaded one.
- bool IsMoreStrict(const DomainState& other);
+ // Returns true if any of the FingerprintVectors |static_spki_hashes|,
+ // |bad_static_spki_hashes|, or |dynamic_spki_hashes| contains any
+ // items.
+ bool HasPins() const;
// ShouldRedirectHTTPToHTTPS returns true iff, given the |mode| of this
// DomainState, HTTP requests should be internally redirected to HTTPS.
bool ShouldRedirectHTTPToHTTPS() const;
- Mode mode;
- base::Time created; // when this host entry was first created
- base::Time expiry; // the absolute time (UTC) when this record expires
- bool include_subdomains; // subdomains included?
+ bool Equals(const DomainState& other) const;
+
+ UpgradeMode upgrade_mode;
+
+ // The absolute time (UTC) when this DomainState was first created.
+ //
+ // Static entries do not have a created time.
+ base::Time created;
+
+ // The absolute time (UTC) when the |upgrade_mode|, if set to
+ // UPGRADE_ALWAYS, downgrades to UPGRADE_NEVER.
+ base::Time upgrade_expiry;
- // Optional; hashes of preloaded "pinned" SubjectPublicKeyInfos. Unless
- // both are empty, at least one of |preloaded_spki_hashes| and
+ // Are subdomains subject to this DomainState?
+ //
+ // TODO(palmer): Decide if we should have separate |pin_subdomains| and
+ // |upgrade_subdomains|. Alternately, and perhaps better, is to separate
+ // DomainState into UpgradeState and PinState (requiring also changing the
+ // serialization format?).
+ bool include_subdomains;
+
+ // Optional; hashes of static pinned SubjectPublicKeyInfos. Unless both
+ // are empty, at least one of |static_spki_hashes| and
// |dynamic_spki_hashes| MUST intersect with the set of SPKIs in the TLS
// server's certificate chain.
//
- // |dynamic_spki_hashes| take precedence over |preloaded_spki_hashes|.
- // That is, when performing pin validation, first check dynamic and then
- // check preloaded.
- FingerprintVector preloaded_spki_hashes;
+ // |dynamic_spki_hashes| take precedence over |static_spki_hashes|.
+ // That is, |IsChainOfPublicKeysPermitted| first checks dynamic pins and
+ // then checks static pins.
+ FingerprintVector static_spki_hashes;
- // Optional; hashes of dynamically pinned SubjectPublicKeyInfos. (They
- // could be set e.g. by an HTTP header or by a superfluous certificate.)
+ // Optional; hashes of dynamically pinned SubjectPublicKeyInfos.
FingerprintVector dynamic_spki_hashes;
// The absolute time (UTC) when the |dynamic_spki_hashes| expire.
base::Time dynamic_spki_hashes_expiry;
- // The max-age directive of the Public-Key-Pins header as parsed. Do not
- // persist this; it is only for testing. TODO(palmer): Therefore, get rid
- // of it and find a better way to test.
- int max_age;
-
- // Optional; hashes of preloaded known-bad SubjectPublicKeyInfos which
+ // Optional; hashes of static known-bad SubjectPublicKeyInfos which
// MUST NOT intersect with the set of SPKIs in the TLS server's
// certificate chain.
- FingerprintVector bad_preloaded_spki_hashes;
+ FingerprintVector bad_static_spki_hashes;
- // The following members are not valid when stored in |enabled_hosts_|.
- bool preloaded; // is this a preloaded entry?
- std::string domain; // the domain which matched
+ // The following members are not valid when stored in |enabled_hosts_|:
+
+ // The domain which matched during a search for this DomainState entry.
+ // Updated by |GetDomainState| and |GetStaticDomainState|.
+ std::string domain;
};
- class Delegate {
+ class Iterator {
public:
- // This function may not block and may be called with internal locks held.
- // Thus it must not reenter the TransportSecurityState object.
- virtual void StateIsDirty(TransportSecurityState* state) = 0;
-
- protected:
- virtual ~Delegate() {}
+ explicit Iterator(const TransportSecurityState& state)
+ : iterator_(state.enabled_hosts_.begin()),
+ end_(state.enabled_hosts_.end()) {
+ }
+ ~Iterator() {}
+
+ bool HasNext() const { return iterator_ != end_; }
+ void Advance() { ++iterator_; }
+ const std::string& hostname() const { return iterator_->first; }
+ const DomainState& domain_state() const { return iterator_->second; }
+
+ private:
+ std::map<std::string, DomainState>::const_iterator iterator_;
+ std::map<std::string, DomainState>::const_iterator end_;
};
+ // Assign a |Delegate| for persisting the transport security state. If
+ // |NULL|, state will not be persisted. Caller owns |delegate|.
void SetDelegate(Delegate* delegate);
- // Enable TransportSecurity for |host|.
+ // Enable TransportSecurity for |host|. |state| supercedes any previous
+ // state for the |host|, including static entries.
+ //
+ // The new state for |host| is persisted using the Delegate (if any).
void EnableHost(const std::string& host, const DomainState& state);
- // Delete any entry for |host|. If |host| doesn't have an exact entry then no
- // action is taken. Returns true iff an entry was deleted.
+ // Delete any entry for |host|. If |host| doesn't have an exact entry then
+ // no action is taken. Does not delete static entries. Returns true iff an
+ // entry was deleted.
+ //
+ // The new state for |host| is persisted using the Delegate (if any).
bool DeleteHost(const std::string& host);
- // Returns true if |host| has TransportSecurity enabled. Before operating
- // on this result, consult |result->mode|, as the expected behavior of
- // TransportSecurity can significantly differ based on mode.
+ // Deletes all records created since a given time.
+ void DeleteSince(const base::Time& time);
+
+ // Returns true and updates |*result| iff there is a DomainState for
+ // |host|.
+ //
+ // If |sni_enabled| is true, searches the static pins defined for
+ // SNI-using hosts as well as the rest of the pins.
//
- // If |sni_available| is true, searches the preloads defined for SNI-using
- // hosts as well as the usual preload list.
+ // If |host| matches both an exact entry and is a subdomain of another
+ // entry, the exact match determines the return value.
//
- // Note that |*result| is always overwritten on every call.
- // TODO(palmer): Only update |*result| on success.
- bool GetDomainState(DomainState* result,
- const std::string& host,
- bool sni_available);
-
- // Returns true if there are any certificates pinned for |host|.
- // If so, updates the |preloaded_spki_hashes|, |dynamic_spki_hashes|, and
- // |bad_preloaded_spki_hashes| fields of |*result| with the pins.
+ // Note that this method is not const because it opportunistically removes
+ // entries that have expired.
+ bool GetDomainState(const std::string& host,
+ bool sni_enabled,
+ DomainState* result);
+
+ // Returns true and updates |*result| iff there is a static DomainState for
+ // |host|.
//
- // Note that |*result| is always overwritten on every call, regardless of
- // whether or not pins are enabled.
+ // |GetStaticDomainState| is identical to |GetDomainState| except that it
+ // searches only the statically-defined transport security state, ignoring
+ // all dynamically-added DomainStates.
//
- // If |sni_available| is true, searches the preloads defined for SNI-using
- // hosts as well as the usual preload list.
+ // If |sni_enabled| is true, searches the static pins defined for
+ // SNI-using hosts as well as the rest of the pins.
//
- // TODO(palmer): Only update |*result| if pins exist.
- bool HasPinsForHost(DomainState* result,
- const std::string& host,
- bool sni_available);
-
- // Returns true and updates |*result| if there is any |DomainState|
- // metadata for |host| in the local TransportSecurityState database;
- // returns false otherwise. TODO(palmer): Unlike the other
- // TransportSecurityState lookup functions in this class (e.g
- // |HasPinsForHost|, |GetDomainState|), |*result| is updated iff metadata
- // is found. The other functions are buggy and will be fixed to behave
- // like this one.
+ // If |host| matches both an exact entry and is a subdomain of another
+ // entry, the exact match determines the return value.
//
- // If |sni_available| is true, searches the preloads defined for SNI-using
- // hosts as well as the usual preload list.
- bool HasMetadata(DomainState* result,
- const std::string& host,
- bool sni_available);
-
- // Returns true if we have a preloaded certificate pin for the |host| and if
- // its set of required certificates is the set we expect for Google
+ // Note that this method is not const because it opportunistically removes
+ // entries that have expired.
+ bool GetStaticDomainState(const std::string& host,
+ bool sni_enabled,
+ DomainState* result);
+
+ // Removed all DomainState records.
+ void Clear() { enabled_hosts_.clear(); }
+
+ // Inserts |state| into |enabled_hosts_| under the key |hashed_host|.
+ // |hashed_host| is already in the internal representation
+ // HashHost(CanonicalizeHost(host)); thus, most callers will use
+ // |EnableHost|.
+ void AddOrUpdateEnabledHosts(std::string hashed_host,
+ const DomainState& state);
+
+ // Inserts |state| into |forced_hosts_| under the key |hashed_host|.
+ // |hashed_host| is already in the internal representation
+ // HashHost(CanonicalizeHost(host)); thus, most callers will use
+ // |EnableHost|.
+ void AddOrUpdateForcedHosts(std::string hashed_host,
+ const DomainState& state);
+
+ // Returns true iff we have any static public key pins for the |host| and
+ // iff its set of required pins is the set we expect for Google
// properties.
//
- // If |sni_available| is true, searches the preloads defined for SNI-using
- // hosts as well as the usual preload list.
+ // If |sni_enabled| is true, searches the static pins defined for
+ // SNI-using hosts as well as the rest of the pins.
//
- // Note that like HasMetadata, if |host| matches both an exact entry and is a
- // subdomain of another entry, the exact match determines the return value.
+ // If |host| matches both an exact entry and is a subdomain of another
+ // entry, the exact match determines the return value.
static bool IsGooglePinnedProperty(const std::string& host,
- bool sni_available);
-
- // Reports UMA statistics upon public key pin failure. Reports only down to
- // the second-level domain of |host| (e.g. google.com if |host| is
- // mail.google.com), and only if |host| is a preloaded STS host.
- static void ReportUMAOnPinFailure(const std::string& host);
-
- // Parses |cert|'s Subject Public Key Info structure, hashes it, and writes
- // the hash into |spki_hash|. Returns true on parse success, false on
- // failure.
- static bool GetPublicKeyHash(const X509Certificate& cert,
- SHA1Fingerprint* spki_hash);
-
- // Decodes a pin string |value| (e.g. "sha1/hvfkN/qlp/zhXR3cuerq6jd2Z7g=")
- // and populates |out|.
- static bool ParsePin(const std::string& value, SHA1Fingerprint* out);
-
- // Deletes all records created since a given time.
- void DeleteSince(const base::Time& time);
+ bool sni_enabled);
- // Parses |value| as a Public-Key-Pins header. If successful, returns |true|
- // and updates the |dynamic_spki_hashes| and |dynamic_spki_hashes_expiry|
- // fields of |*state|; otherwise, returns |false| without updating |*state|.
- static bool ParsePinsHeader(const std::string& value,
- const SSLInfo& ssl_info,
- DomainState* state);
-
- // Returns |true| if |value| parses as a valid *-Transport-Security
- // header value. The values of max-age and and includeSubDomains are
- // returned in |max_age| and |include_subdomains|, respectively. The out
- // parameters are not modified if the function returns |false|.
- static bool ParseHeader(const std::string& value,
- int* max_age,
- bool* include_subdomains);
-
- // ParseSidePin attempts to parse a side pin from |side_info| which signs the
- // SubjectPublicKeyInfo in |leaf_spki|. A side pin is a way for a site to
- // sign their public key with a key that is offline but still controlled by
- // them. If successful, the hash of the public key used to sign |leaf_spki|
- // is put into |out_pub_key_hash|.
- static bool ParseSidePin(const base::StringPiece& leaf_spki,
- const base::StringPiece& side_info,
- FingerprintVector* out_pub_key_hash);
-
- bool Serialise(std::string* output);
- // Existing non-preloaded entries are cleared and repopulated from the
- // passed JSON string.
- bool LoadEntries(const std::string& state, bool* dirty);
+ // Decodes a pin string |value| (e.g. "sha1/hvfkN/qlp/zhXR3cuerq6jd2Z7g=").
+ // If parsing succeeded, updates |*out| and returns true; otherwise returns
+ // false without updating |*out|.
+ static bool ParsePin(const std::string& value, Fingerprint* out);
// The maximum number of seconds for which we'll cache an HSTS request.
static const long int kMaxHSTSAgeSecs;
- private:
- FRIEND_TEST_ALL_PREFIXES(TransportSecurityStateTest, IsPreloaded);
+ // Converts |hostname| from dotted form ("www.google.com") to the form
+ // used in DNS: "\x03www\x06google\x03com", lowercases that, and returns
+ // the result.
+ static std::string CanonicalizeHost(const std::string& hostname);
+
+ // Send an UMA report on pin validation failure, if the host is in a
+ // statically-defined list of domains.
+ //
+ // TODO(palmer): This doesn't really belong here, and should be moved into
+ // the exactly one call site. This requires unifying |struct HSTSPreload|
+ // (an implementation detail of this class) with a more generic
+ // representation of first-class DomainStates, and exposing the preloads
+ // to the caller with |GetStaticDomainState|.
+ static void ReportUMAOnPinFailure(const std::string& host);
- // If we have a callback configured, call it to let our serialiser know that
- // our state is dirty.
+ private:
+ // If a Delegate is present, notify it that the internal state has
+ // changed.
void DirtyNotify();
- bool IsPreloadedSTS(const std::string& canonicalized_host,
- bool sni_available,
- DomainState* out);
-
- static std::string CanonicalizeHost(const std::string& host);
- static bool Deserialise(const std::string& state,
- bool* dirty,
- std::map<std::string, DomainState>* out);
-
- // The set of hosts that have enabled TransportSecurity. The keys here
- // are SHA256(DNSForm(domain)) where DNSForm converts from dotted form
- // ('www.google.com') to the form used in DNS: "\x03www\x06google\x03com"
+
+ // The set of hosts that have enabled TransportSecurity.
std::map<std::string, DomainState> enabled_hosts_;
- // These hosts are extra rules to treat as built-in, passed in the
- // constructor (typically originating from the command line).
+ // Extra entries, provided by the user at run-time, to treat as if they
+ // were static.
std::map<std::string, DomainState> forced_hosts_;
- // Our delegate who gets notified when we are dirtied, or NULL.
Delegate* delegate_;
DISALLOW_COPY_AND_ASSIGN(TransportSecurityState);
diff --git a/net/base/transport_security_state_unittest.cc b/net/base/transport_security_state_unittest.cc
index 9956c27..df3e528 100644
--- a/net/base/transport_security_state_unittest.cc
+++ b/net/base/transport_security_state_unittest.cc
@@ -43,84 +43,72 @@ class TransportSecurityStateTest : public testing::Test {
};
TEST_F(TransportSecurityStateTest, BogusHeaders) {
- int max_age = 42;
- bool include_subdomains = false;
-
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " ", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "abc", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " abc", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " abc ", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age ", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age=", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age =", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age= ", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age = ", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age = xy", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- " max-age = 3488a923", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488a923 ", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-ag=3488923", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-aged=3488923", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age==3488923", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "amax-age=3488923", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=-3488923", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923;", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923 e", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923 includesubdomain", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923includesubdomains", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923=includesubdomains", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923 includesubdomainx", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923 includesubdomain=", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923 includesubdomain=true", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923 includesubdomainsx", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=3488923 includesubdomains x", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=34889.23 includesubdomains", &max_age, &include_subdomains));
- EXPECT_FALSE(TransportSecurityState::ParseHeader(
- "max-age=34889 includesubdomains", &max_age, &include_subdomains));
-
- EXPECT_EQ(max_age, 42);
- EXPECT_FALSE(include_subdomains);
+ TransportSecurityState::DomainState state;
+ base::Time now = base::Time::Now();
+
+ EXPECT_FALSE(state.ParseSTSHeader(now, ""));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " "));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "abc"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " abc"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " abc "));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age "));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age="));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age="));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age ="));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age= "));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age = "));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age = xy"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, " max-age = 3488a923"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488a923 "));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-ag=3488923"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-aged=3488923"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age==3488923"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "amax-age=3488923"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=-3488923"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488923;"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488923 e"));
+ EXPECT_FALSE(state.ParseSTSHeader(
+ now, "max-age=3488923 includesubdomain"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488923includesubdomains"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488923=includesubdomains"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488923 includesubdomainx"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488923 includesubdomain="));
+ EXPECT_FALSE(state.ParseSTSHeader(
+ now, "max-age=3488923 includesubdomain=true"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=3488923 includesubdomainsx"));
+ EXPECT_FALSE(state.ParseSTSHeader(
+ now, "max-age=3488923 includesubdomains x"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=34889.23 includesubdomains"));
+ EXPECT_FALSE(state.ParseSTSHeader(now, "max-age=34889 includesubdomains"));
+
+ // Check that |state| was not updated by expecting the default
+ // values for its predictable fields.
+ EXPECT_EQ(state.upgrade_mode,
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
+ EXPECT_FALSE(state.include_subdomains);
+}
+
+static bool GetPublicKeyHash(const net::X509Certificate::OSCertHandle& cert,
+ SHA1Fingerprint* fingerprint) {
+ std::string der_bytes;
+ if (!net::X509Certificate::GetDEREncoded(cert, &der_bytes))
+ return false;
+
+ base::StringPiece spki;
+ if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki))
+ return false;
+
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()),
+ spki.size(), fingerprint->data);
+ return true;
}
static std::string GetPinFromCert(X509Certificate* cert) {
SHA1Fingerprint spki_hash;
- if (!TransportSecurityState::GetPublicKeyHash(*cert, &spki_hash))
- return "";
+ EXPECT_TRUE(GetPublicKeyHash(cert->os_cert_handle(), &spki_hash));
+
std::string base64;
base::Base64Encode(base::StringPiece(reinterpret_cast<char*>(spki_hash.data),
sizeof(spki_hash.data)),
@@ -130,142 +118,132 @@ static std::string GetPinFromCert(X509Certificate* cert) {
TEST_F(TransportSecurityStateTest, BogusPinsHeaders) {
TransportSecurityState::DomainState state;
- state.max_age = 42;
SSLInfo ssl_info;
ssl_info.cert =
ImportCertFromFile(GetTestCertsDirectory(), "test_mail_google_com.pem");
std::string good_pin = GetPinFromCert(ssl_info.cert);
+ base::Time now = base::Time::Now();
// The backup pin is fake --- it just has to not be in the chain.
std::string backup_pin = "pin-sha1=" +
HttpUtil::Quote("6dcfXufJLW3J6S/9rRe4vUlBj5g=");
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " ", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "abc", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " abc", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " abc ", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age ", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age=", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age=", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age =", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age= ", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age = ", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age = xy", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- " max-age = 3488a923", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age=3488a923 ", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_FALSE(state.ParsePinsHeader(now, "", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " ", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "abc", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " abc", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " abc ", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " max-age", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " max-age ", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age=", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " max-age=", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " max-age =", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " max-age= ", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " max-age = ", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, " max-age = xy", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(
+ now,
+ " max-age = 3488a923",
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age=3488a923 ", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now,
"max-ag=3488923pins=" + good_pin + "," + backup_pin,
- ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-aged=3488923" + backup_pin,
- ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-aged=3488923; " + backup_pin,
- ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-aged=3488923" + backup_pin,
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-aged=3488923; " + backup_pin,
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now,
"max-aged=3488923; " + backup_pin + ";" + backup_pin,
- ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now,
"max-aged=3488923; " + good_pin + ";" + good_pin,
- ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-aged=3488923; " + good_pin,
- ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age==3488923", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "amax-age=3488923", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age=-3488923", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age=3488923;", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age=3488923 e", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age=3488923 includesubdomain", ssl_info, &state));
- EXPECT_FALSE(TransportSecurityState::ParsePinsHeader(
- "max-age=34889.23", ssl_info, &state));
-
- EXPECT_EQ(state.max_age, 42);
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-aged=3488923; " + good_pin,
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age==3488923", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "amax-age=3488923", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age=-3488923", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age=3488923;", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age=3488923 e", ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(
+ now,
+ "max-age=3488923 includesubdomain",
+ ssl_info));
+ EXPECT_FALSE(state.ParsePinsHeader(now, "max-age=34889.23", ssl_info));
+
+ // Check that |state| was not updated by expecting the default
+ // values for its predictable fields.
+ EXPECT_EQ(state.upgrade_mode,
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
+ EXPECT_FALSE(state.include_subdomains);
}
-TEST_F(TransportSecurityStateTest, ValidHeaders) {
- int max_age = 42;
- bool include_subdomains = true;
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- "max-age=243", &max_age, &include_subdomains));
- EXPECT_EQ(max_age, 243);
- EXPECT_FALSE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- " Max-agE = 567", &max_age, &include_subdomains));
- EXPECT_EQ(max_age, 567);
- EXPECT_FALSE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- " mAx-aGe = 890 ", &max_age, &include_subdomains));
- EXPECT_EQ(max_age, 890);
- EXPECT_FALSE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- "max-age=123;incLudesUbdOmains", &max_age, &include_subdomains));
- EXPECT_EQ(max_age, 123);
- EXPECT_TRUE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- "max-age=394082; incLudesUbdOmains", &max_age, &include_subdomains));
- EXPECT_EQ(max_age, 394082);
- EXPECT_TRUE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- "max-age=39408299 ;incLudesUbdOmains", &max_age, &include_subdomains));
- EXPECT_EQ(max_age,
- std::min(TransportSecurityState::kMaxHSTSAgeSecs, 39408299l));
- EXPECT_TRUE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- "max-age=394082038 ; incLudesUbdOmains", &max_age,
- &include_subdomains));
- EXPECT_EQ(max_age,
- std::min(TransportSecurityState::kMaxHSTSAgeSecs, 394082038l));
- EXPECT_TRUE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
- " max-age=0 ; incLudesUbdOmains ", &max_age, &include_subdomains));
- EXPECT_EQ(max_age, 0);
- EXPECT_TRUE(include_subdomains);
-
- EXPECT_TRUE(TransportSecurityState::ParseHeader(
+TEST_F(TransportSecurityStateTest, ValidSTSHeaders) {
+ TransportSecurityState::DomainState state;
+ base::Time expiry;
+ base::Time now = base::Time::Now();
+
+ EXPECT_TRUE(state.ParseSTSHeader(now, "max-age=243"));
+ expiry = now + base::TimeDelta::FromSeconds(243);
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_FALSE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(now, " Max-agE = 567"));
+ expiry = now + base::TimeDelta::FromSeconds(567);
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_FALSE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(now, " mAx-aGe = 890 "));
+ expiry = now + base::TimeDelta::FromSeconds(890);
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_FALSE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(now, "max-age=123;incLudesUbdOmains"));
+ expiry = now + base::TimeDelta::FromSeconds(123);
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_TRUE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(now, "max-age=394082; incLudesUbdOmains"));
+ expiry = now + base::TimeDelta::FromSeconds(394082);
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_TRUE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(
+ now, "max-age=39408299 ;incLudesUbdOmains"));
+ expiry = now + base::TimeDelta::FromSeconds(
+ std::min(TransportSecurityState::kMaxHSTSAgeSecs, 39408299l));
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_TRUE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(
+ now, "max-age=394082038 ; incLudesUbdOmains"));
+ expiry = now + base::TimeDelta::FromSeconds(
+ std::min(TransportSecurityState::kMaxHSTSAgeSecs, 394082038l));
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_TRUE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(
+ now, " max-age=0 ; incLudesUbdOmains "));
+ expiry = now + base::TimeDelta::FromSeconds(0);
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_TRUE(state.include_subdomains);
+
+ EXPECT_TRUE(state.ParseSTSHeader(
+ now,
" max-age=999999999999999999999999999999999999999999999 ;"
- " incLudesUbdOmains ",
- &max_age, &include_subdomains));
- EXPECT_EQ(max_age, TransportSecurityState::kMaxHSTSAgeSecs);
- EXPECT_TRUE(include_subdomains);
+ " incLudesUbdOmains "));
+ expiry = now + base::TimeDelta::FromSeconds(
+ TransportSecurityState::kMaxHSTSAgeSecs);
+ EXPECT_EQ(expiry, state.upgrade_expiry);
+ EXPECT_TRUE(state.include_subdomains);
}
TEST_F(TransportSecurityStateTest, ValidPinsHeaders) {
TransportSecurityState::DomainState state;
- state.max_age = 42;
+ base::Time expiry;
+ base::Time now = base::Time::Now();
// Set up a realistic SSLInfo with a realistic cert chain.
FilePath certs_dir = GetTestCertsDirectory();
@@ -309,211 +287,165 @@ TEST_F(TransportSecurityStateTest, ValidPinsHeaders) {
std::string backup_pin = "pin-sha1=" +
HttpUtil::Quote("6dcfXufJLW3J6S/9rRe4vUlBj5g=");
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
"max-age=243; " + good_pin + ";" + backup_pin,
- ssl_info, &state));
- EXPECT_EQ(state.max_age, 243);
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(243);
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
" " + good_pin + "; " + backup_pin + " ; Max-agE = 567",
- ssl_info, &state));
- EXPECT_EQ(state.max_age, 567);
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(567);
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
good_pin + ";" + backup_pin + " ; mAx-aGe = 890 ",
- ssl_info, &state));
- EXPECT_EQ(state.max_age, 890);
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(890);
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
good_pin + ";" + backup_pin + "; max-age=123;IGNORED;",
- ssl_info, &state));
- EXPECT_EQ(state.max_age, 123);
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(123);
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
"max-age=394082;" + backup_pin + ";" + good_pin + "; ",
- ssl_info, &state));
- EXPECT_EQ(state.max_age, 394082);
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(394082);
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
"max-age=39408299 ;" + backup_pin + ";" + good_pin + "; ",
- ssl_info, &state));
- EXPECT_EQ(state.max_age,
- std::min(TransportSecurityState::kMaxHSTSAgeSecs, 39408299l));
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(
+ std::min(TransportSecurityState::kMaxHSTSAgeSecs, 39408299l));
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
"max-age=39408038 ; cybers=39408038 ; " +
good_pin + ";" + backup_pin + "; ",
- ssl_info, &state));
- EXPECT_EQ(state.max_age,
- std::min(TransportSecurityState::kMaxHSTSAgeSecs, 394082038l));
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(
+ std::min(TransportSecurityState::kMaxHSTSAgeSecs, 394082038l));
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
" max-age=0 ; " + good_pin + ";" + backup_pin,
- ssl_info, &state));
- EXPECT_EQ(state.max_age, 0);
+ ssl_info));
+ expiry = now + base::TimeDelta::FromSeconds(0);
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
- EXPECT_TRUE(TransportSecurityState::ParsePinsHeader(
+ EXPECT_TRUE(state.ParsePinsHeader(
+ now,
" max-age=999999999999999999999999999999999999999999999 ; " +
backup_pin + ";" + good_pin + "; ",
- ssl_info, &state));
- EXPECT_EQ(state.max_age, TransportSecurityState::kMaxHSTSAgeSecs);
+ ssl_info));
+ expiry = now +
+ base::TimeDelta::FromSeconds(TransportSecurityState::kMaxHSTSAgeSecs);
+ EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry);
}
TEST_F(TransportSecurityStateTest, SimpleMatches) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState(&domain_state, "yahoo.com", true));
- domain_state.expiry = expiry;
+ EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ domain_state.upgrade_expiry = expiry;
state.EnableHost("yahoo.com", domain_state);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "yahoo.com", true));
+ EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
}
TEST_F(TransportSecurityStateTest, MatchesCase1) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState(&domain_state, "yahoo.com", true));
- domain_state.expiry = expiry;
+ EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ domain_state.upgrade_expiry = expiry;
state.EnableHost("YAhoo.coM", domain_state);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "yahoo.com", true));
+ EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
}
TEST_F(TransportSecurityStateTest, MatchesCase2) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState(&domain_state, "YAhoo.coM", true));
- domain_state.expiry = expiry;
+ EXPECT_FALSE(state.GetDomainState("YAhoo.coM", true, &domain_state));
+ domain_state.upgrade_expiry = expiry;
state.EnableHost("yahoo.com", domain_state);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "YAhoo.coM", true));
+ EXPECT_TRUE(state.GetDomainState("YAhoo.coM", true, &domain_state));
}
TEST_F(TransportSecurityStateTest, SubdomainMatches) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState(&domain_state, "yahoo.com", true));
- domain_state.expiry = expiry;
+ EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ domain_state.upgrade_expiry = expiry;
domain_state.include_subdomains = true;
state.EnableHost("yahoo.com", domain_state);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "yahoo.com", true));
- EXPECT_TRUE(state.GetDomainState(&domain_state, "foo.yahoo.com", true));
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "foo.bar.yahoo.com",
- true));
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "foo.bar.baz.yahoo.com",
- true));
- EXPECT_FALSE(state.GetDomainState(&domain_state, "com", true));
-}
-
-TEST_F(TransportSecurityStateTest, Serialise1) {
- TransportSecurityState state("");
- std::string output;
- bool dirty;
- state.Serialise(&output);
- EXPECT_TRUE(state.LoadEntries(output, &dirty));
- EXPECT_FALSE(dirty);
-}
-
-TEST_F(TransportSecurityStateTest, Serialise2) {
- TransportSecurityState state("");
- TransportSecurityState::DomainState domain_state;
- const base::Time current_time(base::Time::Now());
- const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
-
- EXPECT_FALSE(state.GetDomainState(&domain_state, "yahoo.com", true));
- domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
- domain_state.expiry = expiry;
- domain_state.include_subdomains = true;
- state.EnableHost("yahoo.com", domain_state);
-
- std::string output;
- bool dirty;
- state.Serialise(&output);
- EXPECT_TRUE(state.LoadEntries(output, &dirty));
-
- EXPECT_TRUE(state.GetDomainState(&domain_state, "yahoo.com", true));
- EXPECT_EQ(domain_state.mode,
- TransportSecurityState::DomainState::MODE_STRICT);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "foo.yahoo.com", true));
- EXPECT_EQ(domain_state.mode,
- TransportSecurityState::DomainState::MODE_STRICT);
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "foo.bar.yahoo.com",
- true));
- EXPECT_EQ(domain_state.mode,
- TransportSecurityState::DomainState::MODE_STRICT);
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "foo.bar.baz.yahoo.com",
- true));
- EXPECT_EQ(domain_state.mode,
- TransportSecurityState::DomainState::MODE_STRICT);
- EXPECT_FALSE(state.GetDomainState(&domain_state, "com", true));
+ EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDomainState("foo.yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDomainState("foo.bar.yahoo.com", true, &domain_state));
+ EXPECT_TRUE(state.GetDomainState("foo.bar.baz.yahoo.com", true,
+ &domain_state));
+ EXPECT_FALSE(state.GetDomainState("com", true, &domain_state));
}
TEST_F(TransportSecurityStateTest, DeleteSince) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
const base::Time older = current_time - base::TimeDelta::FromSeconds(1000);
- EXPECT_FALSE(state.GetDomainState(&domain_state, "yahoo.com", true));
- domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
- domain_state.expiry = expiry;
+ EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
+ domain_state.upgrade_mode =
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
+ domain_state.upgrade_expiry = expiry;
state.EnableHost("yahoo.com", domain_state);
state.DeleteSince(expiry);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "yahoo.com", true));
+ EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
state.DeleteSince(older);
- EXPECT_FALSE(state.GetDomainState(&domain_state, "yahoo.com", true));
+ EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
}
TEST_F(TransportSecurityStateTest, DeleteHost) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
- domain_state.expiry = expiry;
+ domain_state.upgrade_mode =
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
+ domain_state.upgrade_expiry = expiry;
state.EnableHost("yahoo.com", domain_state);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "yahoo.com", true));
- EXPECT_FALSE(state.GetDomainState(&domain_state, "example.com", true));
+ EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state));
+ EXPECT_FALSE(state.GetDomainState("example.com", true, &domain_state));
EXPECT_TRUE(state.DeleteHost("yahoo.com"));
- EXPECT_FALSE(state.GetDomainState(&domain_state, "yahoo.com", true));
-}
-
-TEST_F(TransportSecurityStateTest, SerialiseOld) {
- TransportSecurityState state("");
- // This is an old-style piece of transport state JSON, which has no creation
- // date.
- std::string output =
- "{ "
- "\"NiyD+3J1r6z1wjl2n1ALBu94Zj9OsEAMo0kCN8js0Uk=\": {"
- "\"expiry\": 1266815027.983453, "
- "\"include_subdomains\": false, "
- "\"mode\": \"strict\" "
- "}"
- "}";
- bool dirty;
- EXPECT_TRUE(state.LoadEntries(output, &dirty));
- EXPECT_TRUE(dirty);
+ EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state));
}
TEST_F(TransportSecurityStateTest, IsPreloaded) {
- TransportSecurityState state("");
-
const std::string paypal =
TransportSecurityState::CanonicalizeHost("paypal.com");
const std::string www_paypal =
@@ -527,67 +459,77 @@ TEST_F(TransportSecurityStateTest, IsPreloaded) {
const std::string aypal =
TransportSecurityState::CanonicalizeHost("aypal.com");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- EXPECT_FALSE(state.IsPreloadedSTS(paypal, true, &domain_state));
- EXPECT_TRUE(state.IsPreloadedSTS(www_paypal, true, &domain_state));
+
+ EXPECT_FALSE(state.GetStaticDomainState(paypal, true, &domain_state));
+ EXPECT_TRUE(state.GetStaticDomainState(www_paypal, true, &domain_state));
EXPECT_FALSE(domain_state.include_subdomains);
- EXPECT_FALSE(state.IsPreloadedSTS(a_www_paypal, true, &domain_state));
- EXPECT_FALSE(state.IsPreloadedSTS(abc_paypal, true, &domain_state));
- EXPECT_FALSE(state.IsPreloadedSTS(example, true, &domain_state));
- EXPECT_FALSE(state.IsPreloadedSTS(aypal, true, &domain_state));
+ EXPECT_FALSE(state.GetStaticDomainState(a_www_paypal, true, &domain_state));
+ EXPECT_FALSE(state.GetStaticDomainState(abc_paypal, true, &domain_state));
+ EXPECT_FALSE(state.GetStaticDomainState(example, true, &domain_state));
+ EXPECT_FALSE(state.GetStaticDomainState(aypal, true, &domain_state));
}
TEST_F(TransportSecurityStateTest, PreloadedDomainSet) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
// The domain wasn't being set, leading to a blank string in the
// chrome://net-internals/#hsts UI. So test that.
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "market.android.com",
- true));
+ EXPECT_TRUE(state.GetDomainState("market.android.com", true, &domain_state));
EXPECT_EQ(domain_state.domain, "market.android.com");
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "sub.market.android.com",
- true));
+ EXPECT_TRUE(state.GetDomainState("sub.market.android.com", true,
+ &domain_state));
EXPECT_EQ(domain_state.domain, "market.android.com");
}
static bool ShouldRedirect(const char* hostname) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- return state.GetDomainState(&domain_state, hostname, true /* SNI ok */) &&
+ return state.GetDomainState(hostname, true /* SNI ok */, &domain_state) &&
domain_state.ShouldRedirectHTTPToHTTPS();
}
-static bool HasState(const char *hostname) {
- TransportSecurityState state("");
+static bool HasState(const char* hostname) {
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- return state.GetDomainState(&domain_state, hostname, true /* SNI ok */);
+ return state.GetDomainState(hostname, true /* SNI ok */, &domain_state);
}
-static bool HasPins(const char *hostname) {
- TransportSecurityState state("");
+static bool HasPins(const char* hostname, bool sni_enabled) {
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- return state.HasPinsForHost(&domain_state, hostname, true /* SNI ok */);
+ if (!state.GetDomainState(hostname, sni_enabled, &domain_state))
+ return false;
+
+ return domain_state.HasPins();
+}
+
+static bool HasPins(const char* hostname) {
+ return HasPins(hostname, true);
}
static bool OnlyPinning(const char *hostname) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- return state.HasPinsForHost(&domain_state, hostname, true /* SNI ok */) &&
+ if (!state.GetDomainState(hostname, true /* SNI ok */, &domain_state))
+ return false;
+
+ return (domain_state.static_spki_hashes.size() > 0 ||
+ domain_state.bad_static_spki_hashes.size() > 0 ||
+ domain_state.dynamic_spki_hashes.size() > 0) &&
!domain_state.ShouldRedirectHTTPToHTTPS();
}
TEST_F(TransportSecurityStateTest, Preloaded) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
// We do more extensive checks for the first domain.
- EXPECT_TRUE(state.GetDomainState(&domain_state, "www.paypal.com", true));
- EXPECT_EQ(domain_state.mode,
- TransportSecurityState::DomainState::MODE_STRICT);
- EXPECT_TRUE(domain_state.preloaded);
+ EXPECT_TRUE(state.GetDomainState("www.paypal.com", true, &domain_state));
+ EXPECT_EQ(domain_state.upgrade_mode,
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS);
EXPECT_FALSE(domain_state.include_subdomains);
EXPECT_FALSE(HasState("paypal.com"));
@@ -646,16 +588,13 @@ TEST_F(TransportSecurityStateTest, Preloaded) {
EXPECT_TRUE(OnlyPinning("googlegroups.com"));
// Tests for domains that don't work without SNI.
- EXPECT_FALSE(state.GetDomainState(&domain_state, "gmail.com", false));
- EXPECT_FALSE(state.GetDomainState(&domain_state, "www.gmail.com", false));
- EXPECT_FALSE(state.GetDomainState(&domain_state, "m.gmail.com", false));
- EXPECT_FALSE(state.GetDomainState(&domain_state, "googlemail.com", false));
- EXPECT_FALSE(state.GetDomainState(&domain_state,
- "www.googlemail.com",
- false));
- EXPECT_FALSE(state.GetDomainState(&domain_state,
- "m.googlemail.com",
- false));
+ EXPECT_FALSE(state.GetDomainState("gmail.com", false, &domain_state));
+ EXPECT_FALSE(state.GetDomainState("www.gmail.com", false, &domain_state));
+ EXPECT_FALSE(state.GetDomainState("m.gmail.com", false, &domain_state));
+ EXPECT_FALSE(state.GetDomainState("googlemail.com", false, &domain_state));
+ EXPECT_FALSE(state.GetDomainState("www.googlemail.com", false,
+ &domain_state));
+ EXPECT_FALSE(state.GetDomainState("m.googlemail.com", false, &domain_state));
// Other hosts:
@@ -753,22 +692,17 @@ TEST_F(TransportSecurityStateTest, Preloaded) {
EXPECT_TRUE(ShouldRedirect("www.dropcam.com"));
EXPECT_FALSE(HasState("foo.dropcam.com"));
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "torproject.org",
- false));
- EXPECT_FALSE(domain_state.preloaded_spki_hashes.empty());
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "www.torproject.org",
- false));
- EXPECT_FALSE(domain_state.preloaded_spki_hashes.empty());
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "check.torproject.org",
- false));
- EXPECT_FALSE(domain_state.preloaded_spki_hashes.empty());
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "blog.torproject.org",
- false));
- EXPECT_FALSE(domain_state.preloaded_spki_hashes.empty());
+ EXPECT_TRUE(state.GetDomainState("torproject.org", false, &domain_state));
+ EXPECT_FALSE(domain_state.static_spki_hashes.empty());
+ EXPECT_TRUE(state.GetDomainState("www.torproject.org", false,
+ &domain_state));
+ EXPECT_FALSE(domain_state.static_spki_hashes.empty());
+ EXPECT_TRUE(state.GetDomainState("check.torproject.org", false,
+ &domain_state));
+ EXPECT_FALSE(domain_state.static_spki_hashes.empty());
+ EXPECT_TRUE(state.GetDomainState("blog.torproject.org", false,
+ &domain_state));
+ EXPECT_FALSE(domain_state.static_spki_hashes.empty());
EXPECT_TRUE(ShouldRedirect("ebanking.indovinabank.com.vn"));
EXPECT_TRUE(ShouldRedirect("foo.ebanking.indovinabank.com.vn"));
@@ -837,119 +771,65 @@ TEST_F(TransportSecurityStateTest, Preloaded) {
}
TEST_F(TransportSecurityStateTest, LongNames) {
- TransportSecurityState state("");
+ TransportSecurityState state;
const char kLongName[] =
"lookupByWaveIdHashAndWaveIdIdAndWaveIdDomainAndWaveletIdIdAnd"
"WaveletIdDomainAndBlipBlipid";
TransportSecurityState::DomainState domain_state;
// Just checks that we don't hit a NOTREACHED.
- EXPECT_FALSE(state.GetDomainState(&domain_state, kLongName, true));
+ EXPECT_FALSE(state.GetDomainState(kLongName, true, &domain_state));
}
-TEST_F(TransportSecurityStateTest, PublicKeyHashes) {
- TransportSecurityState state("");
+TEST_F(TransportSecurityStateTest, BuiltinCertPins) {
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- EXPECT_FALSE(state.GetDomainState(&domain_state, "example.com", false));
- FingerprintVector hashes;
- EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(hashes));
-
- SHA1Fingerprint hash;
- memset(hash.data, '1', sizeof(hash.data));
- domain_state.preloaded_spki_hashes.push_back(hash);
- EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(hashes));
- hashes.push_back(hash);
- EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(hashes));
- hashes[0].data[0] = '2';
- EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(hashes));
+ EXPECT_TRUE(state.GetDomainState("chrome.google.com", true, &domain_state));
+ EXPECT_TRUE(HasPins("chrome.google.com"));
- const base::Time current_time(base::Time::Now());
- const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- domain_state.expiry = expiry;
- state.EnableHost("example.com", domain_state);
- std::string ser;
- EXPECT_TRUE(state.Serialise(&ser));
- bool dirty;
- EXPECT_TRUE(state.LoadEntries(ser, &dirty));
- EXPECT_TRUE(state.GetDomainState(&domain_state, "example.com", false));
- EXPECT_EQ(1u, domain_state.preloaded_spki_hashes.size());
- EXPECT_EQ(0, memcmp(domain_state.preloaded_spki_hashes[0].data, hash.data,
- sizeof(hash.data)));
-}
-
-TEST_F(TransportSecurityStateTest, BuiltinCertPins) {
- TransportSecurityState state("");
- TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(state.GetDomainState(&domain_state,
- "chrome.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "chrome.google.com", true));
FingerprintVector hashes;
- // This essential checks that a built-in list does exist.
+ // Checks that a built-in list does exist.
EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(hashes));
- EXPECT_FALSE(state.HasPinsForHost(&domain_state, "www.paypal.com", true));
-
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "docs.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "1.docs.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "sites.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "drive.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "spreadsheets.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "health.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "checkout.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "appengine.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "market.android.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "encrypted.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "accounts.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "profiles.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "mail.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "chatenabled.mail.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "talkgadget.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "hostedtalkgadget.google.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "talk.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "plus.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "groups.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "apis.google.com", true));
-
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "ssl.gstatic.com", true));
- EXPECT_FALSE(state.HasPinsForHost(&domain_state, "www.gstatic.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "ssl.google-analytics.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "www.googleplex.com", true));
+ EXPECT_FALSE(HasPins("www.paypal.com"));
+
+ EXPECT_TRUE(HasPins("docs.google.com"));
+ EXPECT_TRUE(HasPins("1.docs.google.com"));
+ EXPECT_TRUE(HasPins("sites.google.com"));
+ EXPECT_TRUE(HasPins("drive.google.com"));
+ EXPECT_TRUE(HasPins("spreadsheets.google.com"));
+ EXPECT_TRUE(HasPins("health.google.com"));
+ EXPECT_TRUE(HasPins("checkout.google.com"));
+ EXPECT_TRUE(HasPins("appengine.google.com"));
+ EXPECT_TRUE(HasPins("market.android.com"));
+ EXPECT_TRUE(HasPins("encrypted.google.com"));
+ EXPECT_TRUE(HasPins("accounts.google.com"));
+ EXPECT_TRUE(HasPins("profiles.google.com"));
+ EXPECT_TRUE(HasPins("mail.google.com"));
+ EXPECT_TRUE(HasPins("chatenabled.mail.google.com"));
+ EXPECT_TRUE(HasPins("talkgadget.google.com"));
+ EXPECT_TRUE(HasPins("hostedtalkgadget.google.com"));
+ EXPECT_TRUE(HasPins("talk.google.com"));
+ EXPECT_TRUE(HasPins("plus.google.com"));
+ EXPECT_TRUE(HasPins("groups.google.com"));
+ EXPECT_TRUE(HasPins("apis.google.com"));
+
+ EXPECT_TRUE(HasPins("ssl.gstatic.com"));
+ EXPECT_FALSE(HasPins("www.gstatic.com"));
+ EXPECT_TRUE(HasPins("ssl.google-analytics.com"));
+ EXPECT_TRUE(HasPins("www.googleplex.com"));
// Disabled in order to help track down pinning failures --agl
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "twitter.com", true));
- EXPECT_FALSE(state.HasPinsForHost(&domain_state, "foo.twitter.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "www.twitter.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "api.twitter.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "oauth.twitter.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "mobile.twitter.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "dev.twitter.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "business.twitter.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "platform.twitter.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "si0.twimg.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "twimg0-a.akamaihd.net",
- true));
+ EXPECT_TRUE(HasPins("twitter.com"));
+ EXPECT_FALSE(HasPins("foo.twitter.com"));
+ EXPECT_TRUE(HasPins("www.twitter.com"));
+ EXPECT_TRUE(HasPins("api.twitter.com"));
+ EXPECT_TRUE(HasPins("oauth.twitter.com"));
+ EXPECT_TRUE(HasPins("mobile.twitter.com"));
+ EXPECT_TRUE(HasPins("dev.twitter.com"));
+ EXPECT_TRUE(HasPins("business.twitter.com"));
+ EXPECT_TRUE(HasPins("platform.twitter.com"));
+ EXPECT_TRUE(HasPins("si0.twimg.com"));
+ EXPECT_TRUE(HasPins("twimg0-a.akamaihd.net"));
}
static bool AddHash(const std::string& type_and_base64,
@@ -995,9 +875,10 @@ TEST_F(TransportSecurityStateTest, PinValidationWithRejectedCerts) {
EXPECT_TRUE(AddHash(kBadPath[i], &bad_hashes));
}
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "plus.google.com", true));
+ EXPECT_TRUE(state.GetDomainState("plus.google.com", true, &domain_state));
+ EXPECT_TRUE(domain_state.HasPins());
EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(good_hashes));
EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(bad_hashes));
@@ -1030,75 +911,41 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) {
EXPECT_TRUE(AddHash(kBadPath[i], &bad_hashes));
}
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "blog.torproject.org", true));
+ EXPECT_TRUE(state.GetDomainState("blog.torproject.org", true, &domain_state));
+ EXPECT_TRUE(domain_state.HasPins());
EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(good_hashes));
EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(bad_hashes));
}
TEST_F(TransportSecurityStateTest, OptionalHSTSCertPins) {
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
+
EXPECT_FALSE(ShouldRedirect("www.google-analytics.com"));
- EXPECT_FALSE(state.HasPinsForHost(&domain_state,
- "www.google-analytics.com",
- false));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "www.google-analytics.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "www.google.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "mail-attachment.googleusercontent.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "www.youtube.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "i.ytimg.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "googleapis.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "ajax.googleapis.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "googleadservices.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "pagead2.googleadservices.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "googlecode.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "kibbles.googlecode.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "appspot.com", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state,
- "googlesyndication.com",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "doubleclick.net", true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "ad.doubleclick.net", true));
- EXPECT_FALSE(state.HasPinsForHost(&domain_state,
- "learn.doubleclick.net",
- true));
- EXPECT_TRUE(state.HasPinsForHost(&domain_state, "a.googlegroups.com", true));
- EXPECT_FALSE(state.HasPinsForHost(&domain_state,
- "a.googlegroups.com",
- false));
-}
-TEST_F(TransportSecurityStateTest, ForcePreloads) {
- // This is a docs.google.com override.
- std::string preload("{"
- "\"4AGT3lHihuMSd5rUj7B4u6At0jlSH3HFePovjPR+oLE=\": {"
- "\"created\": 0.0,"
- "\"expiry\": 2000000000.0,"
- "\"include_subdomains\": false,"
- "\"mode\": \"pinning-only\""
- "}}");
-
- TransportSecurityState state(preload);
- TransportSecurityState::DomainState domain_state;
- EXPECT_FALSE(state.HasPinsForHost(&domain_state, "docs.google.com", true));
- EXPECT_TRUE(state.GetDomainState(&domain_state, "docs.google.com", true));
- EXPECT_FALSE(domain_state.ShouldRedirectHTTPToHTTPS());
+ EXPECT_FALSE(HasPins("www.google-analytics.com", false));
+ EXPECT_TRUE(HasPins("www.google-analytics.com"));
+ EXPECT_TRUE(HasPins("google.com"));
+ EXPECT_TRUE(HasPins("www.google.com"));
+ EXPECT_TRUE(HasPins("mail-attachment.googleusercontent.com"));
+ EXPECT_TRUE(HasPins("www.youtube.com"));
+ EXPECT_TRUE(HasPins("i.ytimg.com"));
+ EXPECT_TRUE(HasPins("googleapis.com"));
+ EXPECT_TRUE(HasPins("ajax.googleapis.com"));
+ EXPECT_TRUE(HasPins("googleadservices.com"));
+ EXPECT_TRUE(HasPins("pagead2.googleadservices.com"));
+ EXPECT_TRUE(HasPins("googlecode.com"));
+ EXPECT_TRUE(HasPins("kibbles.googlecode.com"));
+ EXPECT_TRUE(HasPins("appspot.com"));
+ EXPECT_TRUE(HasPins("googlesyndication.com"));
+ EXPECT_TRUE(HasPins("doubleclick.net"));
+ EXPECT_TRUE(HasPins("ad.doubleclick.net"));
+ EXPECT_FALSE(HasPins("learn.doubleclick.net"));
+ EXPECT_TRUE(HasPins("a.googlegroups.com"));
+ EXPECT_FALSE(HasPins("a.googlegroups.com", false));
}
TEST_F(TransportSecurityStateTest, OverrideBuiltins) {
@@ -1106,14 +953,14 @@ TEST_F(TransportSecurityStateTest, OverrideBuiltins) {
EXPECT_FALSE(ShouldRedirect("google.com"));
EXPECT_FALSE(ShouldRedirect("www.google.com"));
- TransportSecurityState state("");
+ TransportSecurityState state;
TransportSecurityState::DomainState domain_state;
const base::Time current_time(base::Time::Now());
const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
- domain_state.expiry = expiry;
+ domain_state.upgrade_expiry = expiry;
state.EnableHost("www.google.com", domain_state);
- EXPECT_TRUE(state.GetDomainState(&domain_state, "www.google.com", true));
+ EXPECT_TRUE(state.GetDomainState("www.google.com", true, &domain_state));
}
static const uint8 kSidePinLeafSPKI[] = {
@@ -1148,72 +995,6 @@ static const uint8 kSidePinExpectedHash[20] = {
0xc4, 0x09, 0x3d, 0x2d, 0x1d, 0xea, 0x1e,
};
-TEST_F(TransportSecurityStateTest, ParseSidePins) {
- base::StringPiece leaf_spki(reinterpret_cast<const char*>(kSidePinLeafSPKI),
- sizeof(kSidePinLeafSPKI));
- base::StringPiece side_info(reinterpret_cast<const char*>(kSidePinInfo),
- sizeof(kSidePinInfo));
-
- FingerprintVector pub_key_hashes;
- EXPECT_TRUE(TransportSecurityState::ParseSidePin(
- leaf_spki, side_info, &pub_key_hashes));
- ASSERT_EQ(1u, pub_key_hashes.size());
- EXPECT_EQ(0, memcmp(pub_key_hashes[0].data, kSidePinExpectedHash,
- sizeof(kSidePinExpectedHash)));
-}
-
-TEST_F(TransportSecurityStateTest, ParseSidePinsFailsWithBadData) {
- uint8 leaf_spki_copy[sizeof(kSidePinLeafSPKI)];
- memcpy(leaf_spki_copy, kSidePinLeafSPKI, sizeof(leaf_spki_copy));
-
- uint8 side_info_copy[sizeof(kSidePinInfo)];
- memcpy(side_info_copy, kSidePinInfo, sizeof(kSidePinInfo));
-
- base::StringPiece leaf_spki(reinterpret_cast<const char*>(leaf_spki_copy),
- sizeof(leaf_spki_copy));
- base::StringPiece side_info(reinterpret_cast<const char*>(side_info_copy),
- sizeof(side_info_copy));
- FingerprintVector pub_key_hashes;
-
- // Tweak |leaf_spki| and expect a failure.
- leaf_spki_copy[10] ^= 1;
- EXPECT_FALSE(TransportSecurityState::ParseSidePin(
- leaf_spki, side_info, &pub_key_hashes));
- ASSERT_EQ(0u, pub_key_hashes.size());
-
- // Undo the change to |leaf_spki| and tweak |side_info|.
- leaf_spki_copy[10] ^= 1;
- side_info_copy[30] ^= 1;
- EXPECT_FALSE(TransportSecurityState::ParseSidePin(
- leaf_spki, side_info, &pub_key_hashes));
- ASSERT_EQ(0u, pub_key_hashes.size());
-}
-
-TEST_F(TransportSecurityStateTest, DISABLED_ParseSidePinsFuzz) {
- // Disabled because it's too slow for normal tests. Run manually when
- // changing the underlying code.
-
- base::StringPiece leaf_spki(reinterpret_cast<const char*>(kSidePinLeafSPKI),
- sizeof(kSidePinLeafSPKI));
- uint8 side_info_copy[sizeof(kSidePinInfo)];
- base::StringPiece side_info(reinterpret_cast<const char*>(side_info_copy),
- sizeof(side_info_copy));
- FingerprintVector pub_key_hashes;
- static const size_t bit_length = sizeof(kSidePinInfo) * 8;
-
- for (size_t bit_to_flip = 0; bit_to_flip < bit_length; bit_to_flip++) {
- memcpy(side_info_copy, kSidePinInfo, sizeof(kSidePinInfo));
-
- size_t byte = bit_to_flip >> 3;
- size_t bit = bit_to_flip & 7;
- side_info_copy[byte] ^= (1 << bit);
-
- EXPECT_FALSE(TransportSecurityState::ParseSidePin(
- leaf_spki, side_info, &pub_key_hashes));
- ASSERT_EQ(0u, pub_key_hashes.size());
- }
-}
-
TEST_F(TransportSecurityStateTest, GooglePinnedProperties) {
EXPECT_FALSE(TransportSecurityState::IsGooglePinnedProperty(
"www.example.com", true));
diff --git a/net/base/x509_cert_types.h b/net/base/x509_cert_types.h
index 61af236..e10c395 100644
--- a/net/base/x509_cert_types.h
+++ b/net/base/x509_cert_types.h
@@ -37,6 +37,13 @@ struct NET_EXPORT SHA1Fingerprint {
unsigned char data[20];
};
+// In the future there will be a generic Fingerprint type, with at least two
+// implementations: SHA1 and SHA256. See http://crbug.com/117914. Until that
+// work is done (in a separate patch) this typedef bridges the gap.
+typedef SHA1Fingerprint Fingerprint;
+
+typedef std::vector<Fingerprint> FingerprintVector;
+
class NET_EXPORT SHA1FingerprintLessThan {
public:
bool operator() (const SHA1Fingerprint& lhs,
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 37f9566..bd7b8e9c 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -1723,56 +1723,6 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) {
UMA_HISTOGRAM_TIMES("Net.SSLCertVerificationTimeError", verify_time);
}
- PeerCertificateChain chain(nss_fd_);
- for (unsigned i = 1; i < chain.size(); i++) {
- if (strcmp(chain[i]->subjectName, "CN=meta") != 0)
- continue;
-
- base::StringPiece leaf_der(
- reinterpret_cast<char*>(server_cert_nss_->derCert.data),
- server_cert_nss_->derCert.len);
- base::StringPiece leaf_spki;
- if (!asn1::ExtractSPKIFromDERCert(leaf_der, &leaf_spki))
- break;
-
- static SECOidTag side_data_tag;
- static bool side_data_tag_valid;
- if (!side_data_tag_valid) {
- // It's harmless if multiple threads enter this block concurrently.
- static const uint8 kSideDataOID[] =
- // 1.3.6.1.4.1.11129.2.1.4
- // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
- // certificateExtensions.sideData)
- {0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x05};
- SECOidData oid_data;
- memset(&oid_data, 0, sizeof(oid_data));
- oid_data.oid.data = const_cast<uint8*>(kSideDataOID);
- oid_data.oid.len = sizeof(kSideDataOID);
- oid_data.desc = "Certificate side data";
- oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
- side_data_tag = SECOID_AddEntry(&oid_data);
- DCHECK_NE(SEC_OID_UNKNOWN, side_data_tag);
- side_data_tag_valid = true;
- }
-
- SECItem side_data_item;
- SECStatus rv = CERT_FindCertExtension(chain[i],
- side_data_tag, &side_data_item);
- if (rv != SECSuccess)
- continue;
-
- base::StringPiece side_data(
- reinterpret_cast<char*>(side_data_item.data),
- side_data_item.len);
-
- if (!TransportSecurityState::ParseSidePin(
- leaf_spki, side_data, &side_pinned_public_keys_)) {
- LOG(WARNING) << "Side pinning data failed to parse: "
- << host_and_port_.host();
- }
- break;
- }
-
// We used to remember the intermediate CA certs in the NSS database
// persistently. However, NSS opens a connection to the SQLite database
// during NSS initialization and doesn't close the connection until NSS
diff --git a/net/socket_stream/socket_stream.cc b/net/socket_stream/socket_stream.cc
index c5a8b2a..51e92907 100644
--- a/net/socket_stream/socket_stream.cc
+++ b/net/socket_stream/socket_stream.cc
@@ -9,6 +9,7 @@
#include <set>
#include <string>
+#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -1205,8 +1206,9 @@ int SocketStream::HandleCertificateError(int result) {
const bool fatal =
context_->transport_security_state() &&
context_->transport_security_state()->GetDomainState(
- &domain_state, url_.host(),
- SSLConfigService::IsSNIAvailable(context_->ssl_config_service()));
+ url_.host(),
+ SSLConfigService::IsSNIAvailable(context_->ssl_config_service()),
+ &domain_state);
delegate_->OnSSLCertificateError(this, ssl_info, fatal);
return ERR_IO_PENDING;
diff --git a/net/socket_stream/socket_stream_job.cc b/net/socket_stream/socket_stream_job.cc
index 6672df6..106656e 100644
--- a/net/socket_stream/socket_stream_job.cc
+++ b/net/socket_stream/socket_stream_job.cc
@@ -28,7 +28,7 @@ SocketStreamJob* SocketStreamJob::CreateSocketStreamJob(
GURL socket_url(url);
TransportSecurityState::DomainState domain_state;
if (url.scheme() == "ws" && sts && sts->GetDomainState(
- &domain_state, url.host(), SSLConfigService::IsSNIAvailable(ssl)) &&
+ url.host(), SSLConfigService::IsSNIAvailable(ssl), &domain_state) &&
domain_state.ShouldRedirectHTTPToHTTPS()) {
url_canon::Replacements<char> replacements;
static const char kNewScheme[] = "wss";
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index 552f7af..5f8a32c 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -214,7 +214,7 @@ scoped_refptr<URLRequestContext> URLRequestContextBuilder::Build() {
storage->set_http_auth_handler_factory(
net::HttpAuthHandlerRegistryFactory::CreateDefault(host_resolver));
storage->set_cookie_store(new CookieMonster(NULL, NULL));
- storage->set_transport_security_state(new net::TransportSecurityState(""));
+ storage->set_transport_security_state(new net::TransportSecurityState());
storage->set_http_server_properties(new net::HttpServerPropertiesImpl);
storage->set_cert_verifier(CertVerifier::CreateDefault());
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index cb7a64d..8e6626a 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -144,10 +144,10 @@ URLRequestJob* URLRequestHttpJob::Factory(URLRequest* request,
if (scheme == "http" &&
request->context()->transport_security_state() &&
request->context()->transport_security_state()->GetDomainState(
- &domain_state,
request->url().host(),
SSLConfigService::IsSNIAvailable(
- request->context()->ssl_config_service())) &&
+ request->context()->ssl_config_service()),
+ &domain_state) &&
domain_state.ShouldRedirectHTTPToHTTPS()) {
DCHECK_EQ(request->url().scheme(), "http");
url_canon::Replacements<char> replacements;
@@ -599,30 +599,21 @@ void URLRequestHttpJob::ProcessStrictTransportSecurityHeader() {
bool sni_available =
SSLConfigService::IsSNIAvailable(ctx->ssl_config_service());
- if (!security_state->HasMetadata(&domain_state, host, sni_available)) {
- // |HasMetadata| may have altered |domain_state| while searching. If not
- // found, start with a fresh state.
- domain_state = TransportSecurityState::DomainState();
- domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
- }
+ if (!security_state->GetDomainState(host, sni_available, &domain_state))
+ // |GetDomainState| may have altered |domain_state| while searching. If
+ // not found, start with a fresh state.
+ domain_state.upgrade_mode =
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
HttpResponseHeaders* headers = GetResponseHeaders();
std::string value;
void* iter = NULL;
+ base::Time now = base::Time::Now();
while (headers->EnumerateHeader(&iter, "Strict-Transport-Security", &value)) {
- int max_age;
- bool include_subdomains;
- if (TransportSecurityState::ParseHeader(value, &max_age,
- &include_subdomains)) {
- base::Time current_time(base::Time::Now());
- base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
-
- domain_state.expiry = current_time + max_age_delta;
- domain_state.include_subdomains = include_subdomains;
-
+ TransportSecurityState::DomainState domain_state;
+ if (domain_state.ParseSTSHeader(now, value))
security_state->EnableHost(host, domain_state);
- }
}
}
@@ -645,25 +636,23 @@ void URLRequestHttpJob::ProcessPublicKeyPinsHeader() {
bool sni_available =
SSLConfigService::IsSNIAvailable(ctx->ssl_config_service());
- if (!security_state->HasMetadata(&domain_state, host, sni_available)) {
- // |HasMetadata| may have altered |domain_state| while searching. If not
- // found, start with a fresh state.
- domain_state = TransportSecurityState::DomainState();
- domain_state.mode = TransportSecurityState::DomainState::MODE_PINNING_ONLY;
- }
+ if (!security_state->GetDomainState(host, sni_available, &domain_state))
+ // |GetDomainState| may have altered |domain_state| while searching. If
+ // not found, start with a fresh state.
+ domain_state.upgrade_mode =
+ TransportSecurityState::DomainState::MODE_DEFAULT;
HttpResponseHeaders* headers = GetResponseHeaders();
void* iter = NULL;
std::string value;
+ base::Time now = base::Time::Now();
while (headers->EnumerateHeader(&iter, "Public-Key-Pins", &value)) {
// Note that ParsePinsHeader updates |domain_state| (iff the header parses
// correctly), but does not completely overwrite it. It just updates the
// dynamic pinning metadata.
- if (TransportSecurityState::ParsePinsHeader(value, ssl_info,
- &domain_state)) {
+ if (domain_state.ParsePinsHeader(now, value, ssl_info))
security_state->EnableHost(host, domain_state);
- }
}
}
@@ -729,8 +718,9 @@ void URLRequestHttpJob::OnStartCompleted(int result) {
const bool fatal =
context_->transport_security_state() &&
context_->transport_security_state()->GetDomainState(
- &domain_state, request_info_.url.host(),
- SSLConfigService::IsSNIAvailable(context_->ssl_config_service()));
+ request_info_.url.host(),
+ SSLConfigService::IsSNIAvailable(context_->ssl_config_service()),
+ &domain_state);
NotifySSLCertificateError(transaction_->GetResponseInfo()->ssl_info, fatal);
} else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
NotifyCertificateRequested(
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index d53c01c..204f223 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -479,8 +479,8 @@ class URLRequestTestHTTP : public URLRequestTest {
void HTTPUploadDataOperationTest(const std::string& method) {
const int kMsgSize = 20000; // multiple of 10
const int kIterations = 50;
- char *uploadBytes = new char[kMsgSize+1];
- char *ptr = uploadBytes;
+ char* uploadBytes = new char[kMsgSize+1];
+ char* ptr = uploadBytes;
char marker = 'a';
for (int idx = 0; idx < kMsgSize/10; idx++) {
memcpy(ptr, "----------", 10);
@@ -1774,7 +1774,7 @@ TEST_F(HTTPSRequestTest, HTTPSPreloadedHSTSTest) {
scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext(true));
context->set_network_delegate(&network_delegate);
context->set_host_resolver(&host_resolver);
- TransportSecurityState transport_security_state("");
+ TransportSecurityState transport_security_state;
context->set_transport_security_state(&transport_security_state);
context->Init();
@@ -1817,10 +1817,10 @@ TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) {
scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext(true));
context->set_network_delegate(&network_delegate);
context->set_host_resolver(&host_resolver);
- TransportSecurityState transport_security_state("");
+ TransportSecurityState transport_security_state;
TransportSecurityState::DomainState domain_state;
- EXPECT_TRUE(transport_security_state.HasMetadata(&domain_state,
- "www.google.com", true));
+ EXPECT_TRUE(transport_security_state.GetDomainState("www.google.com", true,
+ &domain_state));
context->set_transport_security_state(&transport_security_state);
context->Init();
@@ -1842,17 +1842,17 @@ TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) {
// Get a fresh copy of the state, and check that it hasn't been updated.
TransportSecurityState::DomainState new_domain_state;
- EXPECT_TRUE(transport_security_state.HasMetadata(&new_domain_state,
- "www.google.com", true));
- EXPECT_EQ(new_domain_state.mode, domain_state.mode);
+ EXPECT_TRUE(transport_security_state.GetDomainState("www.google.com", true,
+ &new_domain_state));
+ EXPECT_EQ(new_domain_state.upgrade_mode, domain_state.upgrade_mode);
EXPECT_EQ(new_domain_state.include_subdomains,
domain_state.include_subdomains);
- EXPECT_TRUE(FingerprintsEqual(new_domain_state.preloaded_spki_hashes,
- domain_state.preloaded_spki_hashes));
+ EXPECT_TRUE(FingerprintsEqual(new_domain_state.static_spki_hashes,
+ domain_state.static_spki_hashes));
EXPECT_TRUE(FingerprintsEqual(new_domain_state.dynamic_spki_hashes,
domain_state.dynamic_spki_hashes));
- EXPECT_TRUE(FingerprintsEqual(new_domain_state.bad_preloaded_spki_hashes,
- domain_state.bad_preloaded_spki_hashes));
+ EXPECT_TRUE(FingerprintsEqual(new_domain_state.bad_static_spki_hashes,
+ domain_state.bad_static_spki_hashes));
}
namespace {
@@ -2571,8 +2571,8 @@ TEST_F(URLRequestTest, ResolveShortcutTest) {
std::wstring lnk_path = app_path.value() + L".lnk";
HRESULT result;
- IShellLink *shell = NULL;
- IPersistFile *persist = NULL;
+ IShellLink* shell = NULL;
+ IPersistFile* persist = NULL;
CoInitialize(NULL);
// Temporarily create a shortcut for test
diff --git a/net/websockets/websocket_job_spdy2_unittest.cc b/net/websockets/websocket_job_spdy2_unittest.cc
index ebcc72f..05731f5 100644
--- a/net/websockets/websocket_job_spdy2_unittest.cc
+++ b/net/websockets/websocket_job_spdy2_unittest.cc
@@ -174,7 +174,7 @@ class MockCookieStore : public net::CookieStore {
const net::CookieOptions& options) {
std::string result;
for (size_t i = 0; i < entries_.size(); i++) {
- Entry &entry = entries_[i];
+ Entry& entry = entries_[i];
if (url == entry.url) {
if (!result.empty()) {
result += "; ";
@@ -250,11 +250,12 @@ class MockSSLConfigService : public net::SSLConfigService {
class MockURLRequestContext : public net::URLRequestContext {
public:
explicit MockURLRequestContext(net::CookieStore* cookie_store)
- : transport_security_state_(std::string()) {
+ : transport_security_state_() {
set_cookie_store(cookie_store);
set_transport_security_state(&transport_security_state_);
net::TransportSecurityState::DomainState state;
- state.expiry = base::Time::Now() + base::TimeDelta::FromSeconds(1000);
+ state.upgrade_expiry = base::Time::Now() +
+ base::TimeDelta::FromSeconds(1000);
transport_security_state_.EnableHost("upgrademe.com", state);
}
@@ -268,7 +269,7 @@ class MockURLRequestContext : public net::URLRequestContext {
class MockHttpTransactionFactory : public net::HttpTransactionFactory {
public:
- MockHttpTransactionFactory(net::OrderedSocketData* data) {
+ explicit MockHttpTransactionFactory(net::OrderedSocketData* data) {
data_ = data;
net::MockConnect connect_data(net::SYNCHRONOUS, net::OK);
data_->set_connect_data(connect_data);
diff --git a/net/websockets/websocket_job_spdy3_unittest.cc b/net/websockets/websocket_job_spdy3_unittest.cc
index 6fd4767..9c908c8 100644
--- a/net/websockets/websocket_job_spdy3_unittest.cc
+++ b/net/websockets/websocket_job_spdy3_unittest.cc
@@ -178,7 +178,7 @@ class MockCookieStore : public net::CookieStore {
const net::CookieOptions& options) {
std::string result;
for (size_t i = 0; i < entries_.size(); i++) {
- Entry &entry = entries_[i];
+ Entry& entry = entries_[i];
if (url == entry.url) {
if (!result.empty()) {
result += "; ";
@@ -254,11 +254,12 @@ class MockSSLConfigService : public net::SSLConfigService {
class MockURLRequestContext : public net::URLRequestContext {
public:
explicit MockURLRequestContext(net::CookieStore* cookie_store)
- : transport_security_state_(std::string()) {
+ : transport_security_state_() {
set_cookie_store(cookie_store);
set_transport_security_state(&transport_security_state_);
net::TransportSecurityState::DomainState state;
- state.expiry = base::Time::Now() + base::TimeDelta::FromSeconds(1000);
+ state.upgrade_expiry = base::Time::Now() +
+ base::TimeDelta::FromSeconds(1000);
transport_security_state_.EnableHost("upgrademe.com", state);
}
@@ -272,7 +273,7 @@ class MockURLRequestContext : public net::URLRequestContext {
class MockHttpTransactionFactory : public net::HttpTransactionFactory {
public:
- MockHttpTransactionFactory(net::OrderedSocketData* data) {
+ explicit MockHttpTransactionFactory(net::OrderedSocketData* data) {
data_ = data;
net::MockConnect connect_data(net::SYNCHRONOUS, net::OK);
data_->set_connect_data(connect_data);