Chromium Code Reviews| Index: net/base/transport_security_state.cc |
| =================================================================== |
| --- net/base/transport_security_state.cc (revision 128526) |
| +++ net/base/transport_security_state.cc (working copy) |
| @@ -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/public_key_hashes.h" |
| #include "net/base/ssl_info.h" |
| @@ -48,13 +45,14 @@ |
| const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
| +/* XXX |
|
Ryan Sleevi
2012/03/28 00:50:32
Delete?
palmer
2012/04/10 23:25:51
Done.
|
| 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]; |
| @@ -62,6 +60,13 @@ |
| return std::string(hashed, sizeof(hashed)); |
| } |
| +TransportSecurityState::TransportSecurityState( |
| + TransportSecurityState::Delegate* delegate) |
| + : delegate_(delegate) { } |
| + |
| +TransportSecurityState::TransportSecurityState() |
| + : delegate_(NULL) { } |
| + |
| void TransportSecurityState::SetDelegate( |
| TransportSecurityState::Delegate* delegate) { |
| delegate_ = delegate; |
| @@ -75,24 +80,24 @@ |
| if (canonicalized_host.empty()) |
| return; |
| + DomainState existing_state; |
| + |
| // 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) && |
| + /*if (GetStaticDomainState(canonicalized_host, true, &existing_state) && |
| canonicalized_host == CanonicalizeHost(existing_state.domain) && |
| existing_state.IsMoreStrict(state)) { |
| return; |
| - } |
| + }*/ |
|
Ryan Sleevi
2012/03/28 00:50:32
Delete?
palmer
2012/04/10 23:25:51
Done.
|
| // Use the original creation date if we already have this host. |
| DomainState state_copy(state); |
| - if (GetDomainState(&existing_state, host, true) && |
| + if (GetDomainState(host, true, &existing_state) && |
|
Ryan Sleevi
2012/03/28 00:50:32
nit: GetDomainState(host, true /*sni_enabled*/, &e
palmer
2012/04/10 23:25:51
It doesn't really matter, since statically-defined
|
| !existing_state.created.is_null()) { |
| state_copy.created = existing_state.created; |
| } |
| - // We don't store these values. |
| - state_copy.preloaded = false; |
| + // We don't store this value. |
|
Ryan Sleevi
2012/03/28 00:50:32
nit: Drop the "we", expand on why this value isn't
palmer
2012/04/10 23:25:51
Done.
|
| state_copy.domain.clear(); |
| enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
| @@ -116,35 +121,18 @@ |
| return false; |
| } |
| -bool TransportSecurityState::HasPinsForHost(DomainState* result, |
| - const std::string& host, |
| - bool sni_available) { |
| +bool TransportSecurityState::GetDomainState(const std::string& host, |
| + bool sni_enabled, |
| + DomainState* result) { |
| 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) { |
| - DCHECK(CalledOnValidThread()); |
| - |
| DomainState state; |
| const std::string canonicalized_host = CanonicalizeHost(host); |
| 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()); |
| @@ -163,7 +151,7 @@ |
| 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(); |
| @@ -291,23 +279,6 @@ |
| 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) {} |
| @@ -369,13 +340,12 @@ |
| // "Public-Key-Pins" ":" |
| // "max-age" "=" delta-seconds ";" |
| // "pin-" algo "=" base64 [ ";" ... ] |
|
Ryan Sleevi
2012/03/28 00:50:32
Update the ABNF for -01.
palmer
2012/04/10 23:25:51
Done.
|
| -// |
| -// 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; |
| @@ -390,11 +360,12 @@ |
| 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)) |
| @@ -411,15 +382,14 @@ |
| 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); |
| } |
| } |
| @@ -428,14 +398,9 @@ |
| // "Strict-Transport-Security" ":" |
| // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
|
Ryan Sleevi
2012/03/28 00:50:32
Definitely update this guy
palmer
2012/04/10 23:25:51
What is wrong about this comment, other than being
|
| -// |
| -// 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 { |
| @@ -512,14 +477,18 @@ |
| 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(); |
| @@ -527,370 +496,6 @@ |
| } |
| } |
| -// 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; |
| @@ -902,115 +507,6 @@ |
| 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, false /* do not allow trailing commas */)); |
| - 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() { |
| } |
| @@ -1124,11 +620,11 @@ |
| 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++; |
| } |
| @@ -1136,7 +632,7 @@ |
| 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++; |
| } |
| @@ -1260,6 +756,14 @@ |
| kNoRejectedPublicKeys, \ |
| } |
| +// 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; |
|
Ryan Sleevi
2012/03/28 00:50:32
This was related to side pinning - delete?
palmer
2012/04/10 23:25:51
Done.
|
| + |
| // kTestAcceptableCerts doesn't actually match any public keys and is used |
| // with "pinningtest.appspot.com", below, to test if pinning is active. |
| static const char* const kTestAcceptableCerts[] = { |
| @@ -1521,7 +1025,7 @@ |
| // 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); |
| @@ -1529,7 +1033,7 @@ |
| 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) |
| @@ -1559,16 +1063,13 @@ |
| 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) { |
| @@ -1579,7 +1080,6 @@ |
| 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; |
| @@ -1587,7 +1087,7 @@ |
| &ret)) { |
| return ret; |
| } |
| - if (sni_available && |
| + if (sni_enabled && |
| HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, |
| out, &ret)) { |
| return ret; |
| @@ -1613,33 +1113,31 @@ |
| } |
| 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; |
| } |
| @@ -1647,20 +1145,9 @@ |
| 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; |
| - |
| - return true; |
| -} |
| - |
| bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() |
| const { |
| - return mode == MODE_STRICT; |
| + return upgrade_mode == MODE_FORCE_HTTPS; |
| } |
| } // namespace |