Updating trunk VERSION from 1014.0 to 1015.0
[chromium-blink-merge.git] / net / base / transport_security_state.cc
blobf3668a2b84db6f73d5282275ba90f2da0132a202
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)
11 #include <cryptohi.h>
12 #include <hasht.h>
13 #include <keyhi.h>
14 #include <pk11pub.h>
15 #include <nspr.h>
16 #endif
18 #include <algorithm>
19 #include <utility>
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"
45 #endif
47 namespace net {
49 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year
51 TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts)
52 : delegate_(NULL) {
53 if (!hsts_hosts.empty()) {
54 bool dirty;
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) {
67 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())
76 return;
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)) {
84 return;
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;
99 DirtyNotify();
102 bool TransportSecurityState::DeleteHost(const std::string& host) {
103 DCHECK(CalledOnValidThread());
105 const std::string canonicalized_host = CanonicalizeHost(host);
106 if (canonicalized_host.empty())
107 return false;
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);
113 DirtyNotify();
114 return true;
116 return false;
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())
145 return false;
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)
157 return true;
159 std::map<std::string, DomainState>::iterator j =
160 enabled_hosts_.find(HashHost(host_sub_chunk));
161 if (j == enabled_hosts_.end())
162 continue;
164 if (current_time > j->second.expiry &&
165 current_time > j->second.dynamic_spki_hashes_expiry) {
166 enabled_hosts_.erase(j);
167 DirtyNotify();
168 continue;
171 *result = j->second;
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.
176 if (i == 0)
177 return true;
179 return j->second.include_subdomains;
182 return false;
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) {
193 dirtied = true;
194 enabled_hosts_.erase(i++);
195 } else {
196 i++;
200 if (dirtied)
201 DirtyNotify();
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,
210 int* result) {
211 const std::string s(begin, end);
212 char* endptr;
213 long int i = strtol(s.data(), &endptr, 10 /* base */);
214 if (*endptr || i < 0)
215 return false;
216 if (i > TransportSecurityState::kMaxHSTSAgeSecs)
217 i = TransportSecurityState::kMaxHSTSAgeSecs;
218 *result = i;
219 return true;
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) {
225 if (source.empty())
226 return 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) {
237 StringPair pair;
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);
244 return pair;
247 // TODO(palmer): Support both sha256 and sha1. This will require additional
248 // infrastructure code changes and can come in a later patch.
250 // static
251 bool TransportSecurityState::ParsePin(const std::string& value,
252 SHA1Fingerprint* out) {
253 StringPair slash = Split(Strip(value), '/');
254 if (slash.first != "sha1")
255 return false;
257 std::string decoded;
258 if (!base::Base64Decode(slash.second, &decoded) ||
259 decoded.size() != arraysize(out->data)) {
260 return false;
263 memcpy(out->data, decoded.data(), arraysize(out->data));
264 return true;
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
271 // SHA256.
272 size_t size = value.size();
273 if (size != 30 || value[0] != '"' || value[size - 1] != '"')
274 return false;
276 std::string unquoted = HttpUtil::Unquote(value);
277 std::string decoded;
278 SHA1Fingerprint fp;
280 if (!base::Base64Decode(unquoted, &decoded) ||
281 decoded.size() != arraysize(fp.data)) {
282 return false;
285 memcpy(fp.data, decoded.data(), arraysize(fp.data));
286 fingerprints->push_back(fp);
287 return true;
290 // static
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))
295 return false;
297 base::StringPiece spki;
298 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki))
299 return false;
301 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()),
302 spki.size(), spki_hash->data);
304 return true;
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())
328 return true;
331 return false;
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));
340 if (j != b.end())
341 return true;
344 return false;
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) {
354 if (pins.size() < 2)
355 return false;
357 const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes;
358 if (from_cert_chain.empty())
359 return false;
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 [ ";" ... ]
369 // static
370 bool TransportSecurityState::ParsePinsHeader(const std::string& value,
371 const SSLInfo& ssl_info,
372 DomainState* state) {
373 bool parsed_max_age = false;
374 int max_age = 0;
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)) {
390 return false;
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))
397 return false;
398 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) {
399 // TODO(palmer)
400 } else {
401 // Silently ignore unknown directives for forward compatibility.
404 source = semicolon.second;
407 if (!parsed_max_age || !IsPinListValid(pins, ssl_info))
408 return false;
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();
415 if (max_age > 0) {
416 for (FingerprintVector::const_iterator i = pins.begin();
417 i != pins.end(); i++) {
418 state->dynamic_spki_hashes.push_back(*i);
422 return true;
425 // "Strict-Transport-Security" ":"
426 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ]
428 // static
429 bool TransportSecurityState::ParseHeader(const std::string& value,
430 int* max_age,
431 bool* include_subdomains) {
432 DCHECK(max_age);
433 DCHECK(include_subdomains);
435 int max_age_candidate = 0;
437 enum ParserState {
438 START,
439 AFTER_MAX_AGE_LABEL,
440 AFTER_MAX_AGE_EQUALS,
441 AFTER_MAX_AGE,
442 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER,
443 AFTER_INCLUDE_SUBDOMAINS,
444 } state = START;
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);
450 switch (state) {
451 case START:
452 if (IsAsciiWhitespace(*tokenizer.token_begin()))
453 continue;
454 if (!LowerCaseEqualsASCII(tokenizer.token(), "max-age"))
455 return false;
456 state = AFTER_MAX_AGE_LABEL;
457 break;
459 case AFTER_MAX_AGE_LABEL:
460 if (IsAsciiWhitespace(*tokenizer.token_begin()))
461 continue;
462 if (*tokenizer.token_begin() != '=')
463 return false;
464 DCHECK_EQ(tokenizer.token().length(), 1U);
465 state = AFTER_MAX_AGE_EQUALS;
466 break;
468 case AFTER_MAX_AGE_EQUALS:
469 if (IsAsciiWhitespace(*tokenizer.token_begin()))
470 continue;
471 if (!MaxAgeToInt(tokenizer.token_begin(),
472 tokenizer.token_end(),
473 &max_age_candidate))
474 return false;
475 state = AFTER_MAX_AGE;
476 break;
478 case AFTER_MAX_AGE:
479 if (IsAsciiWhitespace(*tokenizer.token_begin()))
480 continue;
481 if (*tokenizer.token_begin() != ';')
482 return false;
483 state = AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER;
484 break;
486 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER:
487 if (IsAsciiWhitespace(*tokenizer.token_begin()))
488 continue;
489 if (!LowerCaseEqualsASCII(tokenizer.token(), "includesubdomains"))
490 return false;
491 state = AFTER_INCLUDE_SUBDOMAINS;
492 break;
494 case AFTER_INCLUDE_SUBDOMAINS:
495 if (!IsAsciiWhitespace(*tokenizer.token_begin()))
496 return false;
497 break;
499 default:
500 NOTREACHED();
504 // We've consumed all the input. Let's see what state we ended up in.
505 switch (state) {
506 case START:
507 case AFTER_MAX_AGE_LABEL:
508 case AFTER_MAX_AGE_EQUALS:
509 return false;
510 case AFTER_MAX_AGE:
511 *max_age = max_age_candidate;
512 *include_subdomains = false;
513 return true;
514 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER:
515 return false;
516 case AFTER_INCLUDE_SUBDOMAINS:
517 *max_age = max_age_candidate;
518 *include_subdomains = true;
519 return true;
520 default:
521 NOTREACHED();
522 return false;
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:
544 // u16le num_tags
545 // u32le tag[num_tags]
546 // u16le lengths[num_tags]
547 // ...data...
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)
552 // Linux check
553 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian);
554 #elif defined(__BIG_ENDIAN__)
555 // Mac check
556 #error assumes little endian
557 #endif
559 uint16 num_tags_16;
560 if (in->size() < sizeof(num_tags_16))
561 return false;
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)
568 return false;
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);
575 uint32 prev_tag = 0;
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)
581 return false;
582 // tags must be in ascending order.
583 if (i > 0 && prev_tag >= tag)
584 return false;
585 (*out)[tag] = base::StringPiece(in->data(), len);
586 in->remove_prefix(len);
587 prev_tag = tag;
590 return true;
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);
596 if (i == tags.end())
597 return false;
599 *out = i->second;
600 return true;
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,
616 0x42, 0x00,
619 // VerifySignature returns true iff |sig| is a valid signature of
620 // |hash| by |pubkey|. The actual implementation is crypto library
621 // specific.
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)
632 return NULL;
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());
643 return ret;
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())
652 return false;
655 crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ec_key(
656 EVP_PKEY_get1_EC_KEY(secpubkey.get()));
657 if (!ec_key.get())
658 return false;
660 return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash.data()),
661 hash.size(),
662 reinterpret_cast<const unsigned char*>(sig.data()),
663 sig.size(), ec_key.get()) == 1;
666 #else
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)
674 return NULL;
676 std::string pubkey_spki(
677 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix),
678 sizeof(kP256SubjectPublicKeyInfoPrefix));
679 pubkey_spki += pubkey_bytes.as_string();
681 SECItem der;
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);
687 if (!spki)
688 return NULL;
689 SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki);
690 SECKEY_DestroySubjectPublicKeyInfo(spki);
692 return public_key;
695 static bool VerifySignature(const base::StringPiece& pubkey,
696 const base::StringPiece& sig,
697 const base::StringPiece& hash) {
698 SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey);
699 if (!secpubkey)
700 return false;
702 SECItem sigitem;
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);
713 return false;
716 SECItem hashitem;
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;
738 // static
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);
745 TagMap outer;
746 if (!ParseTags(&side_info, &outer))
747 return false;
748 // trailing data is not allowed
749 if (side_info.size())
750 return false;
752 base::StringPiece side_pin_bytes;
753 if (!GetTag(kTagSPIN, outer, &side_pin_bytes))
754 return false;
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) {
761 TagMap side_pin;
762 if (!ParseTags(&side_pin_bytes, &side_pin))
763 return false;
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)) {
769 return false;
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.
775 continue;
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)))) {
787 SHA1Fingerprint fpr;
788 base::SHA1HashBytes(
789 reinterpret_cast<const uint8*>(pubkey.data()),
790 pubkey.size(),
791 fpr.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) {
803 std::string out;
804 CHECK(base::Base64Encode(hashed, &out));
805 return 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) {
811 std::string out;
812 if (!base::Base64Decode(external, &out) ||
813 out.size() != crypto::kSHA256Length) {
814 return std::string();
817 return out;
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),
826 sizeof(i->data));
827 std::string b64;
828 base::Base64Encode(hash_str, &b64);
829 pins->Append(new StringValue("sha1/" + b64));
832 return pins;
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");
852 break;
853 case DomainState::MODE_SPDY_ONLY:
854 state->SetString("mode", "spdy-only");
855 break;
856 case DomainState::MODE_PINNING_ONLY:
857 state->SetString("mode", "pinning-only");
858 break;
859 default:
860 NOTREACHED() << "DomainState with unknown mode";
861 delete state;
862 continue;
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);
877 return true;
880 bool TransportSecurityState::LoadEntries(const std::string& input,
881 bool* dirty) {
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))
893 return false;
895 out->push_back(hash);
896 return true;
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);
909 // static
910 bool TransportSecurityState::Deserialise(
911 const std::string& input,
912 bool* dirty,
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))
917 return false;
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))
927 continue;
929 bool include_subdomains;
930 std::string mode_string;
931 double created;
932 double expiry;
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)) {
938 continue;
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;
961 } else {
962 LOG(WARNING) << "Unknown TransportSecurityState mode string found: "
963 << mode_string;
964 continue;
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);
973 } else {
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.
976 dirtied = true;
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.
983 dirtied = true;
984 continue;
987 std::string hashed = ExternalStringToHashedDomain(*i);
988 if (hashed.empty()) {
989 dirtied = true;
990 continue;
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;
1004 *dirty = dirtied;
1005 return true;
1008 TransportSecurityState::~TransportSecurityState() {
1011 void TransportSecurityState::DirtyNotify() {
1012 DCHECK(CalledOnValidThread());
1014 if (delegate_)
1015 delegate_->StateIsDirty(this);
1018 // static
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]);
1033 if (!label_length)
1034 break;
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]);
1044 // step 3(b)
1045 if (new_host[i + 1] == '-' ||
1046 new_host[i + label_length] == '-') {
1047 return std::string();
1051 return new_host;
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 {
1060 DOMAIN_NOT_PINNED,
1062 DOMAIN_GOOGLE_COM,
1063 DOMAIN_ANDROID_COM,
1064 DOMAIN_GOOGLE_ANALYTICS_COM,
1065 DOMAIN_GOOGLEPLEX_COM,
1066 DOMAIN_YTIMG_COM,
1067 DOMAIN_GOOGLEUSERCONTENT_COM,
1068 DOMAIN_YOUTUBE_COM,
1069 DOMAIN_GOOGLEAPIS_COM,
1070 DOMAIN_GOOGLEADSERVICES_COM,
1071 DOMAIN_GOOGLECODE_COM,
1072 DOMAIN_APPSPOT_COM,
1073 DOMAIN_GOOGLESYNDICATION_COM,
1074 DOMAIN_DOUBLECLICK_NET,
1075 DOMAIN_GSTATIC_COM,
1076 DOMAIN_GMAIL_COM,
1077 DOMAIN_GOOGLEMAIL_COM,
1078 DOMAIN_GOOGLEGROUPS_COM,
1080 DOMAIN_TORPROJECT_ORG,
1082 DOMAIN_TWITTER_COM,
1083 DOMAIN_TWIMG_COM,
1085 DOMAIN_AKAMAIHD_NET,
1087 // Boundary value for UMA_HISTOGRAM_ENUMERATION:
1088 DOMAIN_NUM_EVENTS
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 {
1100 uint8 length;
1101 bool include_subdomains;
1102 char dns_name[30];
1103 bool https_required;
1104 PublicKeyPins pins;
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) {
1116 *ret = false;
1117 } else {
1118 out->include_subdomains = entries[j].include_subdomains;
1119 *ret = true;
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;
1124 while (*hash) {
1125 bool ok = AddHash(*hash, &out->preloaded_spki_hashes);
1126 DCHECK(ok) << " failed to parse " << *hash;
1127 hash++;
1130 if (entries[j].pins.excluded_hashes) {
1131 const char* const* hash = entries[j].pins.excluded_hashes;
1132 while (*hash) {
1133 bool ok = AddHash(*hash, &out->bad_preloaded_spki_hashes);
1134 DCHECK(ok) << " failed to parse " << *hash;
1135 hash++;
1139 return true;
1142 return false;
1145 // kNoRejectedPublicKeys is a placeholder for when no public keys are rejected.
1146 static const char* const kNoRejectedPublicKeys[] = {
1147 NULL,
1150 static const char* const kGoogleAcceptableCerts[] = {
1151 kSPKIHash_VeriSignClass3,
1152 kSPKIHash_VeriSignClass3_G3,
1153 kSPKIHash_Google1024,
1154 kSPKIHash_Google2048,
1155 kSPKIHash_EquifaxSecureCA,
1156 NULL,
1158 static const char* const kGoogleRejectedCerts[] = {
1159 kSPKIHash_Aetna,
1160 kSPKIHash_Intel,
1161 kSPKIHash_TCTrustCenter,
1162 kSPKIHash_Vodafone,
1163 NULL,
1165 #define kGooglePins { \
1166 kGoogleAcceptableCerts, \
1167 kGoogleRejectedCerts, \
1170 static const char* const kTorAcceptableCerts[] = {
1171 kSPKIHash_RapidSSL,
1172 kSPKIHash_DigiCertEVRoot,
1173 kSPKIHash_Tor1,
1174 kSPKIHash_Tor2,
1175 kSPKIHash_Tor3,
1176 NULL,
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,
1202 kSPKIHash_Twitter1,
1203 NULL,
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,
1231 kSPKIHash_Twitter1,
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,
1250 NULL,
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=",
1261 NULL,
1263 #define kTestPins { \
1264 kTestAcceptableCerts, \
1265 kNoRejectedPublicKeys, \
1268 #define kNoPins { \
1269 NULL, NULL, \
1272 #if defined(OS_CHROMEOS)
1273 static const bool kTwitterHSTS = true;
1274 #else
1275 static const bool kTwitterHSTS = false;
1276 #endif
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
1329 // iff using SSL.
1330 {11, true, "\005ytimg\003com", false, kGooglePins,
1331 DOMAIN_YTIMG_COM },
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
1351 // CA.
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,
1440 DOMAIN_TWIMG_COM },
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,
1449 DOMAIN_GMAIL_COM },
1450 {16, false, "\012googlemail\003com", true, kGooglePins,
1451 DOMAIN_GOOGLEMAIL_COM },
1452 {15, false, "\003www\005gmail\003com", true, kGooglePins,
1453 DOMAIN_GMAIL_COM },
1454 {20, false, "\003www\012googlemail\003com", true, kGooglePins,
1455 DOMAIN_GOOGLEMAIL_COM },
1456 // These SNI-only domains must use an acceptable certificate iff using
1457 // HTTPS.
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)
1481 continue;
1483 if (entry->length == canonicalized_host.size() - i &&
1484 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) {
1485 return entry;
1490 return NULL;
1493 // static
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)
1501 return true;
1503 if (sni_available) {
1504 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS,
1505 kNumPreloadedSNISTS);
1506 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts)
1507 return true;
1510 return false;
1513 // static
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);
1520 if (!entry) {
1521 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS,
1522 kNumPreloadedSNISTS);
1525 DCHECK(entry);
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,
1537 bool sni_available,
1538 DomainState* out) {
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;
1554 return true;
1556 bool ret;
1557 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out,
1558 &ret)) {
1559 return ret;
1561 if (sni_available &&
1562 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i,
1563 out, &ret)) {
1564 return ret;
1568 return false;
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++) {
1576 std::string s;
1577 const std::string hash_str(reinterpret_cast<const char*>(i->data),
1578 sizeof(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),
1590 preloaded(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);
1604 return false;
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);
1615 return false;
1618 return true;
1621 bool TransportSecurityState::DomainState::IsMoreStrict(
1622 const TransportSecurityState::DomainState& other) {
1623 if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty())
1624 return false;
1626 if (!this->include_subdomains && other.include_subdomains)
1627 return false;
1629 return true;
1632 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS()
1633 const {
1634 return mode == MODE_STRICT;
1637 } // namespace