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

Unified Diff: net/base/transport_security_state.cc

Issue 10826257: Implement SHA-256 fingerprint support (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 3 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
« no previous file with comments | « net/base/transport_security_state.h ('k') | net/base/transport_security_state_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/base/transport_security_state.cc
===================================================================
--- net/base/transport_security_state.cc (revision 155029)
+++ net/base/transport_security_state.cc (working copy)
@@ -16,7 +16,6 @@
#endif
#include <algorithm>
-#include <utility>
#include "base/base64.h"
#include "base/logging.h"
@@ -33,6 +32,7 @@
#include "googleurl/src/gurl.h"
#include "net/base/dns_util.h"
#include "net/base/ssl_info.h"
+#include "net/base/x509_cert_types.h"
#include "net/base/x509_certificate.h"
#include "net/http/http_util.h"
@@ -225,69 +225,69 @@
return pair;
}
-// TODO(palmer): Support both sha256 and sha1. This will require additional
-// infrastructure code changes and can come in a later patch.
-//
// static
bool TransportSecurityState::ParsePin(const std::string& value,
- SHA1Fingerprint* out) {
+ HashValue* out) {
StringPair slash = Split(Strip(value), '/');
- if (slash.first != "sha1")
+
+ if (slash.first == "sha1")
+ out->tag = HASH_VALUE_SHA1;
+ else if (slash.first == "sha256")
+ out->tag = HASH_VALUE_SHA256;
+ else
return false;
std::string decoded;
if (!base::Base64Decode(slash.second, &decoded) ||
- decoded.size() != arraysize(out->data)) {
+ decoded.size() != out->size()) {
return false;
}
- memcpy(out->data, decoded.data(), arraysize(out->data));
+ memcpy(out->data(), decoded.data(), out->size());
return true;
}
static bool ParseAndAppendPin(const std::string& value,
- FingerprintVector* fingerprints) {
- // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28
- // characters; 32 bytes base64'd is 44 characters. TODO(palmer): Support
- // SHA256.
- size_t size = value.size();
- if (size != 30 || value[0] != '"' || value[size - 1] != '"')
- return false;
-
+ HashValueTag tag,
+ HashValueVector* hashes) {
std::string unquoted = HttpUtil::Unquote(value);
std::string decoded;
- SHA1Fingerprint fp;
- if (!base::Base64Decode(unquoted, &decoded) ||
- decoded.size() != arraysize(fp.data)) {
+ // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1.
+ // Currently, those are the only two possibilities, so the assumption is
+ // valid.
+ if (!base::Base64Decode(unquoted, &decoded))
return false;
- }
- memcpy(fp.data, decoded.data(), arraysize(fp.data));
- fingerprints->push_back(fp);
+ HashValue hash(tag);
+ if (decoded.size() != hash.size())
+ return false;
+
+ memcpy(hash.data(), decoded.data(), hash.size());
+ hashes->push_back(hash);
return true;
}
-struct FingerprintsEqualPredicate {
- explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) :
+struct HashValuesEqualPredicate {
+ explicit HashValuesEqualPredicate(const HashValue& fingerprint) :
fingerprint_(fingerprint) {}
- bool operator()(const SHA1Fingerprint& other) const {
+ bool operator()(const HashValue& other) const {
return fingerprint_.Equals(other);
}
- const SHA1Fingerprint& fingerprint_;
+ const HashValue& fingerprint_;
};
// Returns true iff there is an item in |pins| which is not present in
// |from_cert_chain|. Such an SPKI hash is called a "backup pin".
-static bool IsBackupPinPresent(const FingerprintVector& pins,
- const FingerprintVector& from_cert_chain) {
- for (FingerprintVector::const_iterator
+static bool IsBackupPinPresent(const HashValueVector& pins,
+ const HashValueVector& from_cert_chain) {
+ for (HashValueVector::const_iterator
i = pins.begin(); i != pins.end(); ++i) {
- FingerprintVector::const_iterator j =
+ HashValueVector::const_iterator j =
std::find_if(from_cert_chain.begin(), from_cert_chain.end(),
- FingerprintsEqualPredicate(*i));
+ HashValuesEqualPredicate(*i));
if (j == from_cert_chain.end())
return true;
}
@@ -295,14 +295,15 @@
return false;
}
-static bool HashesIntersect(const FingerprintVector& a,
- const FingerprintVector& b) {
- for (FingerprintVector::const_iterator
- i = a.begin(); i != a.end(); ++i) {
- FingerprintVector::const_iterator j =
- std::find_if(b.begin(), b.end(), FingerprintsEqualPredicate(*i));
- if (j != b.end())
- return true;
+// Returns true if the intersection of |a| and |b| is not empty. If either
+// |a| or |b| is empty, returns false.
+static bool HashesIntersect(const HashValueVector& a,
+ const HashValueVector& b) {
+ for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) {
+ HashValueVector::const_iterator j =
+ std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i));
+ if (j != b.end())
+ return true;
}
return false;
@@ -313,17 +314,19 @@
// backup pin is a pin intended for disaster recovery, not day-to-day use, and
// thus must be absent from the certificate chain. The Public-Key-Pins header
// specification requires both.
-static bool IsPinListValid(const FingerprintVector& pins,
+static bool IsPinListValid(const HashValueVector& pins,
const SSLInfo& ssl_info) {
+ // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual
+ // liveness and backupness below.)
if (pins.size() < 2)
return false;
- const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes;
+ const HashValueVector& from_cert_chain = ssl_info.public_key_hashes;
if (from_cert_chain.empty())
return false;
return IsBackupPinPresent(pins, from_cert_chain) &&
- HashesIntersect(pins, from_cert_chain);
+ HashesIntersect(pins, from_cert_chain);
}
// "Public-Key-Pins" ":"
@@ -335,7 +338,7 @@
const SSLInfo& ssl_info) {
bool parsed_max_age = false;
int max_age_candidate = 0;
- FingerprintVector pins;
+ HashValueVector pins;
std::string source = value;
@@ -356,11 +359,18 @@
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))
+ } else if (StartsWithASCII(equals.first, "pin-", false)) {
+ HashValueTag tag;
+ if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) {
+ tag = HASH_VALUE_SHA1;
+ } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) {
+ tag = HASH_VALUE_SHA256;
+ } else {
+ LOG(WARNING) << "Ignoring pin of unknown type: " << equals.first;
return false;
- } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) {
- // TODO(palmer)
+ }
+ if (!ParseAndAppendPin(equals.second, tag, &pins))
+ return false;
} else {
// Silently ignore unknown directives for forward compatibility.
}
@@ -376,7 +386,7 @@
dynamic_spki_hashes.clear();
if (max_age_candidate > 0) {
- for (FingerprintVector::const_iterator i = pins.begin();
+ for (HashValueVector::const_iterator i = pins.begin();
i != pins.end(); ++i) {
dynamic_spki_hashes.push_back(*i);
}
@@ -483,8 +493,8 @@
}
static bool AddHash(const std::string& type_and_base64,
- FingerprintVector* out) {
- SHA1Fingerprint hash;
+ HashValueVector* out) {
+ HashValue hash;
if (!TransportSecurityState::ParsePin(type_and_base64, &hash))
return false;
@@ -705,6 +715,21 @@
entry->second_level_domain_name, DOMAIN_NUM_EVENTS);
}
+// static
+const char* TransportSecurityState::HashValueLabel(
+ const HashValue& hash_value) {
+ switch (hash_value.tag) {
+ case HASH_VALUE_SHA1:
+ return "sha1/";
+ case HASH_VALUE_SHA256:
+ return "sha256/";
+ default:
+ NOTREACHED();
+ LOG(WARNING) << "Invalid fingerprint of unknown type " << hash_value.tag;
+ return "unknown/";
+ }
+}
+
bool TransportSecurityState::GetStaticDomainState(
const std::string& canonicalized_host,
bool sni_enabled,
@@ -750,13 +775,13 @@
}
static std::string HashesToBase64String(
- const FingerprintVector& hashes) {
+ const HashValueVector& hashes) {
std::vector<std::string> hashes_strs;
- for (FingerprintVector::const_iterator
+ for (HashValueVector::const_iterator
i = hashes.begin(); i != hashes.end(); i++) {
std::string s;
- const std::string hash_str(reinterpret_cast<const char*>(i->data),
- sizeof(i->data));
+ const std::string hash_str(reinterpret_cast<const char*>(i->data()),
+ i->size());
base::Base64Encode(hash_str, &s);
hashes_strs.push_back(s);
}
@@ -774,7 +799,16 @@
}
bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted(
- const FingerprintVector& hashes) const {
+ const HashValueVector& hashes) const {
+ // Validate that hashes is not empty. By the time this code is called (in
+ // production), that should never happen, but it's good to be defensive.
+ // And, hashes *can* be empty in some test scenarios.
+ if (hashes.empty()) {
+ LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned "
+ "domain " << domain;
+ return false;
+ }
+
if (HashesIntersect(bad_static_spki_hashes, hashes)) {
LOG(ERROR) << "Rejecting public key chain for domain " << domain
<< ". Validated chain: " << HashesToBase64String(hashes)
@@ -783,18 +817,20 @@
return false;
}
- if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) &&
- !HashesIntersect(dynamic_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(static_spki_hashes);
+ // If there are no pins, then any valid chain is acceptable.
+ if (dynamic_spki_hashes.empty() && static_spki_hashes.empty())
+ return true;
- return false;
+ if (HashesIntersect(dynamic_spki_hashes, hashes) ||
+ HashesIntersect(static_spki_hashes, hashes)) {
+ return true;
}
- return true;
+ LOG(ERROR) << "Rejecting public key chain for domain " << domain
+ << ". Validated chain: " << HashesToBase64String(hashes)
+ << ", expected: " << HashesToBase64String(dynamic_spki_hashes)
+ << " or: " << HashesToBase64String(static_spki_hashes);
+ return false;
}
bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const {
« no previous file with comments | « net/base/transport_security_state.h ('k') | net/base/transport_security_state_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698