| Index: net/base/transport_security_state.cc
 | 
| ===================================================================
 | 
| --- net/base/transport_security_state.cc	(revision 165283)
 | 
| +++ net/base/transport_security_state.cc	(working copy)
 | 
| @@ -23,7 +23,6 @@
 | 
|  #include "base/metrics/histogram.h"
 | 
|  #include "base/sha1.h"
 | 
|  #include "base/string_number_conversions.h"
 | 
| -#include "base/string_tokenizer.h"
 | 
|  #include "base/string_util.h"
 | 
|  #include "base/time.h"
 | 
|  #include "base/utf_string_conversions.h"
 | 
| @@ -34,7 +33,7 @@
 | 
|  #include "net/base/ssl_info.h"
 | 
|  #include "net/base/x509_cert_types.h"
 | 
|  #include "net/base/x509_certificate.h"
 | 
| -#include "net/http/http_util.h"
 | 
| +#include "net/http/http_security_headers.h"
 | 
|  
 | 
|  #if defined(USE_OPENSSL)
 | 
|  #include "crypto/openssl_util.h"
 | 
| @@ -42,14 +41,47 @@
 | 
|  
 | 
|  namespace net {
 | 
|  
 | 
| -const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365;  // 1 year
 | 
| +namespace {
 | 
|  
 | 
| -static std::string HashHost(const std::string& canonicalized_host) {
 | 
| +std::string HashesToBase64String(const HashValueVector& hashes) {
 | 
| +  std::string str;
 | 
| +  for (size_t i = 0; i != hashes.size(); ++i) {
 | 
| +    if (i != 0)
 | 
| +      str += ",";
 | 
| +    str += hashes[i].ToString();
 | 
| +  }
 | 
| +  return str;
 | 
| +}
 | 
| +
 | 
| +std::string HashHost(const std::string& canonicalized_host) {
 | 
|    char hashed[crypto::kSHA256Length];
 | 
|    crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
 | 
|    return std::string(hashed, sizeof(hashed));
 | 
|  }
 | 
|  
 | 
| +// Returns true if the intersection of |a| and |b| is not empty. If either
 | 
| +// |a| or |b| is empty, returns false.
 | 
| +bool HashesIntersect(const HashValueVector& a,
 | 
| +                     const HashValueVector& b) {
 | 
| +  for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) {
 | 
| +    HashValueVector::const_iterator j =
 | 
| +        std::find_if(b.begin(), b.end(), HashValuesEqual(*i));
 | 
| +    if (j != b.end())
 | 
| +      return true;
 | 
| +  }
 | 
| +  return false;
 | 
| +}
 | 
| +
 | 
| +bool AddHash(const char* sha1_hash,
 | 
| +             HashValueVector* out) {
 | 
| +  HashValue hash(HASH_VALUE_SHA1);
 | 
| +  memcpy(hash.data(), sha1_hash, hash.size());
 | 
| +  out->push_back(hash);
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +} // namespace
 | 
| +
 | 
|  TransportSecurityState::TransportSecurityState()
 | 
|    : delegate_(NULL) {
 | 
|  }
 | 
| @@ -182,370 +214,6 @@
 | 
|      DirtyNotify();
 | 
|  }
 | 
|  
 | 
| -// MaxAgeToInt converts a string representation of a number of seconds into a
 | 
| -// int. We use strtol in order to handle overflow correctly. The string may
 | 
| -// contain an arbitary number which we should truncate correctly rather than
 | 
| -// throwing a parse failure.
 | 
| -static bool MaxAgeToInt(std::string::const_iterator begin,
 | 
| -                        std::string::const_iterator end,
 | 
| -                        int* result) {
 | 
| -  const std::string s(begin, end);
 | 
| -  char* endptr;
 | 
| -  long int i = strtol(s.data(), &endptr, 10 /* base */);
 | 
| -  if (*endptr || i < 0)
 | 
| -    return false;
 | 
| -  if (i > TransportSecurityState::kMaxHSTSAgeSecs)
 | 
| -    i = TransportSecurityState::kMaxHSTSAgeSecs;
 | 
| -  *result = i;
 | 
| -  return true;
 | 
| -}
 | 
| -
 | 
| -// Strip, Split, StringPair, and ParsePins are private implementation details
 | 
| -// of ParsePinsHeader(std::string&, DomainState&).
 | 
| -static std::string Strip(const std::string& source) {
 | 
| -  if (source.empty())
 | 
| -    return source;
 | 
| -
 | 
| -  std::string::const_iterator start = source.begin();
 | 
| -  std::string::const_iterator end = source.end();
 | 
| -  HttpUtil::TrimLWS(&start, &end);
 | 
| -  return std::string(start, end);
 | 
| -}
 | 
| -
 | 
| -typedef std::pair<std::string, std::string> StringPair;
 | 
| -
 | 
| -static StringPair Split(const std::string& source, char delimiter) {
 | 
| -  StringPair pair;
 | 
| -  size_t point = source.find(delimiter);
 | 
| -
 | 
| -  pair.first = source.substr(0, point);
 | 
| -  if (std::string::npos != point)
 | 
| -    pair.second = source.substr(point + 1);
 | 
| -
 | 
| -  return pair;
 | 
| -}
 | 
| -
 | 
| -// static
 | 
| -bool TransportSecurityState::ParsePin(const std::string& value,
 | 
| -                                      HashValue* out) {
 | 
| -  StringPair slash = Split(Strip(value), '/');
 | 
| -
 | 
| -  if (slash.first == "sha1")
 | 
| -    out->tag = HASH_VALUE_SHA1;
 | 
| -  else if (slash.first == "sha256")
 | 
| -    out->tag = HASH_VALUE_SHA256;
 | 
| -  else
 | 
| -    return false;
 | 
| -
 | 
| -  std::string decoded;
 | 
| -  if (!base::Base64Decode(slash.second, &decoded) ||
 | 
| -      decoded.size() != out->size()) {
 | 
| -    return false;
 | 
| -  }
 | 
| -
 | 
| -  memcpy(out->data(), decoded.data(), out->size());
 | 
| -  return true;
 | 
| -}
 | 
| -
 | 
| -static bool ParseAndAppendPin(const std::string& value,
 | 
| -                              HashValueTag tag,
 | 
| -                              HashValueVector* hashes) {
 | 
| -  std::string unquoted = HttpUtil::Unquote(value);
 | 
| -  std::string decoded;
 | 
| -
 | 
| -  // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1.
 | 
| -  // Currently, those are the only two possibilities, so the assumption is
 | 
| -  // valid.
 | 
| -  if (!base::Base64Decode(unquoted, &decoded))
 | 
| -    return false;
 | 
| -
 | 
| -  HashValue hash(tag);
 | 
| -  if (decoded.size() != hash.size())
 | 
| -    return false;
 | 
| -
 | 
| -  memcpy(hash.data(), decoded.data(), hash.size());
 | 
| -  hashes->push_back(hash);
 | 
| -  return true;
 | 
| -}
 | 
| -
 | 
| -struct HashValuesEqualPredicate {
 | 
| -  explicit HashValuesEqualPredicate(const HashValue& fingerprint) :
 | 
| -      fingerprint_(fingerprint) {}
 | 
| -
 | 
| -  bool operator()(const HashValue& other) const {
 | 
| -    return fingerprint_.Equals(other);
 | 
| -  }
 | 
| -
 | 
| -  const HashValue& fingerprint_;
 | 
| -};
 | 
| -
 | 
| -// Returns true iff there is an item in |pins| which is not present in
 | 
| -// |from_cert_chain|. Such an SPKI hash is called a "backup pin".
 | 
| -static bool IsBackupPinPresent(const HashValueVector& pins,
 | 
| -                               const HashValueVector& from_cert_chain) {
 | 
| -  for (HashValueVector::const_iterator
 | 
| -       i = pins.begin(); i != pins.end(); ++i) {
 | 
| -    HashValueVector::const_iterator j =
 | 
| -        std::find_if(from_cert_chain.begin(), from_cert_chain.end(),
 | 
| -                     HashValuesEqualPredicate(*i));
 | 
| -      if (j == from_cert_chain.end())
 | 
| -        return true;
 | 
| -  }
 | 
| -
 | 
| -  return false;
 | 
| -}
 | 
| -
 | 
| -// Returns true if the intersection of |a| and |b| is not empty. If either
 | 
| -// |a| or |b| is empty, returns false.
 | 
| -static bool HashesIntersect(const HashValueVector& a,
 | 
| -                            const HashValueVector& b) {
 | 
| -  for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) {
 | 
| -    HashValueVector::const_iterator j =
 | 
| -        std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i));
 | 
