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

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 134551)
+++ chrome/browser/transport_security_persister.cc (working copy)
@@ -4,17 +4,90 @@
#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;
+namespace {
+
+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
+
class TransportSecurityPersister::Loader {
public:
Loader(const base::WeakPtr<TransportSecurityPersister>& persister,
@@ -52,7 +125,7 @@
};
TransportSecurityPersister::TransportSecurityPersister(
- net::TransportSecurityState* state,
+ TransportSecurityState* state,
const FilePath& profile_path,
bool readonly)
: transport_security_state_(state),
@@ -80,28 +153,196 @@
transport_security_state_->SetDelegate(NULL);
}
-void TransportSecurityPersister::CompleteLoad(const std::string& state) {
+void TransportSecurityPersister::StateIsDirty(
+ TransportSecurityState* state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_EQ(transport_security_state_, state);
- bool dirty = false;
- if (!transport_security_state_->LoadEntries(state, &dirty)) {
- LOG(ERROR) << "Failed to deserialize state: " << state;
- return;
+ if (!readonly_)
+ writer_.ScheduleWrite(this);
+}
+
+bool TransportSecurityPersister::SerializeData(std::string* output) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ 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);
}
- if (dirty)
- StateIsDirty(transport_security_state_);
+
+ base::JSONWriter::WriteWithOptions(&toplevel,
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ output);
+ return true;
}
-void TransportSecurityPersister::StateIsDirty(
- net::TransportSecurityState* state) {
+bool TransportSecurityPersister::DeserializeFromCommandLine(
+ const std::string& serialized) {
+ // Purposefully ignore |dirty| because we do not want to persist entries
+ // deserialized in this way.
+ bool dirty;
+ return Deserialize(serialized, true, &dirty, transport_security_state_);
+}
+
+bool TransportSecurityPersister::LoadEntries(const std::string& serialized,
+ bool* dirty) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK_EQ(transport_security_state_, state);
- if (!readonly_)
- writer_.ScheduleWrite(this);
+ transport_security_state_->Clear();
+ return Deserialize(serialized, false, dirty, transport_security_state_);
}
-bool TransportSecurityPersister::SerializeData(std::string* data) {
+// static
+bool TransportSecurityPersister::Deserialize(const std::string& serialized,
+ bool forced,
+ bool* dirty,
+ 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)) {
+ LOG(WARNING) << "Could not parse entry " << *i << "; skipping entry";
+ 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)) {
+ LOG(WARNING) << "Could not parse some elements of entry " << *i
+ << "; skipping entry";
+ 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 "
+ << mode_string << " found for entry " << *i
+ << "; skipping entry";
+ 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->AddOrUpdateForcedHosts(hashed, domain_state);
+ else
+ state->AddOrUpdateEnabledHosts(hashed, domain_state);
+ }
+
+ *dirty = dirtied;
+ return true;
+}
+
+void TransportSecurityPersister::CompleteLoad(const std::string& state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- return transport_security_state_->Serialise(data);
+
+ bool dirty = false;
+ if (!LoadEntries(state, &dirty)) {
+ LOG(ERROR) << "Failed to deserialize state: " << state;
+ return;
+ }
+ if (dirty)
+ StateIsDirty(transport_security_state_);
}
« no previous file with comments | « chrome/browser/transport_security_persister.h ('k') | chrome/browser/transport_security_persister_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698