Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3507)

Unified Diff: chrome/browser/transport_security_persister.cc

Issue 9415040: Refactor TransportSecurityState. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/transport_security_persister.cc
===================================================================
--- chrome/browser/transport_security_persister.cc (revision 133741)
+++ chrome/browser/transport_security_persister.cc (working copy)
@@ -4,16 +4,25 @@
#include "chrome/browser/transport_security_persister.h"
+#include "base/base64.h"
#include "base/bind.h"
#include "base/file_path.h"
#include "base/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
#include "base/message_loop.h"
#include "base/path_service.h"
+#include "base/values.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/browser_thread.h"
+#include "crypto/sha2.h"
#include "net/base/transport_security_state.h"
+#include "net/base/x509_certificate.h"
using content::BrowserThread;
+using net::Fingerprint;
+using net::FingerprintVector;
+using net::TransportSecurityState;
class TransportSecurityPersister::Loader {
public:
@@ -52,7 +61,7 @@
};
TransportSecurityPersister::TransportSecurityPersister(
- net::TransportSecurityState* state,
+ TransportSecurityState* state,
const FilePath& profile_path,
bool readonly)
: transport_security_state_(state),
@@ -80,11 +89,194 @@
transport_security_state_->SetDelegate(NULL);
}
+namespace {
Ryan Sleevi 2012/04/26 19:21:12 Place unnamed namespaces at the beginning of the f
palmer 2012/04/27 23:52:34 Done.
+
+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;
+}
+
+void SPKIHashesFromListValue(const ListValue& pins, FingerprintVector* hashes) {
+ size_t num_pins = pins.GetSize();
+ for (size_t i = 0; i < num_pins; ++i) {
+ std::string type_and_base64;
+ Fingerprint fingerprint;
+ if (pins.GetString(i, &type_and_base64) &&
+ TransportSecurityState::ParsePin(type_and_base64, &fingerprint)) {
+ hashes->push_back(fingerprint);
+ }
+ }
+}
+
+// This function converts the binary hashes to a base64 string which we can
+// include in a JSON file.
+std::string HashedDomainToExternalString(const std::string& hashed) {
+ std::string out;
+ base::Base64Encode(hashed, &out);
+ return out;
+}
+
+// This inverts |HashedDomainToExternalString|, above. It turns an external
+// string (from a JSON file) into an internal (binary) string.
+std::string ExternalStringToHashedDomain(const std::string& external) {
+ std::string out;
+ if (!base::Base64Decode(external, &out) ||
+ out.size() != crypto::kSHA256Length) {
+ return std::string();
+ }
+
+ return out;
+}
+
+const char kIncludeSubdomains[] = "include_subdomains";
+const char kMode[] = "mode";
+const char kExpiry[] = "expiry";
+const char kDynamicSPKIHashesExpiry[] = "dynamic_spki_hashes_expiry";
+const char kStaticSPKIHashes[] = "static_spki_hashes";
+const char kPreloadedSPKIHashes[] = "preloaded_spki_hashes";
+const char kDynamicSPKIHashes[] = "dynamic_spki_hashes";
+const char kForceHTTPS[] = "force-https";
+const char kStrict[] = "strict";
+const char kDefault[] = "default";
+const char kPinningOnly[] = "pinning-only";
+const char kCreated[] = "created";
+
+} // anonymous namespce
+
+bool TransportSecurityPersister::DeserializeFromCommandLine(
Ryan Sleevi 2012/04/26 19:21:12 nit: Fix the ordering of this file so that definit
palmer 2012/04/27 23:52:34 Will do.
+ const std::string& serialized) {
+ // We purposefully ignore |dirty| because we do not want to persist
Ryan Sleevi 2012/04/26 19:21:12 nit: Drop the we
palmer 2012/04/27 23:52:34 Done.
+ // entries deserialized in this way.
+ bool dirty;
+ return Deserialize(serialized, &dirty, true, transport_security_state_);
+}
+
+// static
+bool TransportSecurityPersister::Deserialize(const std::string& serialized,
+ bool* dirty,
+ bool forced,
+ TransportSecurityState* state) {
+ scoped_ptr<Value> value(base::JSONReader::Read(serialized));
+ DictionaryValue* dict_value;
+ if (!value.get() || !value->GetAsDictionary(&dict_value))
+ return false;
+
+ 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* parsed;
+ if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &parsed))
+ continue;
+
+ bool include_subdomains;
+ std::string mode_string;
+ double created;
+ double expiry;
+ double dynamic_spki_hashes_expiry = 0.0;
+
+ if (!parsed->GetBoolean(kIncludeSubdomains, &include_subdomains) ||
+ !parsed->GetString(kMode, &mode_string) ||
+ !parsed->GetDouble(kExpiry, &expiry)) {
Ryan Sleevi 2012/04/26 19:21:12 So input data failures aren't fatal? Should there
palmer 2012/04/27 23:52:34 The code as it is here is merely moved, not change
+ continue;
+ }
+
+ // Don't fail if this key is not present.
+ parsed->GetDouble(kDynamicSPKIHashesExpiry,
+ &dynamic_spki_hashes_expiry);
+
+ ListValue* pins_list = NULL;
+ FingerprintVector static_spki_hashes;
+ // preloaded_spki_hashes is a legacy synonym for static_spki_hashes.
+ if (parsed->GetList(kStaticSPKIHashes, &pins_list))
+ SPKIHashesFromListValue(*pins_list, &static_spki_hashes);
+ else if (parsed->GetList(kPreloadedSPKIHashes, &pins_list))
+ SPKIHashesFromListValue(*pins_list, &static_spki_hashes);
+
+ FingerprintVector dynamic_spki_hashes;
+ if (parsed->GetList(kDynamicSPKIHashes, &pins_list))
+ SPKIHashesFromListValue(*pins_list, &dynamic_spki_hashes);
+
+ TransportSecurityState::DomainState::UpgradeMode mode;
+ if (mode_string == kForceHTTPS || mode_string == kStrict) {
+ mode = TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
+ } else if (mode_string == kDefault || mode_string == kPinningOnly) {
+ mode = TransportSecurityState::DomainState::MODE_DEFAULT;
+ } 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 (parsed->GetDouble(kCreated, &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;
+ }
+
+ TransportSecurityState::DomainState domain_state;
+ domain_state.upgrade_mode = mode;
+ domain_state.created = created_time;
+ domain_state.upgrade_expiry = expiry_time;
+ domain_state.include_subdomains = include_subdomains;
+ domain_state.static_spki_hashes = static_spki_hashes;
+ domain_state.dynamic_spki_hashes = dynamic_spki_hashes;
+ domain_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time;
+
+ if (forced)
+ state->GetForcedHosts()[hashed] = domain_state;
+ else
+ state->GetEnabledHosts()[hashed] = domain_state;
+ }
+
+ *dirty = dirtied;
+ return true;
+}
+
+bool TransportSecurityPersister::LoadEntries(const std::string& serialized,
+ bool* dirty) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ transport_security_state_->Clear();
+ return Deserialize(serialized, dirty, false, transport_security_state_);
+}
+
void TransportSecurityPersister::CompleteLoad(const std::string& state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
bool dirty = false;
- if (!transport_security_state_->LoadEntries(state, &dirty)) {
+ if (!LoadEntries(state, &dirty)) {
LOG(ERROR) << "Failed to deserialize state: " << state;
return;
}
@@ -93,7 +285,7 @@
}
void TransportSecurityPersister::StateIsDirty(
- net::TransportSecurityState* state) {
+ TransportSecurityState* state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(transport_security_state_, state);
@@ -101,7 +293,51 @@
writer_.ScheduleWrite(this);
}
-bool TransportSecurityPersister::SerializeData(std::string* data) {
+bool TransportSecurityPersister::SerializeData(std::string* output) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- return transport_security_state_->Serialise(data);
+
+ DictionaryValue toplevel;
+ base::Time now = base::Time::Now();
+ TransportSecurityState::Iterator state(*transport_security_state_);
+ for (; state.HasNext(); state.Advance()) {
+ const std::string& hostname = state.hostname();
+ const TransportSecurityState::DomainState& domain_state =
+ state.domain_state();
+
+ DictionaryValue* serialized = new DictionaryValue;
+ serialized->SetBoolean(kIncludeSubdomains,
+ domain_state.include_subdomains);
+ serialized->SetDouble(kCreated, domain_state.created.ToDoubleT());
+ serialized->SetDouble(kExpiry, domain_state.upgrade_expiry.ToDoubleT());
+ serialized->SetDouble(kDynamicSPKIHashesExpiry,
+ domain_state.dynamic_spki_hashes_expiry.ToDoubleT());
+
+ switch (domain_state.upgrade_mode) {
+ case TransportSecurityState::DomainState::MODE_FORCE_HTTPS:
+ serialized->SetString(kMode, kForceHTTPS);
+ break;
+ case TransportSecurityState::DomainState::MODE_DEFAULT:
+ serialized->SetString(kMode, kDefault);
+ break;
+ default:
+ NOTREACHED() << "DomainState with unknown mode";
+ delete serialized;
+ continue;
+ }
+
+ serialized->Set(kStaticSPKIHashes,
+ SPKIHashesToListValue(domain_state.static_spki_hashes));
+
+ if (now < domain_state.dynamic_spki_hashes_expiry) {
+ serialized->Set(kDynamicSPKIHashes,
+ SPKIHashesToListValue(domain_state.dynamic_spki_hashes));
+ }
+
+ toplevel.Set(HashedDomainToExternalString(hostname), serialized);
+ }
+
+ base::JSONWriter::WriteWithOptions(&toplevel,
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ output);
+ return true;
}

Powered by Google App Engine
This is Rietveld 408576698