| OLD | NEW |
| 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) |
| 11 #include <cryptohi.h> | 11 #include <cryptohi.h> |
| 12 #include <hasht.h> | 12 #include <hasht.h> |
| 13 #include <keyhi.h> | 13 #include <keyhi.h> |
| 14 #include <pk11pub.h> | 14 #include <pk11pub.h> |
| 15 #include <nspr.h> | 15 #include <nspr.h> |
| 16 #endif | 16 #endif |
| 17 | 17 |
| 18 #include <algorithm> | 18 #include <algorithm> |
| 19 #include <utility> | |
| 20 | 19 |
| 21 #include "base/base64.h" | 20 #include "base/base64.h" |
| 22 #include "base/logging.h" | 21 #include "base/logging.h" |
| 23 #include "base/memory/scoped_ptr.h" | 22 #include "base/memory/scoped_ptr.h" |
| 24 #include "base/metrics/histogram.h" | 23 #include "base/metrics/histogram.h" |
| 25 #include "base/sha1.h" | 24 #include "base/sha1.h" |
| 26 #include "base/string_number_conversions.h" | 25 #include "base/string_number_conversions.h" |
| 27 #include "base/string_tokenizer.h" | 26 #include "base/string_tokenizer.h" |
| 28 #include "base/string_util.h" | 27 #include "base/string_util.h" |
| 29 #include "base/time.h" | 28 #include "base/time.h" |
| 30 #include "base/utf_string_conversions.h" | 29 #include "base/utf_string_conversions.h" |
| 31 #include "base/values.h" | 30 #include "base/values.h" |
| 32 #include "crypto/sha2.h" | 31 #include "crypto/sha2.h" |
| 33 #include "googleurl/src/gurl.h" | 32 #include "googleurl/src/gurl.h" |
| 34 #include "net/base/dns_util.h" | 33 #include "net/base/dns_util.h" |
| 35 #include "net/base/ssl_info.h" | 34 #include "net/base/ssl_info.h" |
| 35 #include "net/base/x509_cert_types.h" |
| 36 #include "net/base/x509_certificate.h" | 36 #include "net/base/x509_certificate.h" |
| 37 #include "net/http/http_util.h" | 37 #include "net/http/http_util.h" |
| 38 | 38 |
| 39 #if defined(USE_OPENSSL) | 39 #if defined(USE_OPENSSL) |
| 40 #include "crypto/openssl_util.h" | 40 #include "crypto/openssl_util.h" |
| 41 #endif | 41 #endif |
| 42 | 42 |
| 43 namespace net { | 43 namespace net { |
| 44 | 44 |
| 45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | 45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 StringPair pair; | 218 StringPair pair; |
| 219 size_t point = source.find(delimiter); | 219 size_t point = source.find(delimiter); |
| 220 | 220 |
| 221 pair.first = source.substr(0, point); | 221 pair.first = source.substr(0, point); |
| 222 if (std::string::npos != point) | 222 if (std::string::npos != point) |
| 223 pair.second = source.substr(point + 1); | 223 pair.second = source.substr(point + 1); |
| 224 | 224 |
| 225 return pair; | 225 return pair; |
| 226 } | 226 } |
| 227 | 227 |
| 228 // TODO(palmer): Support both sha256 and sha1. This will require additional | |
| 229 // infrastructure code changes and can come in a later patch. | |
| 230 // | |
| 231 // static | 228 // static |
| 232 bool TransportSecurityState::ParsePin(const std::string& value, | 229 bool TransportSecurityState::ParsePin(const std::string& value, |
| 233 SHA1Fingerprint* out) { | 230 HashValue* out) { |
| 234 StringPair slash = Split(Strip(value), '/'); | 231 StringPair slash = Split(Strip(value), '/'); |
| 235 if (slash.first != "sha1") | 232 |
| 233 if (slash.first == "sha1") |
| 234 out->tag = HASH_VALUE_SHA1; |
| 235 else if (slash.first == "sha256") |
| 236 out->tag = HASH_VALUE_SHA256; |
| 237 else |
| 236 return false; | 238 return false; |
| 237 | 239 |
| 238 std::string decoded; | 240 std::string decoded; |
| 239 if (!base::Base64Decode(slash.second, &decoded) || | 241 if (!base::Base64Decode(slash.second, &decoded) || |
| 240 decoded.size() != arraysize(out->data)) { | 242 decoded.size() != out->size()) { |
| 241 return false; | 243 return false; |
| 242 } | 244 } |
| 243 | 245 |
| 244 memcpy(out->data, decoded.data(), arraysize(out->data)); | 246 memcpy(out->data(), decoded.data(), out->size()); |
| 245 return true; | 247 return true; |
| 246 } | 248 } |
| 247 | 249 |
| 248 static bool ParseAndAppendPin(const std::string& value, | 250 static bool ParseAndAppendPin(const std::string& value, |
| 249 FingerprintVector* fingerprints) { | 251 HashValueVector* fingerprints) { |
| 250 // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28 | 252 std::string unquoted = HttpUtil::Unquote(value); |
| 251 // characters; 32 bytes base64'd is 44 characters. TODO(palmer): Support | 253 std::string decoded; |
| 252 // SHA256. | 254 HashValue fp; |
| 253 size_t size = value.size(); | 255 |
| 254 if (size != 30 || value[0] != '"' || value[size - 1] != '"') | 256 // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1. |
| 257 // Currently, those are the only two possibilities, so the assumption is |
| 258 // valid. |
| 259 if (!base::Base64Decode(unquoted, &decoded)) |
| 255 return false; | 260 return false; |
| 256 | 261 |
| 257 std::string unquoted = HttpUtil::Unquote(value); | 262 if (decoded.size() == base::kSHA1Length) |
| 258 std::string decoded; | 263 fp.tag = HASH_VALUE_SHA1; |
| 259 SHA1Fingerprint fp; | 264 else if (decoded.size() == crypto::kSHA256Length) |
| 265 fp.tag = HASH_VALUE_SHA256; |
| 266 else |
| 267 return false; |
| 260 | 268 |
| 261 if (!base::Base64Decode(unquoted, &decoded) || | 269 memcpy(fp.data(), decoded.data(), fp.size()); |
| 262 decoded.size() != arraysize(fp.data)) { | |
| 263 return false; | |
| 264 } | |
| 265 | |
| 266 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | |
| 267 fingerprints->push_back(fp); | 270 fingerprints->push_back(fp); |
| 268 return true; | 271 return true; |
| 269 } | 272 } |
| 270 | 273 |
| 271 struct FingerprintsEqualPredicate { | 274 struct HashValuesEqualPredicate { |
| 272 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 275 explicit HashValuesEqualPredicate(const HashValue& fingerprint) : |
| 273 fingerprint_(fingerprint) {} | 276 fingerprint_(fingerprint) {} |
| 274 | 277 |
| 275 bool operator()(const SHA1Fingerprint& other) const { | 278 bool operator()(const HashValue& other) const { |
| 276 return fingerprint_.Equals(other); | 279 return fingerprint_.Equals(other); |
| 277 } | 280 } |
| 278 | 281 |
| 279 const SHA1Fingerprint& fingerprint_; | 282 const HashValue& fingerprint_; |
| 280 }; | 283 }; |
| 281 | 284 |
| 282 // Returns true iff there is an item in |pins| which is not present in | 285 // Returns true iff there is an item in |pins| which is not present in |
| 283 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". | 286 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". |
| 284 static bool IsBackupPinPresent(const FingerprintVector& pins, | 287 static bool IsBackupPinPresent(const HashValueVector& pins, |
| 285 const FingerprintVector& from_cert_chain) { | 288 const HashValueVector& from_cert_chain) { |
| 286 for (FingerprintVector::const_iterator | 289 for (HashValueVector::const_iterator |
| 287 i = pins.begin(); i != pins.end(); ++i) { | 290 i = pins.begin(); i != pins.end(); ++i) { |
| 288 FingerprintVector::const_iterator j = | 291 HashValueVector::const_iterator j = |
| 289 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), | 292 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), |
| 290 FingerprintsEqualPredicate(*i)); | 293 HashValuesEqualPredicate(*i)); |
| 291 if (j == from_cert_chain.end()) | 294 if (j == from_cert_chain.end()) |
| 292 return true; | 295 return true; |
| 293 } | 296 } |
| 294 | 297 |
| 295 return false; | 298 return false; |
| 296 } | 299 } |
| 297 | 300 |
| 298 static bool HashesIntersect(const FingerprintVector& a, | 301 // Returns true if the intersection of |a| and |b| is not empty. If either |
| 299 const FingerprintVector& b) { | 302 // |a| or |b| is empty, returns false. |
| 300 for (FingerprintVector::const_iterator | 303 static bool HashesIntersect(const HashValueVector& a, |
| 301 i = a.begin(); i != a.end(); ++i) { | 304 const HashValueVector& b) { |
| 302 FingerprintVector::const_iterator j = | 305 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { |
| 303 std::find_if(b.begin(), b.end(), FingerprintsEqualPredicate(*i)); | 306 HashValueVector::const_iterator j = |
| 304 if (j != b.end()) | 307 std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i)); |
| 305 return true; | 308 if (j != b.end()) |
| 309 return true; |
| 306 } | 310 } |
| 307 | 311 |
| 308 return false; | 312 return false; |
| 309 } | 313 } |
| 310 | 314 |
| 311 // Returns true iff |pins| contains both a live and a backup pin. A live pin | 315 // Returns true iff |pins| contains both a live and a backup pin. A live pin |
| 312 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A | 316 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A |
| 313 // backup pin is a pin intended for disaster recovery, not day-to-day use, and | 317 // backup pin is a pin intended for disaster recovery, not day-to-day use, and |
| 314 // thus must be absent from the certificate chain. The Public-Key-Pins header | 318 // thus must be absent from the certificate chain. The Public-Key-Pins header |
| 315 // specification requires both. | 319 // specification requires both. |
| 316 static bool IsPinListValid(const FingerprintVector& pins, | 320 static bool IsPinListValid(const HashValueVector& pins, |
| 317 const SSLInfo& ssl_info) { | 321 const SSLInfo& ssl_info) { |
| 322 // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual |
| 323 // liveness and backupness below.) |
| 318 if (pins.size() < 2) | 324 if (pins.size() < 2) |
| 319 return false; | 325 return false; |
| 320 | 326 |
| 321 const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes; | 327 const HashValueVector& from_cert_chain = ssl_info.public_key_hashes; |
| 322 if (from_cert_chain.empty()) | 328 if (from_cert_chain.empty()) |
| 323 return false; | 329 return false; |
| 324 | 330 |
| 325 return IsBackupPinPresent(pins, from_cert_chain) && | 331 return IsBackupPinPresent(pins, from_cert_chain) && |
| 326 HashesIntersect(pins, from_cert_chain); | 332 HashesIntersect(pins, from_cert_chain); |
| 327 } | 333 } |
| 328 | 334 |
| 329 // "Public-Key-Pins" ":" | 335 // "Public-Key-Pins" ":" |
| 330 // "max-age" "=" delta-seconds ";" | 336 // "max-age" "=" delta-seconds ";" |
| 331 // "pin-" algo "=" base64 [ ";" ... ] | 337 // "pin-" algo "=" base64 [ ";" ... ] |
| 332 bool TransportSecurityState::DomainState::ParsePinsHeader( | 338 bool TransportSecurityState::DomainState::ParsePinsHeader( |
| 333 const base::Time& now, | 339 const base::Time& now, |
| 334 const std::string& value, | 340 const std::string& value, |
| 335 const SSLInfo& ssl_info) { | 341 const SSLInfo& ssl_info) { |
| 336 bool parsed_max_age = false; | 342 bool parsed_max_age = false; |
| 337 int max_age_candidate = 0; | 343 int max_age_candidate = 0; |
| 338 FingerprintVector pins; | 344 HashValueVector pins; |
| 339 | 345 |
| 340 std::string source = value; | 346 std::string source = value; |
| 341 | 347 |
| 342 while (!source.empty()) { | 348 while (!source.empty()) { |
| 343 StringPair semicolon = Split(source, ';'); | 349 StringPair semicolon = Split(source, ';'); |
| 344 semicolon.first = Strip(semicolon.first); | 350 semicolon.first = Strip(semicolon.first); |
| 345 semicolon.second = Strip(semicolon.second); | 351 semicolon.second = Strip(semicolon.second); |
| 346 StringPair equals = Split(semicolon.first, '='); | 352 StringPair equals = Split(semicolon.first, '='); |
| 347 equals.first = Strip(equals.first); | 353 equals.first = Strip(equals.first); |
| 348 equals.second = Strip(equals.second); | 354 equals.second = Strip(equals.second); |
| 349 | 355 |
| 350 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 356 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
| 351 if (equals.second.empty() || | 357 if (equals.second.empty() || |
| 352 !MaxAgeToInt(equals.second.begin(), equals.second.end(), | 358 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
| 353 &max_age_candidate)) { | 359 &max_age_candidate)) { |
| 354 return false; | 360 return false; |
| 355 } | 361 } |
| 356 if (max_age_candidate > kMaxHSTSAgeSecs) | 362 if (max_age_candidate > kMaxHSTSAgeSecs) |
| 357 max_age_candidate = kMaxHSTSAgeSecs; | 363 max_age_candidate = kMaxHSTSAgeSecs; |
| 358 parsed_max_age = true; | 364 parsed_max_age = true; |
| 359 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 365 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1") || |
| 366 LowerCaseEqualsASCII(equals.first, "pin-sha256")) { |
| 360 if (!ParseAndAppendPin(equals.second, &pins)) | 367 if (!ParseAndAppendPin(equals.second, &pins)) |
| 361 return false; | 368 return false; |
| 362 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | |
| 363 // TODO(palmer) | |
| 364 } else { | 369 } else { |
| 365 // Silently ignore unknown directives for forward compatibility. | 370 // Silently ignore unknown directives for forward compatibility. |
| 366 } | 371 } |
| 367 | 372 |
| 368 source = semicolon.second; | 373 source = semicolon.second; |
| 369 } | 374 } |
| 370 | 375 |
| 371 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 376 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
| 372 return false; | 377 return false; |
| 373 | 378 |
| 374 dynamic_spki_hashes_expiry = | 379 dynamic_spki_hashes_expiry = |
| 375 now + base::TimeDelta::FromSeconds(max_age_candidate); | 380 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 376 | 381 |
| 377 dynamic_spki_hashes.clear(); | 382 dynamic_spki_hashes.clear(); |
| 378 if (max_age_candidate > 0) { | 383 if (max_age_candidate > 0) { |
| 379 for (FingerprintVector::const_iterator i = pins.begin(); | 384 for (HashValueVector::const_iterator i = pins.begin(); |
| 380 i != pins.end(); ++i) { | 385 i != pins.end(); ++i) { |
| 381 dynamic_spki_hashes.push_back(*i); | 386 dynamic_spki_hashes.push_back(*i); |
| 382 } | 387 } |
| 383 } | 388 } |
| 384 | 389 |
| 385 return true; | 390 return true; |
| 386 } | 391 } |
| 387 | 392 |
| 388 // "Strict-Transport-Security" ":" | 393 // "Strict-Transport-Security" ":" |
| 389 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 394 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 476 include_subdomains = true; | 481 include_subdomains = true; |
| 477 upgrade_mode = MODE_FORCE_HTTPS; | 482 upgrade_mode = MODE_FORCE_HTTPS; |
| 478 return true; | 483 return true; |
| 479 default: | 484 default: |
| 480 NOTREACHED(); | 485 NOTREACHED(); |
| 481 return false; | 486 return false; |
| 482 } | 487 } |
| 483 } | 488 } |
| 484 | 489 |
| 485 static bool AddHash(const std::string& type_and_base64, | 490 static bool AddHash(const std::string& type_and_base64, |
| 486 FingerprintVector* out) { | 491 HashValueVector* out) { |
| 487 SHA1Fingerprint hash; | 492 HashValue hash; |
| 488 | 493 |
| 489 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 494 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
| 490 return false; | 495 return false; |
| 491 | 496 |
| 492 out->push_back(hash); | 497 out->push_back(hash); |
| 493 return true; | 498 return true; |
| 494 } | 499 } |
| 495 | 500 |
| 496 TransportSecurityState::~TransportSecurityState() {} | 501 TransportSecurityState::~TransportSecurityState() {} |
| 497 | 502 |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 743 const std::string& hashed_host, const DomainState& state) { | 748 const std::string& hashed_host, const DomainState& state) { |
| 744 enabled_hosts_[hashed_host] = state; | 749 enabled_hosts_[hashed_host] = state; |
| 745 } | 750 } |
| 746 | 751 |
| 747 void TransportSecurityState::AddOrUpdateForcedHosts( | 752 void TransportSecurityState::AddOrUpdateForcedHosts( |
| 748 const std::string& hashed_host, const DomainState& state) { | 753 const std::string& hashed_host, const DomainState& state) { |
| 749 forced_hosts_[hashed_host] = state; | 754 forced_hosts_[hashed_host] = state; |
| 750 } | 755 } |
| 751 | 756 |
| 752 static std::string HashesToBase64String( | 757 static std::string HashesToBase64String( |
| 753 const FingerprintVector& hashes) { | 758 const HashValueVector& hashes) { |
| 754 std::vector<std::string> hashes_strs; | 759 std::vector<std::string> hashes_strs; |
| 755 for (FingerprintVector::const_iterator | 760 for (HashValueVector::const_iterator |
| 756 i = hashes.begin(); i != hashes.end(); i++) { | 761 i = hashes.begin(); i != hashes.end(); i++) { |
| 757 std::string s; | 762 std::string s; |
| 758 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 763 const std::string hash_str(reinterpret_cast<const char*>(i->data()), |
| 759 sizeof(i->data)); | 764 i->size()); |
| 760 base::Base64Encode(hash_str, &s); | 765 base::Base64Encode(hash_str, &s); |
| 761 hashes_strs.push_back(s); | 766 hashes_strs.push_back(s); |
| 762 } | 767 } |
| 763 | 768 |
| 764 return JoinString(hashes_strs, ','); | 769 return JoinString(hashes_strs, ','); |
| 765 } | 770 } |
| 766 | 771 |
| 767 TransportSecurityState::DomainState::DomainState() | 772 TransportSecurityState::DomainState::DomainState() |
| 768 : upgrade_mode(MODE_FORCE_HTTPS), | 773 : upgrade_mode(MODE_FORCE_HTTPS), |
| 769 created(base::Time::Now()), | 774 created(base::Time::Now()), |
| 770 include_subdomains(false) { | 775 include_subdomains(false) { |
| 771 } | 776 } |
| 772 | 777 |
| 773 TransportSecurityState::DomainState::~DomainState() { | 778 TransportSecurityState::DomainState::~DomainState() { |
| 774 } | 779 } |
| 775 | 780 |
| 776 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 781 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
| 777 const FingerprintVector& hashes) const { | 782 const HashValueVector& hashes) const { |
| 783 // Validate that hashes is not empty. By the time this code is called (in |
| 784 // production), that should never happen, but it's good to be defensive. |
| 785 // And, hashes *can* be empty in some test scenarios. |
| 786 if (hashes.empty()) { |
| 787 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " |
| 788 "domain " << domain; |
| 789 return false; |
| 790 } |
| 791 |
| 778 if (HashesIntersect(bad_static_spki_hashes, hashes)) { | 792 if (HashesIntersect(bad_static_spki_hashes, hashes)) { |
| 779 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 793 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 780 << ". Validated chain: " << HashesToBase64String(hashes) | 794 << ". Validated chain: " << HashesToBase64String(hashes) |
| 781 << ", matches one or more bad hashes: " | 795 << ", matches one or more bad hashes: " |
| 782 << HashesToBase64String(bad_static_spki_hashes); | 796 << HashesToBase64String(bad_static_spki_hashes); |
| 783 return false; | 797 return false; |
| 784 } | 798 } |
| 785 | 799 |
| 786 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && | 800 if (HashesIntersect(dynamic_spki_hashes, hashes) || |
| 787 !HashesIntersect(dynamic_spki_hashes, hashes) && | 801 HashesIntersect(static_spki_hashes, hashes)) { |
| 788 !HashesIntersect(static_spki_hashes, hashes)) { | 802 return true; |
| 789 LOG(ERROR) << "Rejecting public key chain for domain " << domain | |
| 790 << ". Validated chain: " << HashesToBase64String(hashes) | |
| 791 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | |
| 792 << " or: " << HashesToBase64String(static_spki_hashes); | |
| 793 | |
| 794 return false; | |
| 795 } | 803 } |
| 796 | 804 |
| 797 return true; | 805 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 806 << ". Validated chain: " << HashesToBase64String(hashes) |
| 807 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
| 808 << " or: " << HashesToBase64String(static_spki_hashes); |
| 809 return false; |
| 798 } | 810 } |
| 799 | 811 |
| 800 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { | 812 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { |
| 801 return upgrade_mode == MODE_FORCE_HTTPS; | 813 return upgrade_mode == MODE_FORCE_HTTPS; |
| 802 } | 814 } |
| 803 | 815 |
| 804 bool TransportSecurityState::DomainState::Equals( | 816 bool TransportSecurityState::DomainState::Equals( |
| 805 const DomainState& other) const { | 817 const DomainState& other) const { |
| 806 // TODO(palmer): Implement this | 818 // TODO(palmer): Implement this |
| 807 (void) other; | 819 (void) other; |
| 808 return true; | 820 return true; |
| 809 } | 821 } |
| 810 | 822 |
| 811 bool TransportSecurityState::DomainState::HasPins() const { | 823 bool TransportSecurityState::DomainState::HasPins() const { |
| 812 return static_spki_hashes.size() > 0 || | 824 return static_spki_hashes.size() > 0 || |
| 813 bad_static_spki_hashes.size() > 0 || | 825 bad_static_spki_hashes.size() > 0 || |
| 814 dynamic_spki_hashes.size() > 0; | 826 dynamic_spki_hashes.size() > 0; |
| 815 } | 827 } |
| 816 | 828 |
| 817 } // namespace | 829 } // namespace |
| OLD | NEW |