| -    if (j != b.end())
 | 
| -      return true;
 | 
| -  }
 | 
| -
 | 
| -  return false;
 | 
| -}
 | 
| -
 | 
| -// Returns true iff |pins| contains both a live and a backup pin. A live pin
 | 
| -// is a pin whose SPKI is present in the certificate chain in |ssl_info|. A
 | 
| -// backup pin is a pin intended for disaster recovery, not day-to-day use, and
 | 
| -// thus must be absent from the certificate chain. The Public-Key-Pins header
 | 
| -// specification requires both.
 | 
| -static bool IsPinListValid(const HashValueVector& pins,
 | 
| -                           const SSLInfo& ssl_info) {
 | 
| -  // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual
 | 
| -  // liveness and backupness below.)
 | 
| -  if (pins.size() < 2)
 | 
| -    return false;
 | 
| -
 | 
| -  const HashValueVector& from_cert_chain = ssl_info.public_key_hashes;
 | 
| -  if (from_cert_chain.empty())
 | 
| -    return false;
 | 
| -
 | 
| -  return IsBackupPinPresent(pins, from_cert_chain) &&
 | 
| -         HashesIntersect(pins, from_cert_chain);
 | 
| -}
 | 
| -
 | 
| -// "Public-Key-Pins" ":"
 | 
