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/base/transport_security_state.h"
7 #if defined(USE_OPENSSL)
8 #include <openssl/ecdsa.h>
9 #include <openssl/ssl.h>
10 #else // !defined(USE_OPENSSL)
21 #include "base/base64.h"
22 #include "base/json/json_reader.h"
23 #include "base/json/json_writer.h"
24 #include "base/logging.h"
25 #include "base/memory/scoped_ptr.h"
26 #include "base/metrics/histogram.h"
27 #include "base/sha1.h"
28 #include "base/string_number_conversions.h"
29 #include "base/string_tokenizer.h"
30 #include "base/string_util.h"
31 #include "base/time.h"
32 #include "base/utf_string_conversions.h"
33 #include "base/values.h"
34 #include "crypto/sha2.h"
35 #include "googleurl/src/gurl.h"
36 #include "net/base/asn1_util.h"
37 #include "net/base/dns_util.h"
38 #include "net/base/public_key_hashes.h"
39 #include "net/base/ssl_info.h"
40 #include "net/base/x509_certificate.h"
41 #include "net/http/http_util.h"
43 #if defined(USE_OPENSSL)
44 #include "crypto/openssl_util.h"
49 const long int TransportSecurityState::kMaxHSTSAgeSecs
= 86400 * 365; // 1 year
51 TransportSecurityState::TransportSecurityState(const std::string
& hsts_hosts
)
53 if (!hsts_hosts
.empty()) {
55 Deserialise(hsts_hosts
, &dirty
, &forced_hosts_
);
59 static std::string
HashHost(const std::string
& canonicalized_host
) {
60 char hashed
[crypto::kSHA256Length
];
61 crypto::SHA256HashString(canonicalized_host
, hashed
, sizeof(hashed
));
62 return std::string(hashed
, sizeof(hashed
));
65 void TransportSecurityState::SetDelegate(
66 TransportSecurityState::Delegate
* delegate
) {
70 void TransportSecurityState::EnableHost(const std::string
& host
,
71 const DomainState
& state
) {
72 DCHECK(CalledOnValidThread());
74 const std::string canonicalized_host
= CanonicalizeHost(host
);
75 if (canonicalized_host
.empty())
78 // Only override a preloaded state if the new state describes a more strict
79 // policy. TODO(palmer): Reconsider this?
80 DomainState existing_state
;
81 if (IsPreloadedSTS(canonicalized_host
, true, &existing_state
) &&
82 canonicalized_host
== CanonicalizeHost(existing_state
.domain
) &&
83 existing_state
.IsMoreStrict(state
)) {
87 // Use the original creation date if we already have this host.
88 DomainState
state_copy(state
);
89 if (GetDomainState(&existing_state
, host
, true) &&
90 !existing_state
.created
.is_null()) {
91 state_copy
.created
= existing_state
.created
;
94 // We don't store these values.
95 state_copy
.preloaded
= false;
96 state_copy
.domain
.clear();
98 enabled_hosts_
[HashHost(canonicalized_host
)] = state_copy
;
102 bool TransportSecurityState::DeleteHost(const std::string
& host
) {
103 DCHECK(CalledOnValidThread());
105 const std::string canonicalized_host
= CanonicalizeHost(host
);
106 if (canonicalized_host
.empty())
109 std::map
<std::string
, DomainState
>::iterator i
= enabled_hosts_
.find(
110 HashHost(canonicalized_host
));
111 if (i
!= enabled_hosts_
.end()) {
112 enabled_hosts_
.erase(i
);
119 bool TransportSecurityState::HasPinsForHost(DomainState
* result
,
120 const std::string
& host
,
121 bool sni_available
) {
122 DCHECK(CalledOnValidThread());
124 return HasMetadata(result
, host
, sni_available
) &&
125 (!result
->dynamic_spki_hashes
.empty() ||
126 !result
->preloaded_spki_hashes
.empty());
129 bool TransportSecurityState::GetDomainState(DomainState
* result
,
130 const std::string
& host
,
131 bool sni_available
) {
132 DCHECK(CalledOnValidThread());
134 return HasMetadata(result
, host
, sni_available
);
137 bool TransportSecurityState::HasMetadata(DomainState
* result
,
138 const std::string
& host
,
139 bool sni_available
) {
140 DCHECK(CalledOnValidThread());
142 *result
= DomainState();
143 const std::string canonicalized_host
= CanonicalizeHost(host
);
144 if (canonicalized_host
.empty())
147 bool has_preload
= IsPreloadedSTS(canonicalized_host
, sni_available
, result
);
148 std::string canonicalized_preload
= CanonicalizeHost(result
->domain
);
150 base::Time
current_time(base::Time::Now());
152 for (size_t i
= 0; canonicalized_host
[i
]; i
+= canonicalized_host
[i
] + 1) {
153 std::string
host_sub_chunk(&canonicalized_host
[i
],
154 canonicalized_host
.size() - i
);
155 // Exact match of a preload always wins.
156 if (has_preload
&& host_sub_chunk
== canonicalized_preload
)
159 std::map
<std::string
, DomainState
>::iterator j
=
160 enabled_hosts_
.find(HashHost(host_sub_chunk
));
161 if (j
== enabled_hosts_
.end())
164 if (current_time
> j
->second
.expiry
&&
165 current_time
> j
->second
.dynamic_spki_hashes_expiry
) {
166 enabled_hosts_
.erase(j
);
172 result
->domain
= DNSDomainToString(host_sub_chunk
);
174 // If we matched the domain exactly, it doesn't matter what the value of
175 // include_subdomains is.
179 return j
->second
.include_subdomains
;
185 void TransportSecurityState::DeleteSince(const base::Time
& time
) {
186 DCHECK(CalledOnValidThread());
188 bool dirtied
= false;
190 std::map
<std::string
, DomainState
>::iterator i
= enabled_hosts_
.begin();
191 while (i
!= enabled_hosts_
.end()) {
192 if (i
->second
.created
>= time
) {
194 enabled_hosts_
.erase(i
++);
204 // MaxAgeToInt converts a string representation of a number of seconds into a
205 // int. We use strtol in order to handle overflow correctly. The string may
206 // contain an arbitary number which we should truncate correctly rather than
207 // throwing a parse failure.
208 static bool MaxAgeToInt(std::string::const_iterator begin
,
209 std::string::const_iterator end
,
211 const std::string
s(begin
, end
);
213 long int i
= strtol(s
.data(), &endptr
, 10 /* base */);
214 if (*endptr
|| i
< 0)
216 if (i
> TransportSecurityState::kMaxHSTSAgeSecs
)
217 i
= TransportSecurityState::kMaxHSTSAgeSecs
;
222 // Strip, Split, StringPair, and ParsePins are private implementation details
223 // of ParsePinsHeader(std::string&, DomainState&).
224 static std::string
Strip(const std::string
& source
) {
228 std::string::const_iterator start
= source
.begin();
229 std::string::const_iterator end
= source
.end();
230 HttpUtil::TrimLWS(&start
, &end
);
231 return std::string(start
, end
);
234 typedef std::pair
<std::string
, std::string
> StringPair
;
236 static StringPair
Split(const std::string
& source
, char delimiter
) {
238 size_t point
= source
.find(delimiter
);
240 pair
.first
= source
.substr(0, point
);
241 if (std::string::npos
!= point
)
242 pair
.second
= source
.substr(point
+ 1);
247 // TODO(palmer): Support both sha256 and sha1. This will require additional
248 // infrastructure code changes and can come in a later patch.
251 bool TransportSecurityState::ParsePin(const std::string
& value
,
252 SHA1Fingerprint
* out
) {
253 StringPair slash
= Split(Strip(value
), '/');
254 if (slash
.first
!= "sha1")
258 if (!base::Base64Decode(slash
.second
, &decoded
) ||
259 decoded
.size() != arraysize(out
->data
)) {
263 memcpy(out
->data
, decoded
.data(), arraysize(out
->data
));
267 static bool ParseAndAppendPin(const std::string
& value
,
268 FingerprintVector
* fingerprints
) {
269 // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28
270 // characters; 32 bytes base64'd is 44 characters. TODO(palmer): Support
272 size_t size
= value
.size();
273 if (size
!= 30 || value
[0] != '"' || value
[size
- 1] != '"')
276 std::string unquoted
= HttpUtil::Unquote(value
);
280 if (!base::Base64Decode(unquoted
, &decoded
) ||
281 decoded
.size() != arraysize(fp
.data
)) {
285 memcpy(fp
.data
, decoded
.data(), arraysize(fp
.data
));
286 fingerprints
->push_back(fp
);
291 bool TransportSecurityState::GetPublicKeyHash(
292 const X509Certificate
& cert
, SHA1Fingerprint
* spki_hash
) {
293 std::string der_bytes
;
294 if (!X509Certificate::GetDEREncoded(cert
.os_cert_handle(), &der_bytes
))
297 base::StringPiece spki
;
298 if (!asn1::ExtractSPKIFromDERCert(der_bytes
, &spki
))
301 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki
.data()),
302 spki
.size(), spki_hash
->data
);
307 struct FingerprintsEqualPredicate
{
308 explicit FingerprintsEqualPredicate(const SHA1Fingerprint
& fingerprint
) :
309 fingerprint_(fingerprint
) {}
311 bool operator()(const SHA1Fingerprint
& other
) const {
312 return fingerprint_
.Equals(other
);
315 const SHA1Fingerprint
& fingerprint_
;
318 // Returns true iff there is an item in |pins| which is not present in
319 // |from_cert_chain|. Such an SPKI hash is called a "backup pin".
320 static bool IsBackupPinPresent(const FingerprintVector
& pins
,
321 const FingerprintVector
& from_cert_chain
) {
322 for (FingerprintVector::const_iterator
323 i
= pins
.begin(); i
!= pins
.end(); ++i
) {
324 FingerprintVector::const_iterator j
=
325 std::find_if(from_cert_chain
.begin(), from_cert_chain
.end(),
326 FingerprintsEqualPredicate(*i
));
327 if (j
== from_cert_chain
.end())
334 static bool HashesIntersect(const FingerprintVector
& a
,
335 const FingerprintVector
& b
) {
336 for (FingerprintVector::const_iterator
337 i
= a
.begin(); i
!= a
.end(); ++i
) {
338 FingerprintVector::const_iterator j
=
339 std::find_if(b
.begin(), b
.end(), FingerprintsEqualPredicate(*i
));
347 // Returns true iff |pins| contains both a live and a backup pin. A live pin
348 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A
349 // backup pin is a pin intended for disaster recovery, not day-to-day use, and
350 // thus must be absent from the certificate chain. The Public-Key-Pins header
351 // specification requires both.
352 static bool IsPinListValid(const FingerprintVector
& pins
,
353 const SSLInfo
& ssl_info
) {
357 const FingerprintVector
& from_cert_chain
= ssl_info
.public_key_hashes
;
358 if (from_cert_chain
.empty())
361 return IsBackupPinPresent(pins
, from_cert_chain
) &&
362 HashesIntersect(pins
, from_cert_chain
);
365 // "Public-Key-Pins" ":"
366 // "max-age" "=" delta-seconds ";"
367 // "pin-" algo "=" base64 [ ";" ... ]
370 bool TransportSecurityState::ParsePinsHeader(const std::string
& value
,
371 const SSLInfo
& ssl_info
,
372 DomainState
* state
) {
373 bool parsed_max_age
= false;
375 FingerprintVector pins
;
377 std::string source
= value
;
379 while (!source
.empty()) {
380 StringPair semicolon
= Split(source
, ';');
381 semicolon
.first
= Strip(semicolon
.first
);
382 semicolon
.second
= Strip(semicolon
.second
);
383 StringPair equals
= Split(semicolon
.first
, '=');
384 equals
.first
= Strip(equals
.first
);
385 equals
.second
= Strip(equals
.second
);
387 if (LowerCaseEqualsASCII(equals
.first
, "max-age")) {
388 if (equals
.second
.empty() ||
389 !MaxAgeToInt(equals
.second
.begin(), equals
.second
.end(), &max_age
)) {
392 if (max_age
> kMaxHSTSAgeSecs
)
393 max_age
= kMaxHSTSAgeSecs
;
394 parsed_max_age
= true;
395 } else if (LowerCaseEqualsASCII(equals
.first
, "pin-sha1")) {
396 if (!ParseAndAppendPin(equals
.second
, &pins
))
398 } else if (LowerCaseEqualsASCII(equals
.first
, "pin-sha256")) {
401 // Silently ignore unknown directives for forward compatibility.
404 source
= semicolon
.second
;
407 if (!parsed_max_age
|| !IsPinListValid(pins
, ssl_info
))
410 state
->max_age
= max_age
;
411 state
->dynamic_spki_hashes_expiry
=
412 base::Time::Now() + base::TimeDelta::FromSeconds(max_age
);
414 state
->dynamic_spki_hashes
.clear();
416 for (FingerprintVector::const_iterator i
= pins
.begin();
417 i
!= pins
.end(); i
++) {
418 state
->dynamic_spki_hashes
.push_back(*i
);
425 // "Strict-Transport-Security" ":"
426 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ]
429 bool TransportSecurityState::ParseHeader(const std::string
& value
,
431 bool* include_subdomains
) {
433 DCHECK(include_subdomains
);
435 int max_age_candidate
= 0;
440 AFTER_MAX_AGE_EQUALS
,
442 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER
,
443 AFTER_INCLUDE_SUBDOMAINS
,
446 StringTokenizer
tokenizer(value
, " \t=;");
447 tokenizer
.set_options(StringTokenizer::RETURN_DELIMS
);
448 while (tokenizer
.GetNext()) {
449 DCHECK(!tokenizer
.token_is_delim() || tokenizer
.token().length() == 1);
452 if (IsAsciiWhitespace(*tokenizer
.token_begin()))
454 if (!LowerCaseEqualsASCII(tokenizer
.token(), "max-age"))
456 state
= AFTER_MAX_AGE_LABEL
;
459 case AFTER_MAX_AGE_LABEL
:
460 if (IsAsciiWhitespace(*tokenizer
.token_begin()))
462 if (*tokenizer
.token_begin() != '=')
464 DCHECK_EQ(tokenizer
.token().length(), 1U);
465 state
= AFTER_MAX_AGE_EQUALS
;
468 case AFTER_MAX_AGE_EQUALS
:
469 if (IsAsciiWhitespace(*tokenizer
.token_begin()))
471 if (!MaxAgeToInt(tokenizer
.token_begin(),
472 tokenizer
.token_end(),
475 state
= AFTER_MAX_AGE
;
479 if (IsAsciiWhitespace(*tokenizer
.token_begin()))
481 if (*tokenizer
.token_begin() != ';')
483 state
= AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER
;
486 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER
:
487 if (IsAsciiWhitespace(*tokenizer
.token_begin()))
489 if (!LowerCaseEqualsASCII(tokenizer
.token(), "includesubdomains"))
491 state
= AFTER_INCLUDE_SUBDOMAINS
;
494 case AFTER_INCLUDE_SUBDOMAINS
:
495 if (!IsAsciiWhitespace(*tokenizer
.token_begin()))
504 // We've consumed all the input. Let's see what state we ended up in.
507 case AFTER_MAX_AGE_LABEL
:
508 case AFTER_MAX_AGE_EQUALS
:
511 *max_age
= max_age_candidate
;
512 *include_subdomains
= false;
514 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER
:
516 case AFTER_INCLUDE_SUBDOMAINS
:
517 *max_age
= max_age_candidate
;
518 *include_subdomains
= true;
526 // Side pinning and superfluous certificates:
528 // In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a
529 // Subject of CN=meta. When we find one we'll currently try and parse side
530 // pinned key from it.
532 // A side pin is a key which can be pinned to, but also can be kept offline and
533 // still held by the site owner. The CN=meta certificate is just a backwards
534 // compatiable method of carrying a lump of bytes to the client. (We could use
535 // a TLS extension just as well, but it's a lot easier for admins to add extra
536 // certificates to the chain.)
538 // A TagMap represents the simple key-value structure that we use. Keys are
539 // 32-bit ints. Values are byte strings.
540 typedef std::map
<uint32
, base::StringPiece
> TagMap
;
542 // ParseTags parses a list of key-value pairs from |in| to |out| and advances
543 // |in| past the data. The key-value pair data is:
545 // u32le tag[num_tags]
546 // u16le lengths[num_tags]
548 static bool ParseTags(base::StringPiece
* in
, TagMap
*out
) {
549 // Many part of Chrome already assume little-endian. This is just to help
550 // anyone who should try to port it in the future.
551 #if defined(__BYTE_ORDER)
553 COMPILE_ASSERT(__BYTE_ORDER
== __LITTLE_ENDIAN
, assumes_little_endian
);
554 #elif defined(__BIG_ENDIAN__)
556 #error assumes little endian
560 if (in
->size() < sizeof(num_tags_16
))
563 memcpy(&num_tags_16
, in
->data(), sizeof(num_tags_16
));
564 in
->remove_prefix(sizeof(num_tags_16
));
565 unsigned num_tags
= num_tags_16
;
567 if (in
->size() < 6 * num_tags
)
570 const uint32
* tags
= reinterpret_cast<const uint32
*>(in
->data());
571 const uint16
* lens
= reinterpret_cast<const uint16
*>(
572 in
->data() + 4*num_tags
);
573 in
->remove_prefix(6*num_tags
);
576 for (unsigned i
= 0; i
< num_tags
; i
++) {
577 size_t len
= lens
[i
];
578 uint32 tag
= tags
[i
];
580 if (in
->size() < len
)
582 // tags must be in ascending order.
583 if (i
> 0 && prev_tag
>= tag
)
585 (*out
)[tag
] = base::StringPiece(in
->data(), len
);
586 in
->remove_prefix(len
);
593 // GetTag extracts the data associated with |tag| in |tags|.
594 static bool GetTag(uint32 tag
, const TagMap
& tags
, base::StringPiece
* out
) {
595 TagMap::const_iterator i
= tags
.find(tag
);
603 // kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve
604 // point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The
605 // ASN.1 interpretation of these bytes is:
607 // 0:d=0 hl=2 l= 89 cons: SEQUENCE
608 // 2:d=1 hl=2 l= 19 cons: SEQUENCE
609 // 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
610 // 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1
611 // 23:d=1 hl=2 l= 66 prim: BIT STRING
612 static const uint8 kP256SubjectPublicKeyInfoPrefix
[] = {
613 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
614 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
615 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
619 // VerifySignature returns true iff |sig| is a valid signature of
620 // |hash| by |pubkey|. The actual implementation is crypto library
622 static bool VerifySignature(const base::StringPiece
& pubkey
,
623 const base::StringPiece
& sig
,
624 const base::StringPiece
& hash
);
626 #if defined(USE_OPENSSL)
628 static EVP_PKEY
* DecodeX962P256PublicKey(
629 const base::StringPiece
& pubkey_bytes
) {
630 // The public key is an X9.62 encoded P256 point.
631 if (pubkey_bytes
.size() != 1 + 2*32)
634 std::string
pubkey_spki(
635 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix
),
636 sizeof(kP256SubjectPublicKeyInfoPrefix
));
637 pubkey_spki
+= pubkey_bytes
.as_string();
639 EVP_PKEY
* ret
= NULL
;
640 const unsigned char* der_pubkey
=
641 reinterpret_cast<const unsigned char*>(pubkey_spki
.data());
642 d2i_PUBKEY(&ret
, &der_pubkey
, pubkey_spki
.size());
646 static bool VerifySignature(const base::StringPiece
& pubkey
,
647 const base::StringPiece
& sig
,
648 const base::StringPiece
& hash
) {
649 crypto::ScopedOpenSSL
<EVP_PKEY
, EVP_PKEY_free
> secpubkey(
650 DecodeX962P256PublicKey(pubkey
));
651 if (!secpubkey
.get())
655 crypto::ScopedOpenSSL
<EC_KEY
, EC_KEY_free
> ec_key(
656 EVP_PKEY_get1_EC_KEY(secpubkey
.get()));
660 return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash
.data()),
662 reinterpret_cast<const unsigned char*>(sig
.data()),
663 sig
.size(), ec_key
.get()) == 1;
668 // DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic
669 // curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey.
670 static SECKEYPublicKey
* DecodeX962P256PublicKey(
671 const base::StringPiece
& pubkey_bytes
) {
672 // The public key is an X9.62 encoded P256 point.
673 if (pubkey_bytes
.size() != 1 + 2*32)
676 std::string
pubkey_spki(
677 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix
),
678 sizeof(kP256SubjectPublicKeyInfoPrefix
));
679 pubkey_spki
+= pubkey_bytes
.as_string();
682 memset(&der
, 0, sizeof(der
));
683 der
.data
= reinterpret_cast<uint8
*>(const_cast<char*>(pubkey_spki
.data()));
684 der
.len
= pubkey_spki
.size();
686 CERTSubjectPublicKeyInfo
* spki
= SECKEY_DecodeDERSubjectPublicKeyInfo(&der
);
689 SECKEYPublicKey
* public_key
= SECKEY_ExtractPublicKey(spki
);
690 SECKEY_DestroySubjectPublicKeyInfo(spki
);
695 static bool VerifySignature(const base::StringPiece
& pubkey
,
696 const base::StringPiece
& sig
,
697 const base::StringPiece
& hash
) {
698 SECKEYPublicKey
* secpubkey
= DecodeX962P256PublicKey(pubkey
);
703 memset(&sigitem
, 0, sizeof(sigitem
));
704 sigitem
.data
= reinterpret_cast<uint8
*>(const_cast<char*>(sig
.data()));
705 sigitem
.len
= sig
.size();
707 // |decoded_sigitem| is newly allocated, as is the data that it points to.
708 SECItem
* decoded_sigitem
= DSAU_DecodeDerSigToLen(
709 &sigitem
, SECKEY_SignatureLen(secpubkey
));
711 if (!decoded_sigitem
) {
712 SECKEY_DestroyPublicKey(secpubkey
);
717 memset(&hashitem
, 0, sizeof(hashitem
));
718 hashitem
.data
= reinterpret_cast<unsigned char*>(
719 const_cast<char*>(hash
.data()));
720 hashitem
.len
= hash
.size();
722 SECStatus rv
= PK11_Verify(secpubkey
, decoded_sigitem
, &hashitem
, NULL
);
723 SECKEY_DestroyPublicKey(secpubkey
);
724 SECITEM_FreeItem(decoded_sigitem
, PR_TRUE
);
725 return rv
== SECSuccess
;
728 #endif // !defined(USE_OPENSSL)
730 // These are the tag values that we use. Tags are little-endian on the wire and
731 // these values correspond to the ASCII of the name.
732 static const uint32 kTagALGO
= 0x4f474c41;
733 static const uint32 kTagP256
= 0x36353250;
734 static const uint32 kTagPUBK
= 0x4b425550;
735 static const uint32 kTagSIG
= 0x474953;
736 static const uint32 kTagSPIN
= 0x4e495053;
739 bool TransportSecurityState::ParseSidePin(
740 const base::StringPiece
& leaf_spki
,
741 const base::StringPiece
& in_side_info
,
742 FingerprintVector
* out_pub_key_hash
) {
743 base::StringPiece
side_info(in_side_info
);
746 if (!ParseTags(&side_info
, &outer
))
748 // trailing data is not allowed
749 if (side_info
.size())
752 base::StringPiece side_pin_bytes
;
753 if (!GetTag(kTagSPIN
, outer
, &side_pin_bytes
))
756 bool have_parsed_a_key
= false;
757 uint8 leaf_spki_hash
[crypto::kSHA256Length
];
758 bool have_leaf_spki_hash
= false;
760 while (side_pin_bytes
.size() > 0) {
762 if (!ParseTags(&side_pin_bytes
, &side_pin
))
765 base::StringPiece algo
, pubkey
, sig
;
766 if (!GetTag(kTagALGO
, side_pin
, &algo
) ||
767 !GetTag(kTagPUBK
, side_pin
, &pubkey
) ||
768 !GetTag(kTagSIG
, side_pin
, &sig
)) {
772 if (algo
.size() != sizeof(kTagP256
) ||
773 0 != memcmp(algo
.data(), &kTagP256
, sizeof(kTagP256
))) {
774 // We don't support anything but P256 at the moment.
778 if (!have_leaf_spki_hash
) {
779 crypto::SHA256HashString(
780 leaf_spki
.as_string(), leaf_spki_hash
, sizeof(leaf_spki_hash
));
781 have_leaf_spki_hash
= true;
784 if (VerifySignature(pubkey
, sig
, base::StringPiece(
785 reinterpret_cast<const char*>(leaf_spki_hash
),
786 sizeof(leaf_spki_hash
)))) {
789 reinterpret_cast<const uint8
*>(pubkey
.data()),
792 out_pub_key_hash
->push_back(fpr
);
793 have_parsed_a_key
= true;
797 return have_parsed_a_key
;
800 // This function converts the binary hashes, which we store in
801 // |enabled_hosts_|, to a base64 string which we can include in a JSON file.
802 static std::string
HashedDomainToExternalString(const std::string
& hashed
) {
804 CHECK(base::Base64Encode(hashed
, &out
));
808 // This inverts |HashedDomainToExternalString|, above. It turns an external
809 // string (from a JSON file) into an internal (binary) string.
810 static std::string
ExternalStringToHashedDomain(const std::string
& external
) {
812 if (!base::Base64Decode(external
, &out
) ||
813 out
.size() != crypto::kSHA256Length
) {
814 return std::string();
820 static ListValue
* SPKIHashesToListValue(const FingerprintVector
& hashes
) {
821 ListValue
* pins
= new ListValue
;
823 for (FingerprintVector::const_iterator i
= hashes
.begin();
824 i
!= hashes
.end(); ++i
) {
825 std::string
hash_str(reinterpret_cast<const char*>(i
->data
),
828 base::Base64Encode(hash_str
, &b64
);
829 pins
->Append(new StringValue("sha1/" + b64
));
835 bool TransportSecurityState::Serialise(std::string
* output
) {
836 DCHECK(CalledOnValidThread());
838 DictionaryValue toplevel
;
839 base::Time now
= base::Time::Now();
840 for (std::map
<std::string
, DomainState
>::const_iterator
841 i
= enabled_hosts_
.begin(); i
!= enabled_hosts_
.end(); ++i
) {
842 DictionaryValue
* state
= new DictionaryValue
;
843 state
->SetBoolean("include_subdomains", i
->second
.include_subdomains
);
844 state
->SetDouble("created", i
->second
.created
.ToDoubleT());
845 state
->SetDouble("expiry", i
->second
.expiry
.ToDoubleT());
846 state
->SetDouble("dynamic_spki_hashes_expiry",
847 i
->second
.dynamic_spki_hashes_expiry
.ToDoubleT());
849 switch (i
->second
.mode
) {
850 case DomainState::MODE_STRICT
:
851 state
->SetString("mode", "strict");
853 case DomainState::MODE_SPDY_ONLY
:
854 state
->SetString("mode", "spdy-only");
856 case DomainState::MODE_PINNING_ONLY
:
857 state
->SetString("mode", "pinning-only");
860 NOTREACHED() << "DomainState with unknown mode";
865 state
->Set("preloaded_spki_hashes",
866 SPKIHashesToListValue(i
->second
.preloaded_spki_hashes
));
868 if (now
< i
->second
.dynamic_spki_hashes_expiry
) {
869 state
->Set("dynamic_spki_hashes",
870 SPKIHashesToListValue(i
->second
.dynamic_spki_hashes
));
873 toplevel
.Set(HashedDomainToExternalString(i
->first
), state
);
876 base::JSONWriter::Write(&toplevel
, true /* pretty print */, output
);
880 bool TransportSecurityState::LoadEntries(const std::string
& input
,
882 DCHECK(CalledOnValidThread());
884 enabled_hosts_
.clear();
885 return Deserialise(input
, dirty
, &enabled_hosts_
);
888 static bool AddHash(const std::string
& type_and_base64
,
889 FingerprintVector
* out
) {
890 SHA1Fingerprint hash
;
892 if (!TransportSecurityState::ParsePin(type_and_base64
, &hash
))
895 out
->push_back(hash
);
899 static void SPKIHashesFromListValue(FingerprintVector
* hashes
,
900 const ListValue
& pins
) {
901 size_t num_pins
= pins
.GetSize();
902 for (size_t i
= 0; i
< num_pins
; ++i
) {
903 std::string type_and_base64
;
904 if (pins
.GetString(i
, &type_and_base64
))
905 AddHash(type_and_base64
, hashes
);
910 bool TransportSecurityState::Deserialise(
911 const std::string
& input
,
913 std::map
<std::string
, DomainState
>* out
) {
914 scoped_ptr
<Value
> value(
915 base::JSONReader::Read(input
, false /* do not allow trailing commas */));
916 if (!value
.get() || !value
->IsType(Value::TYPE_DICTIONARY
))
919 DictionaryValue
* dict_value
= reinterpret_cast<DictionaryValue
*>(value
.get());
920 const base::Time
current_time(base::Time::Now());
921 bool dirtied
= false;
923 for (DictionaryValue::key_iterator i
= dict_value
->begin_keys();
924 i
!= dict_value
->end_keys(); ++i
) {
925 DictionaryValue
* state
;
926 if (!dict_value
->GetDictionaryWithoutPathExpansion(*i
, &state
))
929 bool include_subdomains
;
930 std::string mode_string
;
933 double dynamic_spki_hashes_expiry
= 0.0;
935 if (!state
->GetBoolean("include_subdomains", &include_subdomains
) ||
936 !state
->GetString("mode", &mode_string
) ||
937 !state
->GetDouble("expiry", &expiry
)) {
941 // Don't fail if this key is not present.
942 (void) state
->GetDouble("dynamic_spki_hashes_expiry",
943 &dynamic_spki_hashes_expiry
);
945 ListValue
* pins_list
= NULL
;
946 FingerprintVector preloaded_spki_hashes
;
947 if (state
->GetList("preloaded_spki_hashes", &pins_list
))
948 SPKIHashesFromListValue(&preloaded_spki_hashes
, *pins_list
);
950 FingerprintVector dynamic_spki_hashes
;
951 if (state
->GetList("dynamic_spki_hashes", &pins_list
))
952 SPKIHashesFromListValue(&dynamic_spki_hashes
, *pins_list
);
954 DomainState::Mode mode
;
955 if (mode_string
== "strict") {
956 mode
= DomainState::MODE_STRICT
;
957 } else if (mode_string
== "spdy-only") {
958 mode
= DomainState::MODE_SPDY_ONLY
;
959 } else if (mode_string
== "pinning-only") {
960 mode
= DomainState::MODE_PINNING_ONLY
;
962 LOG(WARNING
) << "Unknown TransportSecurityState mode string found: "
967 base::Time expiry_time
= base::Time::FromDoubleT(expiry
);
968 base::Time dynamic_spki_hashes_expiry_time
=
969 base::Time::FromDoubleT(dynamic_spki_hashes_expiry
);
970 base::Time created_time
;
971 if (state
->GetDouble("created", &created
)) {
972 created_time
= base::Time::FromDoubleT(created
);
974 // We're migrating an old entry with no creation date. Make sure we
975 // write the new date back in a reasonable time frame.
977 created_time
= base::Time::Now();
980 if (expiry_time
<= current_time
&&
981 dynamic_spki_hashes_expiry_time
<= current_time
) {
982 // Make sure we dirty the state if we drop an entry.
987 std::string hashed
= ExternalStringToHashedDomain(*i
);
988 if (hashed
.empty()) {
993 DomainState new_state
;
994 new_state
.mode
= mode
;
995 new_state
.created
= created_time
;
996 new_state
.expiry
= expiry_time
;
997 new_state
.include_subdomains
= include_subdomains
;
998 new_state
.preloaded_spki_hashes
= preloaded_spki_hashes
;
999 new_state
.dynamic_spki_hashes
= dynamic_spki_hashes
;
1000 new_state
.dynamic_spki_hashes_expiry
= dynamic_spki_hashes_expiry_time
;
1001 (*out
)[hashed
] = new_state
;
1008 TransportSecurityState::~TransportSecurityState() {
1011 void TransportSecurityState::DirtyNotify() {
1012 DCHECK(CalledOnValidThread());
1015 delegate_
->StateIsDirty(this);
1019 std::string
TransportSecurityState::CanonicalizeHost(const std::string
& host
) {
1020 // We cannot perform the operations as detailed in the spec here as |host|
1021 // has already undergone IDN processing before it reached us. Thus, we check
1022 // that there are no invalid characters in the host and lowercase the result.
1024 std::string new_host
;
1025 if (!DNSDomainFromDot(host
, &new_host
)) {
1026 // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole
1027 // name is >255 bytes. However, search terms can have those properties.
1028 return std::string();
1031 for (size_t i
= 0; new_host
[i
]; i
+= new_host
[i
] + 1) {
1032 const unsigned label_length
= static_cast<unsigned>(new_host
[i
]);
1036 for (size_t j
= 0; j
< label_length
; ++j
) {
1037 // RFC 3490, 4.1, step 3
1038 if (!IsSTD3ASCIIValidCharacter(new_host
[i
+ 1 + j
]))
1039 return std::string();
1041 new_host
[i
+ 1 + j
] = tolower(new_host
[i
+ 1 + j
]);
1045 if (new_host
[i
+ 1] == '-' ||
1046 new_host
[i
+ label_length
] == '-') {
1047 return std::string();
1054 // |ReportUMAOnPinFailure| uses these to report which domain was associated
1055 // with the public key pinning failure.
1057 // DO NOT CHANGE THE ORDERING OF THESE NAMES OR REMOVE ANY OF THEM. Add new
1058 // domains at the END of the listing (but before DOMAIN_NUM_EVENTS).
1059 enum SecondLevelDomainName
{
1064 DOMAIN_GOOGLE_ANALYTICS_COM
,
1065 DOMAIN_GOOGLEPLEX_COM
,
1067 DOMAIN_GOOGLEUSERCONTENT_COM
,
1069 DOMAIN_GOOGLEAPIS_COM
,
1070 DOMAIN_GOOGLEADSERVICES_COM
,
1071 DOMAIN_GOOGLECODE_COM
,
1073 DOMAIN_GOOGLESYNDICATION_COM
,
1074 DOMAIN_DOUBLECLICK_NET
,
1077 DOMAIN_GOOGLEMAIL_COM
,
1078 DOMAIN_GOOGLEGROUPS_COM
,
1080 DOMAIN_TORPROJECT_ORG
,
1085 DOMAIN_AKAMAIHD_NET
,
1087 // Boundary value for UMA_HISTOGRAM_ENUMERATION:
1091 // PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site.
1092 // The validated certificate chain for the site must not include any of
1093 // |excluded_hashes| and must include one or more of |required_hashes|.
1094 struct PublicKeyPins
{
1095 const char* const* required_hashes
;
1096 const char* const* excluded_hashes
;
1099 struct HSTSPreload
{
1101 bool include_subdomains
;
1103 bool https_required
;
1105 SecondLevelDomainName second_level_domain_name
;
1108 static bool HasPreload(const struct HSTSPreload
* entries
, size_t num_entries
,
1109 const std::string
& canonicalized_host
, size_t i
,
1110 TransportSecurityState::DomainState
* out
, bool* ret
) {
1111 for (size_t j
= 0; j
< num_entries
; j
++) {
1112 if (entries
[j
].length
== canonicalized_host
.size() - i
&&
1113 memcmp(entries
[j
].dns_name
, &canonicalized_host
[i
],
1114 entries
[j
].length
) == 0) {
1115 if (!entries
[j
].include_subdomains
&& i
!= 0) {
1118 out
->include_subdomains
= entries
[j
].include_subdomains
;
1120 if (!entries
[j
].https_required
)
1121 out
->mode
= TransportSecurityState::DomainState::MODE_PINNING_ONLY
;
1122 if (entries
[j
].pins
.required_hashes
) {
1123 const char* const* hash
= entries
[j
].pins
.required_hashes
;
1125 bool ok
= AddHash(*hash
, &out
->preloaded_spki_hashes
);
1126 DCHECK(ok
) << " failed to parse " << *hash
;
1130 if (entries
[j
].pins
.excluded_hashes
) {
1131 const char* const* hash
= entries
[j
].pins
.excluded_hashes
;
1133 bool ok
= AddHash(*hash
, &out
->bad_preloaded_spki_hashes
);
1134 DCHECK(ok
) << " failed to parse " << *hash
;
1145 // kNoRejectedPublicKeys is a placeholder for when no public keys are rejected.
1146 static const char* const kNoRejectedPublicKeys
[] = {
1150 static const char* const kGoogleAcceptableCerts
[] = {
1151 kSPKIHash_VeriSignClass3
,
1152 kSPKIHash_VeriSignClass3_G3
,
1153 kSPKIHash_Google1024
,
1154 kSPKIHash_Google2048
,
1155 kSPKIHash_EquifaxSecureCA
,
1158 static const char* const kGoogleRejectedCerts
[] = {
1161 kSPKIHash_TCTrustCenter
,
1165 #define kGooglePins { \
1166 kGoogleAcceptableCerts, \
1167 kGoogleRejectedCerts, \
1170 static const char* const kTorAcceptableCerts
[] = {
1172 kSPKIHash_DigiCertEVRoot
,
1178 #define kTorPins { \
1179 kTorAcceptableCerts, \
1180 kNoRejectedPublicKeys, \
1183 static const char* const kTwitterComAcceptableCerts
[] = {
1184 kSPKIHash_VeriSignClass1
,
1185 kSPKIHash_VeriSignClass3
,
1186 kSPKIHash_VeriSignClass3_G4
,
1187 kSPKIHash_VeriSignClass4_G3
,
1188 kSPKIHash_VeriSignClass3_G3
,
1189 kSPKIHash_VeriSignClass1_G3
,
1190 kSPKIHash_VeriSignClass2_G3
,
1191 kSPKIHash_VeriSignClass3_G2
,
1192 kSPKIHash_VeriSignClass2_G2
,
1193 kSPKIHash_VeriSignClass3_G5
,
1194 kSPKIHash_VeriSignUniversal
,
1195 kSPKIHash_GeoTrustGlobal
,
1196 kSPKIHash_GeoTrustGlobal2
,
1197 kSPKIHash_GeoTrustUniversal
,
1198 kSPKIHash_GeoTrustUniversal2
,
1199 kSPKIHash_GeoTrustPrimary
,
1200 kSPKIHash_GeoTrustPrimary_G2
,
1201 kSPKIHash_GeoTrustPrimary_G3
,
1205 #define kTwitterComPins { \
1206 kTwitterComAcceptableCerts, \
1207 kNoRejectedPublicKeys, \
1210 // kTwitterCDNAcceptableCerts are the set of public keys valid for Twitter's
1211 // CDNs, which includes all the keys from kTwitterComAcceptableCerts.
1212 static const char* const kTwitterCDNAcceptableCerts
[] = {
1213 kSPKIHash_VeriSignClass1
,
1214 kSPKIHash_VeriSignClass3
,
1215 kSPKIHash_VeriSignClass3_G4
,
1216 kSPKIHash_VeriSignClass4_G3
,
1217 kSPKIHash_VeriSignClass3_G3
,
1218 kSPKIHash_VeriSignClass1_G3
,
1219 kSPKIHash_VeriSignClass2_G3
,
1220 kSPKIHash_VeriSignClass3_G2
,
1221 kSPKIHash_VeriSignClass2_G2
,
1222 kSPKIHash_VeriSignClass3_G5
,
1223 kSPKIHash_VeriSignUniversal
,
1224 kSPKIHash_GeoTrustGlobal
,
1225 kSPKIHash_GeoTrustGlobal2
,
1226 kSPKIHash_GeoTrustUniversal
,
1227 kSPKIHash_GeoTrustUniversal2
,
1228 kSPKIHash_GeoTrustPrimary
,
1229 kSPKIHash_GeoTrustPrimary_G2
,
1230 kSPKIHash_GeoTrustPrimary_G3
,
1233 kSPKIHash_Entrust_2048
,
1234 kSPKIHash_Entrust_EV
,
1235 kSPKIHash_Entrust_G2
,
1236 kSPKIHash_Entrust_SSL
,
1237 kSPKIHash_AAACertificateServices
,
1238 kSPKIHash_AddTrustClass1CARoot
,
1239 kSPKIHash_AddTrustExternalCARoot
,
1240 kSPKIHash_AddTrustPublicCARoot
,
1241 kSPKIHash_AddTrustQualifiedCARoot
,
1242 kSPKIHash_COMODOCertificationAuthority
,
1243 kSPKIHash_SecureCertificateServices
,
1244 kSPKIHash_TrustedCertificateServices
,
1245 kSPKIHash_UTNDATACorpSGC
,
1246 kSPKIHash_UTNUSERFirstClientAuthenticationandEmail
,
1247 kSPKIHash_UTNUSERFirstHardware
,
1248 kSPKIHash_UTNUSERFirstObject
,
1249 kSPKIHash_GTECyberTrustGlobalRoot
,
1252 #define kTwitterCDNPins { \
1253 kTwitterCDNAcceptableCerts, \
1254 kNoRejectedPublicKeys, \
1257 // kTestAcceptableCerts doesn't actually match any public keys and is used
1258 // with "pinningtest.appspot.com", below, to test if pinning is active.
1259 static const char* const kTestAcceptableCerts
[] = {
1260 "sha1/AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
1263 #define kTestPins { \
1264 kTestAcceptableCerts, \
1265 kNoRejectedPublicKeys, \
1272 #if defined(OS_CHROMEOS)
1273 static const bool kTwitterHSTS
= true;
1275 static const bool kTwitterHSTS
= false;
1278 // In the medium term this list is likely to just be hardcoded here. This
1279 // slightly odd form removes the need for additional relocations records.
1280 static const struct HSTSPreload kPreloadedSTS
[] = {
1281 // (*.)google.com, iff using SSL must use an acceptable certificate.
1282 {12, true, "\006google\003com", false, kGooglePins
,
1283 DOMAIN_GOOGLE_COM
},
1284 {25, true, "\013pinningtest\007appspot\003com", false,
1285 kTestPins
, DOMAIN_APPSPOT_COM
},
1286 // Now we force HTTPS for subtrees of google.com.
1287 {19, true, "\006health\006google\003com", true, kGooglePins
,
1288 DOMAIN_GOOGLE_COM
},
1289 {21, true, "\010checkout\006google\003com", true, kGooglePins
,
1290 DOMAIN_GOOGLE_COM
},
1291 {19, true, "\006chrome\006google\003com", true, kGooglePins
,
1292 DOMAIN_GOOGLE_COM
},
1293 {17, true, "\004docs\006google\003com", true, kGooglePins
,
1294 DOMAIN_GOOGLE_COM
},
1295 {18, true, "\005sites\006google\003com", true, kGooglePins
,
1296 DOMAIN_GOOGLE_COM
},
1297 {25, true, "\014spreadsheets\006google\003com", true,
1298 kGooglePins
, DOMAIN_GOOGLE_COM
},
1299 {22, false, "\011appengine\006google\003com", true,
1300 kGooglePins
, DOMAIN_GOOGLE_COM
},
1301 {22, true, "\011encrypted\006google\003com", true, kGooglePins
,
1302 DOMAIN_GOOGLE_COM
},
1303 {21, true, "\010accounts\006google\003com", true, kGooglePins
,
1304 DOMAIN_GOOGLE_COM
},
1305 {21, true, "\010profiles\006google\003com", true, kGooglePins
,
1306 DOMAIN_GOOGLE_COM
},
1307 {17, true, "\004mail\006google\003com", true, kGooglePins
,
1308 DOMAIN_GOOGLE_COM
},
1309 {23, true, "\012talkgadget\006google\003com", true,
1310 kGooglePins
, DOMAIN_GOOGLE_COM
},
1311 {17, true, "\004talk\006google\003com", true, kGooglePins
,
1312 DOMAIN_GOOGLE_COM
},
1313 {29, true, "\020hostedtalkgadget\006google\003com", true,
1314 kGooglePins
, DOMAIN_GOOGLE_COM
},
1315 {17, true, "\004plus\006google\003com", true, kGooglePins
,
1316 DOMAIN_GOOGLE_COM
},
1317 // Other Google-related domains that must use HTTPS.
1318 {20, true, "\006market\007android\003com", true, kGooglePins
,
1319 DOMAIN_ANDROID_COM
},
1320 {26, true, "\003ssl\020google-analytics\003com", true,
1321 kGooglePins
, DOMAIN_GOOGLE_ANALYTICS_COM
},
1322 {18, true, "\005drive\006google\003com", true, kGooglePins
,
1323 DOMAIN_GOOGLE_COM
},
1324 {16, true, "\012googleplex\003com", true, kGooglePins
,
1325 DOMAIN_GOOGLEPLEX_COM
},
1326 {19, true, "\006groups\006google\003com", true, kGooglePins
,
1327 DOMAIN_GOOGLE_COM
},
1328 // Other Google-related domains that must use an acceptable certificate
1330 {11, true, "\005ytimg\003com", false, kGooglePins
,
1332 {23, true, "\021googleusercontent\003com", false, kGooglePins
,
1333 DOMAIN_GOOGLEUSERCONTENT_COM
},
1334 {13, true, "\007youtube\003com", false, kGooglePins
,
1335 DOMAIN_YOUTUBE_COM
},
1336 {16, true, "\012googleapis\003com", false, kGooglePins
,
1337 DOMAIN_GOOGLEAPIS_COM
},
1338 {22, true, "\020googleadservices\003com", false, kGooglePins
,
1339 DOMAIN_GOOGLEADSERVICES_COM
},
1340 {16, true, "\012googlecode\003com", false, kGooglePins
,
1341 DOMAIN_GOOGLECODE_COM
},
1342 {13, true, "\007appspot\003com", false, kGooglePins
,
1343 DOMAIN_APPSPOT_COM
},
1344 {23, true, "\021googlesyndication\003com", false, kGooglePins
,
1345 DOMAIN_GOOGLESYNDICATION_COM
},
1346 {17, true, "\013doubleclick\003net", false, kGooglePins
,
1347 DOMAIN_DOUBLECLICK_NET
},
1348 {17, true, "\003ssl\007gstatic\003com", false, kGooglePins
,
1349 DOMAIN_GSTATIC_COM
},
1350 // Exclude the learn.doubleclick.net subdomain because it uses a different
1352 {23, true, "\005learn\013doubleclick\003net", false, kNoPins
, DOMAIN_NOT_PINNED
},
1353 // Now we force HTTPS for other sites that have requested it.
1354 {16, false, "\003www\006paypal\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1355 {16, false, "\003www\006elanex\003biz", true, kNoPins
, DOMAIN_NOT_PINNED
},
1356 {12, true, "\006jottit\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1357 {19, true, "\015sunshinepress\003org", true, kNoPins
, DOMAIN_NOT_PINNED
},
1358 {21, false, "\003www\013noisebridge\003net", true, kNoPins
,
1359 DOMAIN_NOT_PINNED
},
1360 {10, false, "\004neg9\003org", true, kNoPins
, DOMAIN_NOT_PINNED
},
1361 {12, true, "\006riseup\003net", true, kNoPins
, DOMAIN_NOT_PINNED
},
1362 {11, false, "\006factor\002cc", true, kNoPins
, DOMAIN_NOT_PINNED
},
1363 {22, false, "\007members\010mayfirst\003org", true, kNoPins
, DOMAIN_NOT_PINNED
},
1364 {22, false, "\007support\010mayfirst\003org", true, kNoPins
, DOMAIN_NOT_PINNED
},
1365 {17, false, "\002id\010mayfirst\003org", true, kNoPins
, DOMAIN_NOT_PINNED
},
1366 {20, false, "\005lists\010mayfirst\003org", true, kNoPins
, DOMAIN_NOT_PINNED
},
1367 {19, true, "\015splendidbacon\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1368 {28, false, "\016aladdinschools\007appspot\003com", true, kNoPins
,
1369 DOMAIN_NOT_PINNED
},
1370 {14, true, "\011ottospora\002nl", true, kNoPins
, DOMAIN_NOT_PINNED
},
1371 {25, false, "\003www\017paycheckrecords\003com", true, kNoPins
,
1372 DOMAIN_NOT_PINNED
},
1373 {14, false, "\010lastpass\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1374 {18, false, "\003www\010lastpass\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1375 {14, true, "\010keyerror\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1376 {13, false, "\010entropia\002de", true, kNoPins
, DOMAIN_NOT_PINNED
},
1377 {17, false, "\003www\010entropia\002de", true, kNoPins
, DOMAIN_NOT_PINNED
},
1378 {11, true, "\005romab\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1379 {16, false, "\012logentries\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1380 {20, false, "\003www\012logentries\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1381 {12, true, "\006stripe\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1382 {27, true, "\025cloudsecurityalliance\003org", true, kNoPins
,
1383 DOMAIN_NOT_PINNED
},
1384 {15, true, "\005login\004sapo\002pt", true, kNoPins
, DOMAIN_NOT_PINNED
},
1385 {19, true, "\015mattmccutchen\003net", true, kNoPins
, DOMAIN_NOT_PINNED
},
1386 {11, true, "\006betnet\002fr", true, kNoPins
, DOMAIN_NOT_PINNED
},
1387 {13, true, "\010uprotect\002it", true, kNoPins
, DOMAIN_NOT_PINNED
},
1388 {14, false, "\010squareup\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1389 {9, true, "\004cert\002se", true, kNoPins
, DOMAIN_NOT_PINNED
},
1390 {11, true, "\006crypto\002is", true, kNoPins
, DOMAIN_NOT_PINNED
},
1391 {20, true, "\005simon\007butcher\004name", true, kNoPins
, DOMAIN_NOT_PINNED
},
1392 {10, true, "\004linx\003net", true, kNoPins
, DOMAIN_NOT_PINNED
},
1393 {13, false, "\007dropcam\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1394 {17, false, "\003www\007dropcam\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1395 {30, true, "\010ebanking\014indovinabank\003com\002vn", true, kNoPins
,
1396 DOMAIN_NOT_PINNED
},
1397 {13, false, "\007epoxate\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1398 {16, false, "\012torproject\003org", true, kTorPins
,
1399 DOMAIN_TORPROJECT_ORG
},
1400 {21, true, "\004blog\012torproject\003org", true, kTorPins
,
1401 DOMAIN_TORPROJECT_ORG
},
1402 {22, true, "\005check\012torproject\003org", true, kTorPins
,
1403 DOMAIN_TORPROJECT_ORG
},
1404 {20, true, "\003www\012torproject\003org", true, kTorPins
,
1405 DOMAIN_TORPROJECT_ORG
},
1406 {22, true, "\003www\014moneybookers\003com", true, kNoPins
,
1407 DOMAIN_NOT_PINNED
},
1408 {17, false, "\013ledgerscope\003net", true, kNoPins
, DOMAIN_NOT_PINNED
},
1409 {21, false, "\003www\013ledgerscope\003net", true, kNoPins
,
1410 DOMAIN_NOT_PINNED
},
1411 {10, false, "\004kyps\003net", true, kNoPins
, DOMAIN_NOT_PINNED
},
1412 {14, false, "\003www\004kyps\003net", true, kNoPins
, DOMAIN_NOT_PINNED
},
1413 {17, true, "\003app\007recurly\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1414 {17, true, "\003api\007recurly\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1415 {13, false, "\007greplin\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1416 {17, false, "\003www\007greplin\003com", true, kNoPins
, DOMAIN_NOT_PINNED
},
1417 {27, true, "\006luneta\016nearbuysystems\003com", true, kNoPins
,
1418 DOMAIN_NOT_PINNED
},
1419 {12, true, "\006ubertt\003org", true, kNoPins
, DOMAIN_NOT_PINNED
},
1420 {9, true, "\004pixi\002me", true, kNoPins
, DOMAIN_NOT_PINNED
},
1422 // Twitter pins disabled in order to track down pinning failures --agl
1423 {13, false, "\007twitter\003com", kTwitterHSTS
,
1424 kTwitterComPins
, DOMAIN_TWITTER_COM
},
1425 {17, true, "\003www\007twitter\003com", kTwitterHSTS
,
1426 kTwitterComPins
, DOMAIN_TWITTER_COM
},
1427 {17, true, "\003api\007twitter\003com", kTwitterHSTS
,
1428 kTwitterCDNPins
, DOMAIN_TWITTER_COM
},
1429 {19, true, "\005oauth\007twitter\003com", kTwitterHSTS
,
1430 kTwitterComPins
, DOMAIN_TWITTER_COM
},
1431 {20, true, "\006mobile\007twitter\003com", kTwitterHSTS
,
1432 kTwitterComPins
, DOMAIN_TWITTER_COM
},
1433 {17, true, "\003dev\007twitter\003com", kTwitterHSTS
,
1434 kTwitterComPins
, DOMAIN_TWITTER_COM
},
1435 {22, true, "\010business\007twitter\003com", kTwitterHSTS
,
1436 kTwitterComPins
, DOMAIN_TWITTER_COM
},
1437 {22, true, "\010platform\007twitter\003com", false,
1438 kTwitterCDNPins
, DOMAIN_TWITTER_COM
},
1439 {15, true, "\003si0\005twimg\003com", false, kTwitterCDNPins
,
1441 {23, true, "\010twimg0-a\010akamaihd\003net", false,
1442 kTwitterCDNPins
, DOMAIN_AKAMAIHD_NET
},
1444 static const size_t kNumPreloadedSTS
= ARRAYSIZE_UNSAFE(kPreloadedSTS
);
1446 static const struct HSTSPreload kPreloadedSNISTS
[] = {
1447 // These SNI-only domains must always use HTTPS.
1448 {11, false, "\005gmail\003com", true, kGooglePins
,
1450 {16, false, "\012googlemail\003com", true, kGooglePins
,
1451 DOMAIN_GOOGLEMAIL_COM
},
1452 {15, false, "\003www\005gmail\003com", true, kGooglePins
,
1454 {20, false, "\003www\012googlemail\003com", true, kGooglePins
,
1455 DOMAIN_GOOGLEMAIL_COM
},
1456 // These SNI-only domains must use an acceptable certificate iff using
1458 {22, true, "\020google-analytics\003com", false, kGooglePins
,
1459 DOMAIN_GOOGLE_ANALYTICS_COM
},
1460 // www. requires SNI.
1461 {18, true, "\014googlegroups\003com", false, kGooglePins
,
1462 DOMAIN_GOOGLEGROUPS_COM
},
1464 static const size_t kNumPreloadedSNISTS
= ARRAYSIZE_UNSAFE(kPreloadedSNISTS
);
1466 // Returns the HSTSPreload entry for the |canonicalized_host| in |entries|,
1467 // or NULL if there is none. Prefers exact hostname matches to those that
1468 // match only because HSTSPreload.include_subdomains is true.
1470 // |canonicalized_host| should be the hostname as canonicalized by
1471 // CanonicalizeHost.
1472 static const struct HSTSPreload
* GetHSTSPreload(
1473 const std::string
& canonicalized_host
,
1474 const struct HSTSPreload
* entries
,
1475 size_t num_entries
) {
1476 for (size_t i
= 0; canonicalized_host
[i
]; i
+= canonicalized_host
[i
] + 1) {
1477 for (size_t j
= 0; j
< num_entries
; j
++) {
1478 const struct HSTSPreload
* entry
= entries
+ j
;
1480 if (i
!= 0 && !entry
->include_subdomains
)
1483 if (entry
->length
== canonicalized_host
.size() - i
&&
1484 memcmp(entry
->dns_name
, &canonicalized_host
[i
], entry
->length
) == 0) {
1494 bool TransportSecurityState::IsGooglePinnedProperty(const std::string
& host
,
1495 bool sni_available
) {
1496 std::string canonicalized_host
= CanonicalizeHost(host
);
1497 const struct HSTSPreload
* entry
=
1498 GetHSTSPreload(canonicalized_host
, kPreloadedSTS
, kNumPreloadedSTS
);
1500 if (entry
&& entry
->pins
.required_hashes
== kGoogleAcceptableCerts
)
1503 if (sni_available
) {
1504 entry
= GetHSTSPreload(canonicalized_host
, kPreloadedSNISTS
,
1505 kNumPreloadedSNISTS
);
1506 if (entry
&& entry
->pins
.required_hashes
== kGoogleAcceptableCerts
)
1514 void TransportSecurityState::ReportUMAOnPinFailure(const std::string
& host
) {
1515 std::string canonicalized_host
= CanonicalizeHost(host
);
1517 const struct HSTSPreload
* entry
=
1518 GetHSTSPreload(canonicalized_host
, kPreloadedSTS
, kNumPreloadedSTS
);
1521 entry
= GetHSTSPreload(canonicalized_host
, kPreloadedSNISTS
,
1522 kNumPreloadedSNISTS
);
1526 DCHECK(entry
->pins
.required_hashes
);
1527 DCHECK(entry
->second_level_domain_name
!= DOMAIN_NOT_PINNED
);
1529 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain",
1530 entry
->second_level_domain_name
, DOMAIN_NUM_EVENTS
);
1533 // IsPreloadedSTS returns true if the canonicalized hostname should always be
1534 // considered to have STS enabled.
1535 bool TransportSecurityState::IsPreloadedSTS(
1536 const std::string
& canonicalized_host
,
1539 DCHECK(CalledOnValidThread());
1541 out
->preloaded
= true;
1542 out
->mode
= DomainState::MODE_STRICT
;
1543 out
->include_subdomains
= false;
1545 for (size_t i
= 0; canonicalized_host
[i
]; i
+= canonicalized_host
[i
] + 1) {
1546 std::string
host_sub_chunk(&canonicalized_host
[i
],
1547 canonicalized_host
.size() - i
);
1548 out
->domain
= DNSDomainToString(host_sub_chunk
);
1549 std::string
hashed_host(HashHost(host_sub_chunk
));
1550 if (forced_hosts_
.find(hashed_host
) != forced_hosts_
.end()) {
1551 *out
= forced_hosts_
[hashed_host
];
1552 out
->domain
= DNSDomainToString(host_sub_chunk
);
1553 out
->preloaded
= true;
1557 if (HasPreload(kPreloadedSTS
, kNumPreloadedSTS
, canonicalized_host
, i
, out
,
1561 if (sni_available
&&
1562 HasPreload(kPreloadedSNISTS
, kNumPreloadedSNISTS
, canonicalized_host
, i
,
1571 static std::string
HashesToBase64String(
1572 const FingerprintVector
& hashes
) {
1573 std::vector
<std::string
> hashes_strs
;
1574 for (FingerprintVector::const_iterator
1575 i
= hashes
.begin(); i
!= hashes
.end(); i
++) {
1577 const std::string
hash_str(reinterpret_cast<const char*>(i
->data
),
1579 base::Base64Encode(hash_str
, &s
);
1580 hashes_strs
.push_back(s
);
1583 return JoinString(hashes_strs
, ',');
1586 TransportSecurityState::DomainState::DomainState()
1587 : mode(MODE_STRICT
),
1588 created(base::Time::Now()),
1589 include_subdomains(false),
1593 TransportSecurityState::DomainState::~DomainState() {
1596 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted(
1597 const FingerprintVector
& hashes
) {
1599 if (HashesIntersect(bad_preloaded_spki_hashes
, hashes
)) {
1600 LOG(ERROR
) << "Rejecting public key chain for domain " << domain
1601 << ". Validated chain: " << HashesToBase64String(hashes
)
1602 << ", matches one or more bad hashes: "
1603 << HashesToBase64String(bad_preloaded_spki_hashes
);
1607 if (!(dynamic_spki_hashes
.empty() && preloaded_spki_hashes
.empty()) &&
1608 !HashesIntersect(dynamic_spki_hashes
, hashes
) &&
1609 !HashesIntersect(preloaded_spki_hashes
, hashes
)) {
1610 LOG(ERROR
) << "Rejecting public key chain for domain " << domain
1611 << ". Validated chain: " << HashesToBase64String(hashes
)
1612 << ", expected: " << HashesToBase64String(dynamic_spki_hashes
)
1613 << " or: " << HashesToBase64String(preloaded_spki_hashes
);
1621 bool TransportSecurityState::DomainState::IsMoreStrict(
1622 const TransportSecurityState::DomainState
& other
) {
1623 if (this->dynamic_spki_hashes
.empty() && !other
.dynamic_spki_hashes
.empty())
1626 if (!this->include_subdomains
&& other
.include_subdomains
)
1632 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS()
1634 return mode
== MODE_STRICT
;