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

Side by Side Diff: net/base/transport_security_state.cc

Issue 10836150: Revert 150375 - Implement SHA-256 fingerprint support (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 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 unified diff | Download patch | Annotate | Revision Log
« 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/base/transport_security_state.h" 5 #include "net/base/transport_security_state.h"
6 6
7 #if defined(USE_OPENSSL) 7 #if defined(USE_OPENSSL)
8 #include <openssl/ecdsa.h> 8 #include <openssl/ecdsa.h>
9 #include <openssl/ssl.h> 9 #include <openssl/ssl.h>
10 #else // !defined(USE_OPENSSL) 10 #else // !defined(USE_OPENSSL)
(...skipping 15 matching lines...) Expand all
26 #include "base/string_number_conversions.h" 26 #include "base/string_number_conversions.h"
27 #include "base/string_tokenizer.h" 27 #include "base/string_tokenizer.h"
28 #include "base/string_util.h" 28 #include "base/string_util.h"
29 #include "base/time.h" 29 #include "base/time.h"
30 #include "base/utf_string_conversions.h" 30 #include "base/utf_string_conversions.h"
31 #include "base/values.h" 31 #include "base/values.h"
32 #include "crypto/sha2.h" 32 #include "crypto/sha2.h"
33 #include "googleurl/src/gurl.h" 33 #include "googleurl/src/gurl.h"
34 #include "net/base/dns_util.h" 34 #include "net/base/dns_util.h"
35 #include "net/base/ssl_info.h" 35 #include "net/base/ssl_info.h"
36 #include "net/base/x509_cert_types.h"
37 #include "net/base/x509_certificate.h" 36 #include "net/base/x509_certificate.h"
38 #include "net/http/http_util.h" 37 #include "net/http/http_util.h"
39 38
40 #if defined(USE_OPENSSL) 39 #if defined(USE_OPENSSL)
41 #include "crypto/openssl_util.h" 40 #include "crypto/openssl_util.h"
42 #endif 41 #endif
43 42
44 namespace net { 43 namespace net {
45 44
46 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year 45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 StringPair pair; 211 StringPair pair;
213 size_t point = source.find(delimiter); 212 size_t point = source.find(delimiter);
214 213
215 pair.first = source.substr(0, point); 214 pair.first = source.substr(0, point);
216 if (std::string::npos != point) 215 if (std::string::npos != point)
217 pair.second = source.substr(point + 1); 216 pair.second = source.substr(point + 1);
218 217
219 return pair; 218 return pair;
220 } 219 }
221 220
221 // TODO(palmer): Support both sha256 and sha1. This will require additional
222 // infrastructure code changes and can come in a later patch.
223 //
222 // static 224 // static
223 bool TransportSecurityState::ParsePin(const std::string& value, 225 bool TransportSecurityState::ParsePin(const std::string& value,
224 HashValue* out) { 226 SHA1Fingerprint* out) {
225 StringPair slash = Split(Strip(value), '/'); 227 StringPair slash = Split(Strip(value), '/');
226 228 if (slash.first != "sha1")
227 if (slash.first == "sha1")
228 out->tag = HASH_VALUE_SHA1;
229 else if (slash.first == "sha256")
230 out->tag = HASH_VALUE_SHA256;
231 else
232 return false; 229 return false;
233 230
234 std::string decoded; 231 std::string decoded;
235 if (!base::Base64Decode(slash.second, &decoded) || 232 if (!base::Base64Decode(slash.second, &decoded) ||
236 decoded.size() != out->size()) { 233 decoded.size() != arraysize(out->data)) {
237 return false; 234 return false;
238 } 235 }
239 236
240 memcpy(out->data(), decoded.data(), out->size()); 237 memcpy(out->data, decoded.data(), arraysize(out->data));
241 return true; 238 return true;
242 } 239 }
243 240
244 static bool ParseAndAppendPin(const std::string& value, 241 static bool ParseAndAppendPin(const std::string& value,
245 HashValueVector* fingerprints) { 242 FingerprintVector* fingerprints) {
246 // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28 243 // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28
247 // characters; 32 bytes base64'd is 44 characters. 244 // characters; 32 bytes base64'd is 44 characters. TODO(palmer): Support
245 // SHA256.
248 size_t size = value.size(); 246 size_t size = value.size();
249 if ((size != 30 && size != 46) || value[0] != '"' || value[size - 1] != '"') 247 if (size != 30 || value[0] != '"' || value[size - 1] != '"')
250 return false; 248 return false;
251 249
252 std::string unquoted = HttpUtil::Unquote(value); 250 std::string unquoted = HttpUtil::Unquote(value);
253 std::string decoded; 251 std::string decoded;
254 HashValue fp; 252 SHA1Fingerprint fp;
255 253
256 // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1. 254 if (!base::Base64Decode(unquoted, &decoded) ||
257 // Currently, those are the only two possibilities, so the assumption is 255 decoded.size() != arraysize(fp.data)) {
258 // valid.
259 if (!base::Base64Decode(unquoted, &decoded))
260 return false; 256 return false;
257 }
261 258
262 if (decoded.size() == 20) 259 memcpy(fp.data, decoded.data(), arraysize(fp.data));
263 fp.tag = HASH_VALUE_SHA1;
264 else if (decoded.size() == 32)
265 fp.tag = HASH_VALUE_SHA256;
266 else
267 return false;
268
269 memcpy(fp.data(), decoded.data(), fp.size());
270 fingerprints->push_back(fp); 260 fingerprints->push_back(fp);
271 return true; 261 return true;
272 } 262 }
273 263
274 struct HashValuesEqualPredicate { 264 struct FingerprintsEqualPredicate {
275 explicit HashValuesEqualPredicate(const HashValue& fingerprint) : 265 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) :
276 fingerprint_(fingerprint) {} 266 fingerprint_(fingerprint) {}
277 267
278 bool operator()(const HashValue& other) const { 268 bool operator()(const SHA1Fingerprint& other) const {
279 return fingerprint_.Equals(other); 269 return fingerprint_.Equals(other);
280 } 270 }
281 271
282 const HashValue& fingerprint_; 272 const SHA1Fingerprint& fingerprint_;
283 }; 273 };
284 274
285 // Returns true iff there is an item in |pins| which is not present in 275 // Returns true iff there is an item in |pins| which is not present in
286 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". 276 // |from_cert_chain|. Such an SPKI hash is called a "backup pin".
287 static bool IsBackupPinPresent(const HashValueVector& pins, 277 static bool IsBackupPinPresent(const FingerprintVector& pins,
288 const HashValueVector& from_cert_chain) { 278 const FingerprintVector& from_cert_chain) {
289 for (HashValueVector::const_iterator 279 for (FingerprintVector::const_iterator
290 i = pins.begin(); i != pins.end(); ++i) { 280 i = pins.begin(); i != pins.end(); ++i) {
291 HashValueVector::const_iterator j = 281 FingerprintVector::const_iterator j =
292 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), 282 std::find_if(from_cert_chain.begin(), from_cert_chain.end(),
293 HashValuesEqualPredicate(*i)); 283 FingerprintsEqualPredicate(*i));
294 if (j == from_cert_chain.end()) 284 if (j == from_cert_chain.end())
295 return true; 285 return true;
296 } 286 }
297 287
298 return false; 288 return false;
299 } 289 }
300 290
301 static bool HashesIntersect(const HashValueVector& a, 291 static bool HashesIntersect(const FingerprintVector& a,
302 const HashValueVector& b) { 292 const FingerprintVector& b) {
303 for (HashValueVector::const_iterator 293 for (FingerprintVector::const_iterator
304 i = a.begin(); i != a.end(); ++i) { 294 i = a.begin(); i != a.end(); ++i) {
305 HashValueVector::const_iterator j = 295 FingerprintVector::const_iterator j =
306 std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i)); 296 std::find_if(b.begin(), b.end(), FingerprintsEqualPredicate(*i));
307 if (j != b.end()) 297 if (j != b.end())
308 return true; 298 return true;
309 } 299 }
310 300
311 return false; 301 return false;
312 } 302 }
313 303
314 // Returns true iff |pins| contains both a live and a backup pin. A live pin 304 // Returns true iff |pins| contains both a live and a backup pin. A live pin
315 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A 305 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A
316 // backup pin is a pin intended for disaster recovery, not day-to-day use, and 306 // backup pin is a pin intended for disaster recovery, not day-to-day use, and
317 // thus must be absent from the certificate chain. The Public-Key-Pins header 307 // thus must be absent from the certificate chain. The Public-Key-Pins header
318 // specification requires both. 308 // specification requires both.
319 static bool IsPinListValid(const HashValueVector& pins, 309 static bool IsPinListValid(const FingerprintVector& pins,
320 const SSLInfo& ssl_info) { 310 const SSLInfo& ssl_info) {
321 // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual
322 // liveness and backupness below.)
323 if (pins.size() < 2) 311 if (pins.size() < 2)
324 return false; 312 return false;
325 313
326 // Site operators might pin a key using either the SHA-1 or SHA-256 hash 314 const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes;
327 // of the SPKI. So check for success using either hash function. 315 if (from_cert_chain.empty())
328 //
329 // TODO(palmer): Make this generic so that it works regardless of what
330 // HashValueTags are defined in the future.
331 const HashValueVector& from_cert_chain_sha1 =
332 ssl_info.public_key_hashes[HASH_VALUE_SHA1];
333 const HashValueVector& from_cert_chain_sha256 =
334 ssl_info.public_key_hashes[HASH_VALUE_SHA256];
335
336 if (from_cert_chain_sha1.empty() && from_cert_chain_sha256.empty())
337 return false; 316 return false;
338 317
339 return (IsBackupPinPresent(pins, from_cert_chain_sha1) || 318 return IsBackupPinPresent(pins, from_cert_chain) &&
340 IsBackupPinPresent(pins, from_cert_chain_sha256)) && 319 HashesIntersect(pins, from_cert_chain);
341 (HashesIntersect(pins, from_cert_chain_sha1) ||
342 HashesIntersect(pins, from_cert_chain_sha256));
343 } 320 }
344 321
345 // "Public-Key-Pins" ":" 322 // "Public-Key-Pins" ":"
346 // "max-age" "=" delta-seconds ";" 323 // "max-age" "=" delta-seconds ";"
347 // "pin-" algo "=" base64 [ ";" ... ] 324 // "pin-" algo "=" base64 [ ";" ... ]
348 bool TransportSecurityState::DomainState::ParsePinsHeader( 325 bool TransportSecurityState::DomainState::ParsePinsHeader(
349 const base::Time& now, 326 const base::Time& now,
350 const std::string& value, 327 const std::string& value,
351 const SSLInfo& ssl_info) { 328 const SSLInfo& ssl_info) {
352 bool parsed_max_age = false; 329 bool parsed_max_age = false;
353 int max_age_candidate = 0; 330 int max_age_candidate = 0;
354 HashValueVector pins; 331 FingerprintVector pins;
355 332
356 std::string source = value; 333 std::string source = value;
357 334
358 while (!source.empty()) { 335 while (!source.empty()) {
359 StringPair semicolon = Split(source, ';'); 336 StringPair semicolon = Split(source, ';');
360 semicolon.first = Strip(semicolon.first); 337 semicolon.first = Strip(semicolon.first);
361 semicolon.second = Strip(semicolon.second); 338 semicolon.second = Strip(semicolon.second);
362 StringPair equals = Split(semicolon.first, '='); 339 StringPair equals = Split(semicolon.first, '=');
363 equals.first = Strip(equals.first); 340 equals.first = Strip(equals.first);
364 equals.second = Strip(equals.second); 341 equals.second = Strip(equals.second);
365 342
366 if (LowerCaseEqualsASCII(equals.first, "max-age")) { 343 if (LowerCaseEqualsASCII(equals.first, "max-age")) {
367 if (equals.second.empty() || 344 if (equals.second.empty() ||
368 !MaxAgeToInt(equals.second.begin(), equals.second.end(), 345 !MaxAgeToInt(equals.second.begin(), equals.second.end(),
369 &max_age_candidate)) { 346 &max_age_candidate)) {
370 return false; 347 return false;
371 } 348 }
372 if (max_age_candidate > kMaxHSTSAgeSecs) 349 if (max_age_candidate > kMaxHSTSAgeSecs)
373 max_age_candidate = kMaxHSTSAgeSecs; 350 max_age_candidate = kMaxHSTSAgeSecs;
374 parsed_max_age = true; 351 parsed_max_age = true;
375 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1") || 352 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) {
376 LowerCaseEqualsASCII(equals.first, "pin-sha256")) {
377 if (!ParseAndAppendPin(equals.second, &pins)) 353 if (!ParseAndAppendPin(equals.second, &pins))
378 return false; 354 return false;
355 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) {
356 // TODO(palmer)
379 } else { 357 } else {
380 // Silently ignore unknown directives for forward compatibility. 358 // Silently ignore unknown directives for forward compatibility.
381 } 359 }
382 360
383 source = semicolon.second; 361 source = semicolon.second;
384 } 362 }
385 363
386 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) 364 if (!parsed_max_age || !IsPinListValid(pins, ssl_info))
387 return false; 365 return false;
388 366
389 dynamic_spki_hashes_expiry = 367 dynamic_spki_hashes_expiry =
390 now + base::TimeDelta::FromSeconds(max_age_candidate); 368 now + base::TimeDelta::FromSeconds(max_age_candidate);
391 369
392 dynamic_spki_hashes.clear(); 370 dynamic_spki_hashes.clear();
393 if (max_age_candidate > 0) { 371 if (max_age_candidate > 0) {
394 for (HashValueVector::const_iterator i = pins.begin(); 372 for (FingerprintVector::const_iterator i = pins.begin();
395 i != pins.end(); ++i) { 373 i != pins.end(); ++i) {
396 dynamic_spki_hashes.push_back(*i); 374 dynamic_spki_hashes.push_back(*i);
397 } 375 }
398 } 376 }
399 377
400 return true; 378 return true;
401 } 379 }
402 380
403 // "Strict-Transport-Security" ":" 381 // "Strict-Transport-Security" ":"
404 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] 382 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ]
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
491 include_subdomains = true; 469 include_subdomains = true;
492 upgrade_mode = MODE_FORCE_HTTPS; 470 upgrade_mode = MODE_FORCE_HTTPS;
493 return true; 471 return true;
494 default: 472 default:
495 NOTREACHED(); 473 NOTREACHED();
496 return false; 474 return false;
497 } 475 }
498 } 476 }
499 477
500 static bool AddHash(const std::string& type_and_base64, 478 static bool AddHash(const std::string& type_and_base64,
501 HashValueVector* out) { 479 FingerprintVector* out) {
502 HashValue hash; 480 SHA1Fingerprint hash;
503 481
504 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) 482 if (!TransportSecurityState::ParsePin(type_and_base64, &hash))
505 return false; 483 return false;
506 484
507 out->push_back(hash); 485 out->push_back(hash);
508 return true; 486 return true;
509 } 487 }
510 488
511 TransportSecurityState::~TransportSecurityState() {} 489 TransportSecurityState::~TransportSecurityState() {}
512 490
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 const std::string& hashed_host, const DomainState& state) { 736 const std::string& hashed_host, const DomainState& state) {
759 enabled_hosts_[hashed_host] = state; 737 enabled_hosts_[hashed_host] = state;
760 } 738 }
761 739
762 void TransportSecurityState::AddOrUpdateForcedHosts( 740 void TransportSecurityState::AddOrUpdateForcedHosts(
763 const std::string& hashed_host, const DomainState& state) { 741 const std::string& hashed_host, const DomainState& state) {
764 forced_hosts_[hashed_host] = state; 742 forced_hosts_[hashed_host] = state;
765 } 743 }
766 744
767 static std::string HashesToBase64String( 745 static std::string HashesToBase64String(
768 const HashValueVector& hashes) { 746 const FingerprintVector& hashes) {
769 std::vector<std::string> hashes_strs; 747 std::vector<std::string> hashes_strs;
770 for (HashValueVector::const_iterator 748 for (FingerprintVector::const_iterator
771 i = hashes.begin(); i != hashes.end(); i++) { 749 i = hashes.begin(); i != hashes.end(); i++) {
772 std::string s; 750 std::string s;
773 const std::string hash_str(reinterpret_cast<const char*>(i->data()), 751 const std::string hash_str(reinterpret_cast<const char*>(i->data),
774 i->size()); 752 sizeof(i->data));
775 base::Base64Encode(hash_str, &s); 753 base::Base64Encode(hash_str, &s);
776 hashes_strs.push_back(s); 754 hashes_strs.push_back(s);
777 } 755 }
778 756
779 return JoinString(hashes_strs, ','); 757 return JoinString(hashes_strs, ',');
780 } 758 }
781 759
782 TransportSecurityState::DomainState::DomainState() 760 TransportSecurityState::DomainState::DomainState()
783 : upgrade_mode(MODE_FORCE_HTTPS), 761 : upgrade_mode(MODE_FORCE_HTTPS),
784 created(base::Time::Now()), 762 created(base::Time::Now()),
785 include_subdomains(false) { 763 include_subdomains(false) {
786 } 764 }
787 765
788 TransportSecurityState::DomainState::~DomainState() { 766 TransportSecurityState::DomainState::~DomainState() {
789 } 767 }
790 768
791 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( 769 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted(
792 const std::vector<HashValueVector>& hashes) const { 770 const FingerprintVector& hashes) const {
793 // Validate that hashes is not empty. By the time this code is called (in 771 if (HashesIntersect(bad_static_spki_hashes, hashes)) {
794 // production), that should never happen, but it's good to be defensive. 772 LOG(ERROR) << "Rejecting public key chain for domain " << domain
795 // And, hashes *can* be empty in some test scenarios. 773 << ". Validated chain: " << HashesToBase64String(hashes)
796 bool empty = true; 774 << ", matches one or more bad hashes: "
797 for (size_t i = 0; i < hashes.size(); ++i) { 775 << HashesToBase64String(bad_static_spki_hashes);
798 if (hashes[i].size()) {
799 empty = false;
800 break;
801 }
802 }
803 if (empty) {
804 LOG(ERROR) << "Rejecting empty certificate chain for public key pinned "
805 "domain " << domain;
806 return false; 776 return false;
807 } 777 }
808 778
809 for (size_t i = 0; i < hashes.size(); ++i) { 779 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) &&
810 if (HashesIntersect(bad_static_spki_hashes, hashes[i])) { 780 !HashesIntersect(dynamic_spki_hashes, hashes) &&
811 LOG(ERROR) << "Rejecting public key chain for domain " << domain 781 !HashesIntersect(static_spki_hashes, hashes)) {
812 << ". Validated chain: " << HashesToBase64String(hashes[i]) 782 LOG(ERROR) << "Rejecting public key chain for domain " << domain
813 << ", matches one or more bad hashes: " 783 << ". Validated chain: " << HashesToBase64String(hashes)
814 << HashesToBase64String(bad_static_spki_hashes); 784 << ", expected: " << HashesToBase64String(dynamic_spki_hashes)
815 return false; 785 << " or: " << HashesToBase64String(static_spki_hashes);
816 }
817 786
818 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && 787 return false;
819 hashes[i].size() > 0 &&
820 !HashesIntersect(dynamic_spki_hashes, hashes[i]) &&
821 !HashesIntersect(static_spki_hashes, hashes[i])) {
822 LOG(ERROR) << "Rejecting public key chain for domain " << domain
823 << ". Validated chain: " << HashesToBase64String(hashes[i])
824 << ", expected: " << HashesToBase64String(dynamic_spki_hashes)
825 << " or: " << HashesToBase64String(static_spki_hashes);
826
827 return false;
828 }
829 } 788 }
830 789
831 return true; 790 return true;
832 } 791 }
833 792
834 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { 793 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const {
835 return upgrade_mode == MODE_FORCE_HTTPS; 794 return upgrade_mode == MODE_FORCE_HTTPS;
836 } 795 }
837 796
838 bool TransportSecurityState::DomainState::Equals( 797 bool TransportSecurityState::DomainState::Equals(
839 const DomainState& other) const { 798 const DomainState& other) const {
840 // TODO(palmer): Implement this 799 // TODO(palmer): Implement this
841 (void) other; 800 (void) other;
842 return true; 801 return true;
843 } 802 }
844 803
845 bool TransportSecurityState::DomainState::HasPins() const { 804 bool TransportSecurityState::DomainState::HasPins() const {
846 return static_spki_hashes.size() > 0 || 805 return static_spki_hashes.size() > 0 ||
847 bad_static_spki_hashes.size() > 0 || 806 bad_static_spki_hashes.size() > 0 ||
848 dynamic_spki_hashes.size() > 0; 807 dynamic_spki_hashes.size() > 0;
849 } 808 }
850 809
851 } // namespace 810 } // namespace
OLDNEW
« 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