| -//     "max-age" "=" delta-seconds ";"
 | 
| -//     "pin-" algo "=" base64 [ ";" ... ]
 | 
| -bool TransportSecurityState::DomainState::ParsePinsHeader(
 | 
| -    const base::Time& now,
 | 
| -    const std::string& value,
 | 
| -    const SSLInfo& ssl_info) {
 | 
| -  bool parsed_max_age = false;
 | 
| -  int max_age_candidate = 0;
 | 
| -  HashValueVector pins;
 | 
| -
 | 
| -  std::string source = value;
 | 
| -
 | 
| -  while (!source.empty()) {
 | 
| -    StringPair semicolon = Split(source, ';');
 | 
| -    semicolon.first = Strip(semicolon.first);
 | 
| -    semicolon.second = Strip(semicolon.second);
 | 
| -    StringPair equals = Split(semicolon.first, '=');
 | 
| -    equals.first = Strip(equals.first);
 | 
| -    equals.second = Strip(equals.second);
 | 
| -
 | 
| -    if (LowerCaseEqualsASCII(equals.first, "max-age")) {
 | 
| -      if (equals.second.empty() ||
 | 
| -          !MaxAgeToInt(equals.second.begin(), equals.second.end(),
 | 
| -                       &max_age_candidate)) {
 | 
| -        return false;
 | 
| -      }
 | 
| -      if (max_age_candidate > kMaxHSTSAgeSecs)
 | 
| -        max_age_candidate = kMaxHSTSAgeSecs;
 | 
| -      parsed_max_age = true;
 | 
| -    } else if (StartsWithASCII(equals.first, "pin-", false)) {
 | 
| -      HashValueTag tag;
 | 
| -      if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) {
 | 
| -        tag = HASH_VALUE_SHA1;
 | 
| -      } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) {
 | 
| -        tag = HASH_VALUE_SHA256;
 | 
| -      } else {
 | 
| -        LOG(WARNING) << "Ignoring pin of unknown type: " << equals.first;
 | 
| -        return false;
 | 
| -      }
 | 
| -      if (!ParseAndAppendPin(equals.second, tag, &pins))
 | 
| -        return false;
 | 
| -    } else {
 | 
| -      // Silently ignore unknown directives for forward compatibility.
 | 
| -    }
 | 
| -
 | 
| -    source = semicolon.second;
 | 
| -  }
 | 
| -
 | 
| -  if (!parsed_max_age || !IsPinListValid(pins, ssl_info))
 | 
| -    return false;
 | 
| -
 | 
| -  dynamic_spki_hashes_expiry =
 | 
| -      now + base::TimeDelta::FromSeconds(max_age_candidate);
 | 
| -
 | 
| -  dynamic_spki_hashes.clear();
 | 
