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
3 // found in the LICENSE file.
5 #include "net/http/transport_security_state.h"
7 #if defined(USE_OPENSSL)
8 #include <openssl/ecdsa.h>
9 #include <openssl/ssl.h>
10 #else // !defined(USE_OPENSSL)
20 #include "base/base64.h"
21 #include "base/build_time.h"
22 #include "base/logging.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/metrics/histogram.h"
25 #include "base/sha1.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/time/time.h"
30 #include "base/values.h"
31 #include "crypto/sha2.h"
32 #include "net/base/dns_util.h"
33 #include "net/cert/x509_cert_types.h"
34 #include "net/cert/x509_certificate.h"
35 #include "net/http/http_security_headers.h"
36 #include "net/ssl/ssl_info.h"
39 #if defined(USE_OPENSSL)
40 #include "crypto/openssl_util.h"
47 std::string HashesToBase64String(const HashValueVector& hashes) {
49 for (size_t i = 0; i != hashes.size(); ++i) {
52 str += hashes[i].ToString();
57 std::string HashHost(const std::string& canonicalized_host) {
58 char hashed[crypto::kSHA256Length];
59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
60 return std::string(hashed, sizeof(hashed));
63 // Returns true if the intersection of |a| and |b| is not empty. If either
64 // |a| or |b| is empty, returns false.
65 bool HashesIntersect(const HashValueVector& a,
66 const HashValueVector& b) {
67 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) {
68 HashValueVector::const_iterator j =
69 std::find_if(b.begin(), b.end(), HashValuesEqual(*i));
76 bool AddHash(const char* sha1_hash,
77 HashValueVector* out) {
78 HashValue hash(HASH_VALUE_SHA1);
79 memcpy(hash.data(), sha1_hash, hash.size());
86 TransportSecurityState::TransportSecurityState()
87 : delegate_(NULL), enable_static_pins_(true) {
88 // Static pinning is only enabled for official builds to make sure that
89 // others don't end up with pins that cannot be easily updated.
90 #if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS)
91 enable_static_pins_ = false;
93 DCHECK(CalledOnValidThread());
96 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state)
97 : iterator_(state.enabled_hosts_.begin()),
98 end_(state.enabled_hosts_.end()) {
101 TransportSecurityState::Iterator::~Iterator() {}
103 bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) {
105 if (GetStaticDomainState(host, &state))
107 return GetDynamicDomainState(host, &state);
110 bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host) {
111 DomainState dynamic_state;
112 if (GetDynamicDomainState(host, &dynamic_state))
113 return dynamic_state.ShouldUpgradeToSSL();
115 DomainState static_state;
116 if (GetStaticDomainState(host, &static_state) &&
117 static_state.ShouldUpgradeToSSL()) {
124 bool TransportSecurityState::CheckPublicKeyPins(
125 const std::string& host,
126 bool is_issued_by_known_root,
127 const HashValueVector& public_key_hashes,
128 std::string* pinning_failure_log) {
129 // Perform pin validation if, and only if, all these conditions obtain:
131 // * the server's certificate chain chains up to a known root (i.e. not a
132 // user-installed trust anchor); and
133 // * the server actually has public key pins.
134 if (!is_issued_by_known_root || !HasPublicKeyPins(host)) {
138 bool pins_are_valid = CheckPublicKeyPinsImpl(
139 host, public_key_hashes, pinning_failure_log);
140 if (!pins_are_valid) {
141 LOG(ERROR) << *pinning_failure_log;
142 ReportUMAOnPinFailure(host);
145 UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid);
146 return pins_are_valid;
149 bool TransportSecurityState::HasPublicKeyPins(const std::string& host) {
150 DomainState dynamic_state;
151 if (GetDynamicDomainState(host, &dynamic_state))
152 return dynamic_state.HasPublicKeyPins();
154 DomainState static_state;
155 if (GetStaticDomainState(host, &static_state)) {
156 if (static_state.HasPublicKeyPins())
163 void TransportSecurityState::SetDelegate(
164 TransportSecurityState::Delegate* delegate) {
165 DCHECK(CalledOnValidThread());
166 delegate_ = delegate;
169 void TransportSecurityState::EnableHost(const std::string& host,
170 const DomainState& state) {
171 DCHECK(CalledOnValidThread());
173 const std::string canonicalized_host = CanonicalizeHost(host);
174 if (canonicalized_host.empty())
177 DomainState state_copy(state);
178 // No need to store this value since it is redundant. (|canonicalized_host|
180 state_copy.domain.clear();
182 enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
186 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) {
187 DCHECK(CalledOnValidThread());
189 const std::string canonicalized_host = CanonicalizeHost(host);
190 if (canonicalized_host.empty())
193 DomainStateMap::iterator i = enabled_hosts_.find(
194 HashHost(canonicalized_host));
195 if (i != enabled_hosts_.end()) {
196 enabled_hosts_.erase(i);
203 void TransportSecurityState::ClearDynamicData() {
204 DCHECK(CalledOnValidThread());
205 enabled_hosts_.clear();
208 void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) {
209 DCHECK(CalledOnValidThread());
211 bool dirtied = false;
212 DomainStateMap::iterator i = enabled_hosts_.begin();
213 while (i != enabled_hosts_.end()) {
214 if (i->second.sts.last_observed >= time &&
215 i->second.pkp.last_observed >= time) {
217 enabled_hosts_.erase(i++);
221 if (i->second.sts.last_observed >= time) {
223 i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT;
224 } else if (i->second.pkp.last_observed >= time) {
226 i->second.pkp.spki_hashes.clear();
227 i->second.pkp.expiry = base::Time();
236 TransportSecurityState::~TransportSecurityState() {
237 DCHECK(CalledOnValidThread());
240 void TransportSecurityState::DirtyNotify() {
241 DCHECK(CalledOnValidThread());
244 delegate_->StateIsDirty(this);
248 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) {
249 // We cannot perform the operations as detailed in the spec here as |host|
250 // has already undergone IDN processing before it reached us. Thus, we check
251 // that there are no invalid characters in the host and lowercase the result.
253 std::string new_host;
254 if (!DNSDomainFromDot(host, &new_host)) {
255 // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole
256 // name is >255 bytes. However, search terms can have those properties.
257 return std::string();
260 for (size_t i = 0; new_host[i]; i += new_host[i] + 1) {
261 const unsigned label_length = static_cast<unsigned>(new_host[i]);
265 for (size_t j = 0; j < label_length; ++j) {
266 new_host[i + 1 + j] = tolower(new_host[i + 1 + j]);
273 // BitReader is a class that allows a bytestring to be read bit-by-bit.
276 BitReader(const uint8* bytes, size_t num_bits)
279 num_bytes_((num_bits + 7) / 8),
280 current_byte_index_(0),
283 // Next sets |*out| to the next bit from the input. It returns false if no
284 // more bits are available or true otherwise.
285 bool Next(bool* out) {
286 if (num_bits_used_ == 8) {
287 if (current_byte_index_ >= num_bytes_) {
290 current_byte_ = bytes_[current_byte_index_++];
294 *out = 1 & (current_byte_ >> (7 - num_bits_used_));
299 // Read sets the |num_bits| least-significant bits of |*out| to the value of
300 // the next |num_bits| bits from the input. It returns false if there are
301 // insufficient bits in the input or true otherwise.
302 bool Read(unsigned num_bits, uint32* out) {
303 DCHECK_LE(num_bits, 32u);
306 for (unsigned i = 0; i < num_bits; ++i) {
311 ret |= static_cast<uint32>(bit) << (num_bits - 1 - i);
318 // Unary sets |*out| to the result of decoding a unary value from the input.
319 // It returns false if there were insufficient bits in the input and true
321 bool Unary(size_t* out) {
339 // Seek sets the current offest in the input to bit number |offset|. It
340 // returns true if |offset| is within the range of the input and false
342 bool Seek(size_t offset) {
343 if (offset >= num_bits_) {
346 current_byte_index_ = offset / 8;
347 current_byte_ = bytes_[current_byte_index_++];
348 num_bits_used_ = offset % 8;
353 const uint8* const bytes_;
354 const size_t num_bits_;
355 const size_t num_bytes_;
356 // current_byte_index_ contains the current byte offset in |bytes_|.
357 size_t current_byte_index_;
358 // current_byte_ contains the current byte of the input.
360 // num_bits_used_ contains the number of bits of |current_byte_| that have
362 unsigned num_bits_used_;
365 // HuffmanDecoder is a very simple Huffman reader. The input Huffman tree is
366 // simply encoded as a series of two-byte structures. The first byte determines
367 // the "0" pointer for that node and the second the "1" pointer. Each byte
368 // either has the MSB set, in which case the bottom 7 bits are the value for
369 // that position, or else the bottom seven bits contain the index of a node.
371 // The tree is decoded by walking rather than a table-driven approach.
372 class HuffmanDecoder {
374 HuffmanDecoder(const uint8* tree, size_t tree_bytes)
376 tree_bytes_(tree_bytes) {}
378 bool Decode(BitReader* reader, char* out) {
379 const uint8* current = &tree_[tree_bytes_-2];
383 if (!reader->Next(&bit)) {
387 uint8 b = current[bit];
389 *out = static_cast<char>(b & 0x7f);
393 unsigned offset = static_cast<unsigned>(b) * 2;
394 DCHECK_LT(offset, tree_bytes_);
395 if (offset >= tree_bytes_) {
399 current = &tree_[offset];
404 const uint8* const tree_;
405 const size_t tree_bytes_;
408 #include "net/http/transport_security_state_static.h"
410 // PreloadResult is the result of resolving a specific name in the preloaded
412 struct PreloadResult {
415 // hostname_offset contains the number of bytes from the start of the given
416 // hostname where the name of the matching entry starts.
417 size_t hostname_offset;
418 bool include_subdomains;
423 // DecodeHSTSPreloadRaw resolves |hostname| in the preloaded data. It returns
424 // false on internal error and true otherwise. After a successful return,
425 // |*out_found| is true iff a relevant entry has been found. If so, |*out|
426 // contains the details.
428 // Don't call this function, call DecodeHSTSPreload, below.
430 // Although this code should be robust, it never processes attacker-controlled
431 // data -- it only operates on the preloaded data built into the binary.
433 // The preloaded data is represented as a trie and matches the hostname
434 // backwards. Each node in the trie starts with a number of characters, which
435 // must match exactly. After that is a dispatch table which maps the next
436 // character in the hostname to another node in the trie.
438 // In the dispatch table, the zero character represents the "end of string"
439 // (which is the *beginning* of a hostname since we process it backwards). The
440 // value in that case is special -- rather than an offset to another trie node,
441 // it contains the HSTS information: whether subdomains are included, pinsets
442 // etc. If an "end of string" matches a period in the hostname then the
443 // information is remembered because, if no more specific node is found, then
444 // that information applies to the hostname.
446 // Dispatch tables are always given in order, but the "end of string" (zero)
447 // value always comes before an entry for '.'.
448 bool DecodeHSTSPreloadRaw(const std::string& hostname,
450 PreloadResult* out) {
451 HuffmanDecoder huffman(kHSTSHuffmanTree, sizeof(kHSTSHuffmanTree));
452 BitReader reader(kPreloadedHSTSData, kPreloadedHSTSBits);
453 size_t bit_offset = kHSTSRootPosition;
454 static const char kEndOfString = 0;
455 static const char kEndOfTable = 127;
459 if (hostname.empty()) {
462 // hostname_offset contains one more than the index of the current character
463 // in the hostname that is being considered. It's one greater so that we can
464 // represent the position just before the beginning (with zero).
465 size_t hostname_offset = hostname.size();
468 // Seek to the desired location.
469 if (!reader.Seek(bit_offset)) {
473 // Decode the unary length of the common prefix.
474 size_t prefix_length;
475 if (!reader.Unary(&prefix_length)) {
479 // Match each character in the prefix.
480 for (size_t i = 0; i < prefix_length; ++i) {
481 if (hostname_offset == 0) {
482 // We can't match the terminator with a prefix string.
487 if (!huffman.Decode(&reader, &c)) {
490 if (hostname[hostname_offset - 1] != c) {
496 bool is_first_offset = true;
497 size_t current_offset = 0;
499 // Next is the dispatch table.
502 if (!huffman.Decode(&reader, &c)) {
505 if (c == kEndOfTable) {
510 if (c == kEndOfString) {
512 if (!reader.Next(&tmp.include_subdomains) ||
513 !reader.Next(&tmp.force_https) ||
514 !reader.Next(&tmp.has_pins)) {
519 if (!reader.Read(4, &tmp.pinset_id) ||
520 !reader.Read(9, &tmp.domain_id)) {
525 tmp.hostname_offset = hostname_offset;
527 if (hostname_offset == 0 || hostname[hostname_offset - 1] == '.') {
528 *out_found = tmp.include_subdomains;
532 if (hostname_offset == 0) {
540 // The entries in a dispatch table are in order thus we can tell if there
541 // will be no match if the current character past the one that we want.
542 if (hostname_offset == 0 || hostname[hostname_offset-1] < c) {
546 if (is_first_offset) {
547 // The first offset is backwards from the current position.
548 uint32 jump_delta_bits;
550 if (!reader.Read(5, &jump_delta_bits) ||
551 !reader.Read(jump_delta_bits, &jump_delta)) {
555 if (bit_offset < jump_delta) {
559 current_offset = bit_offset - jump_delta;
560 is_first_offset = false;
562 // Subsequent offsets are forward from the target of the first offset.
564 if (!reader.Read(1, &is_long_jump)) {
570 if (!reader.Read(7, &jump_delta)) {
574 uint32 jump_delta_bits;
575 if (!reader.Read(4, &jump_delta_bits) ||
576 !reader.Read(jump_delta_bits + 8, &jump_delta)) {
581 current_offset += jump_delta;
582 if (current_offset >= bit_offset) {
587 DCHECK_LT(0u, hostname_offset);
588 if (hostname[hostname_offset - 1] == c) {
589 bit_offset = current_offset;
597 bool DecodeHSTSPreload(const std::string& hostname,
598 PreloadResult* out) {
600 if (!DecodeHSTSPreloadRaw(hostname, &found, out)) {
601 DCHECK(false) << "Internal error in DecodeHSTSPreloadRaw for hostname "
609 bool TransportSecurityState::AddHSTSHeader(const std::string& host,
610 const std::string& value) {
611 DCHECK(CalledOnValidThread());
613 base::Time now = base::Time::Now();
614 base::TimeDelta max_age;
615 TransportSecurityState::DomainState domain_state;
616 GetDynamicDomainState(host, &domain_state);
617 if (ParseHSTSHeader(value, &max_age, &domain_state.sts.include_subdomains)) {
618 // Handle max-age == 0.
619 if (max_age.InSeconds() == 0)
620 domain_state.sts.upgrade_mode = DomainState::MODE_DEFAULT;
622 domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
623 domain_state.sts.last_observed = now;
624 domain_state.sts.expiry = now + max_age;
625 EnableHost(host, domain_state);
631 bool TransportSecurityState::AddHPKPHeader(const std::string& host,
632 const std::string& value,
633 const SSLInfo& ssl_info) {
634 DCHECK(CalledOnValidThread());
636 base::Time now = base::Time::Now();
637 base::TimeDelta max_age;
638 TransportSecurityState::DomainState domain_state;
639 GetDynamicDomainState(host, &domain_state);
640 if (ParseHPKPHeader(value,
641 ssl_info.public_key_hashes,
643 &domain_state.pkp.include_subdomains,
644 &domain_state.pkp.spki_hashes)) {
645 // Handle max-age == 0.
646 if (max_age.InSeconds() == 0)
647 domain_state.pkp.spki_hashes.clear();
648 domain_state.pkp.last_observed = now;
649 domain_state.pkp.expiry = now + max_age;
650 EnableHost(host, domain_state);
656 bool TransportSecurityState::AddHSTS(const std::string& host,
657 const base::Time& expiry,
658 bool include_subdomains) {
659 DCHECK(CalledOnValidThread());
661 // Copy-and-modify the existing DomainState for this host (if any).
662 TransportSecurityState::DomainState domain_state;
663 const std::string canonicalized_host = CanonicalizeHost(host);
664 const std::string hashed_host = HashHost(canonicalized_host);
665 DomainStateMap::const_iterator i = enabled_hosts_.find(
667 if (i != enabled_hosts_.end())
668 domain_state = i->second;
670 domain_state.sts.last_observed = base::Time::Now();
671 domain_state.sts.include_subdomains = include_subdomains;
672 domain_state.sts.expiry = expiry;
673 domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
674 EnableHost(host, domain_state);
678 bool TransportSecurityState::AddHPKP(const std::string& host,
679 const base::Time& expiry,
680 bool include_subdomains,
681 const HashValueVector& hashes) {
682 DCHECK(CalledOnValidThread());
684 // Copy-and-modify the existing DomainState for this host (if any).
685 TransportSecurityState::DomainState domain_state;
686 const std::string canonicalized_host = CanonicalizeHost(host);
687 const std::string hashed_host = HashHost(canonicalized_host);
688 DomainStateMap::const_iterator i = enabled_hosts_.find(
690 if (i != enabled_hosts_.end())
691 domain_state = i->second;
693 domain_state.pkp.last_observed = base::Time::Now();
694 domain_state.pkp.include_subdomains = include_subdomains;
695 domain_state.pkp.expiry = expiry;
696 domain_state.pkp.spki_hashes = hashes;
697 EnableHost(host, domain_state);
702 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host) {
703 PreloadResult result;
704 return DecodeHSTSPreload(host, &result) && result.has_pins &&
705 kPinsets[result.pinset_id].accepted_pins == kGoogleAcceptableCerts;
709 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) {
710 PreloadResult result;
711 if (!DecodeHSTSPreload(host, &result) ||
716 DCHECK(result.domain_id != DOMAIN_NOT_PINNED);
718 UMA_HISTOGRAM_ENUMERATION(
719 "Net.PublicKeyPinFailureDomain", result.domain_id, DOMAIN_NUM_EVENTS);
723 bool TransportSecurityState::IsBuildTimely() {
724 // If the build metadata aren't embedded in the binary then we can't use the
725 // build time to determine if the build is timely, return true by default. If
726 // we're building an official build then keep using the build time, even if
727 // it's invalid it'd be a date in the past and this function will return
729 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
732 const base::Time build_time = base::GetBuildTime();
733 // We consider built-in information to be timely for 10 weeks.
734 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
738 bool TransportSecurityState::CheckPublicKeyPinsImpl(
739 const std::string& host,
740 const HashValueVector& hashes,
741 std::string* failure_log) {
742 DomainState dynamic_state;
743 if (GetDynamicDomainState(host, &dynamic_state))
744 return dynamic_state.CheckPublicKeyPins(hashes, failure_log);
746 DomainState static_state;
747 if (GetStaticDomainState(host, &static_state))
748 return static_state.CheckPublicKeyPins(hashes, failure_log);
750 // HasPublicKeyPins should have returned true in order for this method
751 // to have been called, so if we fall through to here, it's an error.
755 bool TransportSecurityState::GetStaticDomainState(const std::string& host,
756 DomainState* out) const {
757 DCHECK(CalledOnValidThread());
759 out->sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
760 out->sts.include_subdomains = false;
761 out->pkp.include_subdomains = false;
763 if (!IsBuildTimely())
766 PreloadResult result;
767 if (!DecodeHSTSPreload(host, &result))
770 out->domain = host.substr(result.hostname_offset);
771 out->sts.include_subdomains = result.include_subdomains;
772 out->sts.last_observed = base::GetBuildTime();
773 out->sts.upgrade_mode =
774 TransportSecurityState::DomainState::MODE_DEFAULT;
775 if (result.force_https) {
776 out->sts.upgrade_mode =
777 TransportSecurityState::DomainState::MODE_FORCE_HTTPS;
780 if (enable_static_pins_ && result.has_pins) {
781 out->pkp.include_subdomains = result.include_subdomains;
782 out->pkp.last_observed = base::GetBuildTime();
784 if (result.pinset_id >= arraysize(kPinsets))
786 const Pinset *pinset = &kPinsets[result.pinset_id];
788 if (pinset->accepted_pins) {
789 const char* const* sha1_hash = pinset->accepted_pins;
791 AddHash(*sha1_hash, &out->pkp.spki_hashes);
795 if (pinset->rejected_pins) {
796 const char* const* sha1_hash = pinset->rejected_pins;
798 AddHash(*sha1_hash, &out->pkp.bad_spki_hashes);
807 bool TransportSecurityState::GetDynamicDomainState(const std::string& host,
808 DomainState* result) {
809 DCHECK(CalledOnValidThread());
812 const std::string canonicalized_host = CanonicalizeHost(host);
813 if (canonicalized_host.empty())
816 base::Time current_time(base::Time::Now());
818 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
819 std::string host_sub_chunk(&canonicalized_host[i],
820 canonicalized_host.size() - i);
821 DomainStateMap::iterator j =
822 enabled_hosts_.find(HashHost(host_sub_chunk));
823 if (j == enabled_hosts_.end())
826 if (current_time > j->second.sts.expiry &&
827 current_time > j->second.pkp.expiry) {
828 enabled_hosts_.erase(j);
834 state.domain = DNSDomainToString(host_sub_chunk);
836 // Succeed if we matched the domain exactly or if subdomain matches are
838 if (i == 0 || j->second.sts.include_subdomains ||
839 j->second.pkp.include_subdomains) {
850 void TransportSecurityState::AddOrUpdateEnabledHosts(
851 const std::string& hashed_host, const DomainState& state) {
852 DCHECK(CalledOnValidThread());
853 enabled_hosts_[hashed_host] = state;
856 TransportSecurityState::DomainState::DomainState() {
857 sts.upgrade_mode = MODE_DEFAULT;
858 sts.include_subdomains = false;
859 pkp.include_subdomains = false;
862 TransportSecurityState::DomainState::~DomainState() {
865 bool TransportSecurityState::DomainState::CheckPublicKeyPins(
866 const HashValueVector& hashes, std::string* failure_log) const {
867 // Validate that hashes is not empty. By the time this code is called (in
868 // production), that should never happen, but it's good to be defensive.
869 // And, hashes *can* be empty in some test scenarios.
870 if (hashes.empty()) {
872 "Rejecting empty public key chain for public-key-pinned domains: " +
877 if (HashesIntersect(pkp.bad_spki_hashes, hashes)) {
878 failure_log->append("Rejecting public key chain for domain " + domain +
879 ". Validated chain: " + HashesToBase64String(hashes) +
880 ", matches one or more bad hashes: " +
881 HashesToBase64String(pkp.bad_spki_hashes));
885 // If there are no pins, then any valid chain is acceptable.
886 if (pkp.spki_hashes.empty())
889 if (HashesIntersect(pkp.spki_hashes, hashes)) {
893 failure_log->append("Rejecting public key chain for domain " + domain +
894 ". Validated chain: " + HashesToBase64String(hashes) +
895 ", expected: " + HashesToBase64String(pkp.spki_hashes));
899 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const {
900 return sts.upgrade_mode == MODE_FORCE_HTTPS;
903 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const {
907 bool TransportSecurityState::DomainState::HasPublicKeyPins() const {
908 return pkp.spki_hashes.size() > 0 || pkp.bad_spki_hashes.size() > 0;
911 TransportSecurityState::DomainState::PKPState::PKPState() {
914 TransportSecurityState::DomainState::PKPState::~PKPState() {