| -  if (max_age_candidate > 0) {
 | 
| -    for (HashValueVector::const_iterator i = pins.begin();
 | 
| -         i != pins.end(); ++i) {
 | 
| -      dynamic_spki_hashes.push_back(*i);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  return true;
 | 
| -}
 | 
| -
 | 
| -// Parse the Strict-Transport-Security header, as currently defined in
 | 
| -// http://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14:
 | 
| -//
 | 
| -// Strict-Transport-Security = "Strict-Transport-Security" ":"
 | 
| -//                             [ directive ]  *( ";" [ directive ] )
 | 
| -//
 | 
| -// directive                 = directive-name [ "=" directive-value ]
 | 
| -// directive-name            = token
 | 
| -// directive-value           = token | quoted-string
 | 
| -//
 | 
| -// 1.  The order of appearance of directives is not significant.
 | 
| -//
 | 
| -// 2.  All directives MUST appear only once in an STS header field.
 | 
| -//     Directives are either optional or required, as stipulated in
 | 
| -//     their definitions.
 | 
| -//
 | 
| -// 3.  Directive names are case-insensitive.
 | 
| -//
 | 
| -// 4.  UAs MUST ignore any STS header fields containing directives, or
 | 
| -//     other header field value data, that does not conform to the
 | 
| -//     syntax defined in this specification.
 | 
| -//
 | 
| -// 5.  If an STS header field contains directive(s) not recognized by
 | 
| -//     the UA, the UA MUST ignore the unrecognized directives and if the
 | 
| -//     STS header field otherwise satisfies the above requirements (1
 | 
| -//     through 4), the UA MUST process the recognized directives.
 | 
| -bool TransportSecurityState::DomainState::ParseSTSHeader(
 | 
| -    const base::Time& now,
 | 
| -    const std::string& value) {
 | 
| -  int max_age_candidate = 0;
 | 
| -  bool include_subdomains_candidate = false;
 | 
| -
 | 
| -  // We must see max-age exactly once.
 | 
| -  int max_age_observed = 0;
 | 
| -  // We must see includeSubdomains exactly 0 or 1 times.
 | 
| -  int include_subdomains_observed = 0;
 | 
| -
 | 
| -  enum ParserState {
 | 
| -    START,
 | 
| -    AFTER_MAX_AGE_LABEL,
 | 
| -    AFTER_MAX_AGE_EQUALS,
 | 
| -    AFTER_MAX_AGE,
 | 
| -    AFTER_INCLUDE_SUBDOMAINS,
 | 
| -    AFTER_UNKNOWN_LABEL,
 | 
| -    DIRECTIVE_END
 | 
| -  } state = START;
 | 
| -
 | 
| -  StringTokenizer tokenizer(value, " \t=;");
 | 
| -  tokenizer.set_options(StringTokenizer::RETURN_DELIMS);
 | 
| -  tokenizer.set_quote_chars("\"");
 | 
| -  std::string unquoted;
 | 
| -  while (tokenizer.GetNext()) {
 | 
| -    DCHECK(!tokenizer.token_is_delim() || tokenizer.token().length() == 1);
 | 
| -    switch (state) {
 | 
| -      case START:
 | 
| -      case DIRECTIVE_END:
 | 
| -        if (IsAsciiWhitespace(*tokenizer.token_begin()))
 | 
| -          continue;
 | 
| -        if (LowerCaseEqualsASCII(tokenizer.token(), "max-age")) {
 | 
| -          state = AFTER_MAX_AGE_LABEL;
 | 
| -          max_age_observed++;
 | 
| -        } else if (LowerCaseEqualsASCII(tokenizer.token(),
 | 
| -                                        "includesubdomains")) {
 | 
| -          state = AFTER_INCLUDE_SUBDOMAINS;
 | 
| -          include_subdomains_observed++;
 | 
| -          include_subdomains_candidate = true;
 | 
| -        } else {
 | 
| -          state = AFTER_UNKNOWN_LABEL;
 | 
| -        }
 | 
| -        break;
 | 
| -
 | 
| -      case AFTER_MAX_AGE_LABEL:
 | 
| -        if (IsAsciiWhitespace(*tokenizer.token_begin()))
 | 
| -          continue;
 | 
| -        if (*tokenizer.token_begin() != '=')
 | 
| -          return false;
 | 
| -        DCHECK_EQ(tokenizer.token().length(), 1U);
 | 
| -        state = AFTER_MAX_AGE_EQUALS;
 | 
| -        break;
 | 
| -
 | 
| -      case AFTER_MAX_AGE_EQUALS:
 | 
| -        if (IsAsciiWhitespace(*tokenizer.token_begin()))
 | 
| -          continue;
 | 
| -        unquoted = HttpUtil::Unquote(tokenizer.token());
 | 
| -        if (!MaxAgeToInt(unquoted.begin(),
 | 
| -                         unquoted.end(),
 | 
| -                         &max_age_candidate))
 | 
| -          return false;
 | 
| -        state = AFTER_MAX_AGE;
 | 
| -        break;
 | 
| -
 | 
| -      case AFTER_MAX_AGE:
 | 
| -      case AFTER_INCLUDE_SUBDOMAINS:
 | 
| -        if (IsAsciiWhitespace(*tokenizer.token_begin()))
 | 
| -          continue;
 | 
| -        else if (*tokenizer.token_begin() == ';')
 | 
| -          state = DIRECTIVE_END;
 | 
| -        else
 | 
| -          return false;
 | 
| -        break;
 | 
| -
 | 
| -      case AFTER_UNKNOWN_LABEL:
 | 
| -        // Consume and ignore the post-label contents (if any).
 | 
| -        if (*tokenizer.token_begin() != ';')
 | 
| -          continue;
 | 
| -        state = DIRECTIVE_END;
 | 
| -        break;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  // We've consumed all the input.  Let's see what state we ended up in.
 | 
| -  if (max_age_observed != 1 ||
 | 
| -      (include_subdomains_observed != 0 && include_subdomains_observed != 1)) {
 | 
| -    return false;
 | 
| -  }
 | 
| -
 | 
| -  switch (state) {
 | 
| -    case AFTER_MAX_AGE:
 | 
| -    case AFTER_INCLUDE_SUBDOMAINS:
 | 
| -    case AFTER_UNKNOWN_LABEL:
 | 
| -      if (max_age_candidate > 0) {
 | 
| -        upgrade_expiry = now + base::TimeDelta::FromSeconds(max_age_candidate);
 | 
| -        upgrade_mode = MODE_FORCE_HTTPS;
 | 
| -      } else {
 | 
| -        upgrade_expiry = now;
 | 
| -        upgrade_mode = MODE_DEFAULT;
 | 
| -      }
 | 
| -      include_subdomains = include_subdomains_candidate;
 | 
| -      return true;
 | 
| -    case START:
 | 
| -    case DIRECTIVE_END:
 | 
| -    case AFTER_MAX_AGE_LABEL:
 | 
| -    case AFTER_MAX_AGE_EQUALS:
 | 
| -      return false;
 | 
| -    default:
 | 
| -      NOTREACHED();
 | 
| -      return false;
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -static bool AddHash(const std::string& type_and_base64,
 | 
| -                    HashValueVector* out) {
 | 
| -  HashValue hash;
 | 
| -
 | 
| -  if (!TransportSecurityState::ParsePin(type_and_base64, &hash))
 | 
| -    return false;
 | 
| -
 | 
| -  out->push_back(hash);
 | 
| -  return true;
 | 
| -}
 | 
| -
 | 
|  TransportSecurityState::~TransportSecurityState() {}
 | 
|  
 | 
|  void TransportSecurityState::DirtyNotify() {
 | 
| @@ -885,19 +553,17 @@
 | 
|          if (!entries[j].https_required)
 | 
|            out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT;
 | 
|          if (entries[j].pins.required_hashes) {
 | 
| -          const char* const* hash = entries[j].pins.required_hashes;
 | 
| -          while (*hash) {
 | 
| -            bool ok = AddHash(*hash, &out->static_spki_hashes);
 | 
| -            DCHECK(ok) << " failed to parse " << *hash;
 | 
| -            hash++;
 | 
| +          const char* const* sha1_hash = entries[j].pins.required_hashes;
 | 
| +          while (*sha1_hash) {
 | 
| +            AddHash(*sha1_hash, &out->static_spki_hashes);
 | 
| +            sha1_hash++;
 | 
|            }
 | 
|          }
 | 
|          if (entries[j].pins.excluded_hashes) {
 | 
| -          const char* const* hash = entries[j].pins.excluded_hashes;
 | 
| -          while (*hash) {
 | 
| -            bool ok = AddHash(*hash, &out->bad_static_spki_hashes);
 | 
| -            DCHECK(ok) << " failed to parse " << *hash;
 | 
| -            hash++;
 | 
| +          const char* const* sha1_hash = entries[j].pins.excluded_hashes;
 | 
| +          while (*sha1_hash) {
 | 
| +            AddHash(*sha1_hash, &out->bad_static_spki_hashes);
 | 
| +            sha1_hash++;
 | 
|            }
 | 
|          }
 | 
|        }
 | 
| @@ -936,6 +602,40 @@
 | 
|    return NULL;
 | 
|  }
 | 
|  
 | 
| +bool TransportSecurityState::AddHSTSHeader(const std::string& host,
 | 
| +                                           const std::string& value) {
 | 
| +  base::Time now = base::Time::Now();
 | 
| +  TransportSecurityState::DomainState domain_state;
 | 
| +  if (ParseHSTSHeader(now, value, &domain_state.upgrade_expiry,
 | 
| +                      &domain_state.include_subdomains)) {
 | 
| +    // Handle max-age == 0
 | 
| +    if (now == domain_state.upgrade_expiry)
 | 
| +      domain_state.upgrade_mode = DomainState::MODE_DEFAULT;
 | 
| +    else
 | 
| +      domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS;
 | 
| +    domain_state.created = now;
 | 
| +    EnableHost(host, domain_state);
 | 
| +    return true;
 | 
| +  }
 | 
| +  return false;
 | 
| +}
 | 
| +
 | 
| +bool TransportSecurityState::AddHPKPHeader(const std::string& host,
 | 
| +                                           const std::string& value,
 | 
| +                                           const SSLInfo& ssl_info) {
 | 
| +  base::Time now = base::Time::Now();
 | 
| +  TransportSecurityState::DomainState domain_state;
 | 
| +  if (ParseHPKPHeader(now, value, ssl_info.public_key_hashes,
 | 
| +                      &domain_state.dynamic_spki_hashes_expiry,
 | 
| +                      &domain_state.dynamic_spki_hashes)) {
 | 
| +    domain_state.upgrade_mode = DomainState::MODE_DEFAULT;
 | 
| +    domain_state.created = now;
 | 
| +    EnableHost(host, domain_state);
 | 
| +    return true;
 | 
| +  }
 | 
| +  return false;
 | 
| +}
 | 
| +
 | 
|  // static
 | 
|  bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host,
 | 
|                                                      bool sni_enabled) {
 | 
| @@ -981,21 +681,6 @@
 | 
|                              entry->second_level_domain_name, DOMAIN_NUM_EVENTS);
 | 
|  }
 | 
|  
 | 
| -// static
 | 
| -const char* TransportSecurityState::HashValueLabel(
 | 
| -    const HashValue& hash_value) {
 | 
| -  switch (hash_value.tag) {
 | 
| -    case HASH_VALUE_SHA1:
 | 
| -      return "sha1/";
 | 
| -    case HASH_VALUE_SHA256:
 | 
| -      return "sha256/";
 | 
| -    default:
 | 
| -      NOTREACHED();
 | 
| -      LOG(WARNING) << "Invalid fingerprint of unknown type " << hash_value.tag;
 | 
| -      return "unknown/";
 | 
| -  }
 | 
| -}
 | 
| -
 | 
|  bool TransportSecurityState::GetStaticDomainState(
 | 
|      const std::string& canonicalized_host,
 | 
|      bool sni_enabled,
 | 
| @@ -1040,21 +725,6 @@
 | 
|    forced_hosts_[hashed_host] = state;
 | 
|  }
 | 
|  
 | 
| -static std::string HashesToBase64String(
 | 
| -    const HashValueVector& hashes) {
 | 
| -  std::vector<std::string> hashes_strs;
 | 
| -  for (HashValueVector::const_iterator
 | 
| -       i = hashes.begin(); i != hashes.end(); i++) {
 | 
| -    std::string s;
 | 
| -    const std::string hash_str(reinterpret_cast<const char*>(i->data()),
 | 
| -                               i->size());
 | 
| -    base::Base64Encode(hash_str, &s);
 | 
| -    hashes_strs.push_back(s);
 | 
| -  }
 | 
| -
 | 
| -  return JoinString(hashes_strs, ',');
 | 
| -}
 | 
| -
 | 
|  TransportSecurityState::DomainState::DomainState()
 | 
|      : upgrade_mode(MODE_FORCE_HTTPS),
 | 
|        created(base::Time::Now()),
 | 
| 
 |