Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / net / quic / crypto / quic_crypto_client_config.cc
index 3dafdb8..4828c3a 100644 (file)
@@ -4,8 +4,12 @@
 
 #include "net/quic/crypto/quic_crypto_client_config.h"
 
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
 #include "base/stl_util.h"
+#include "base/strings/string_util.h"
 #include "net/quic/crypto/cert_compressor.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
 #include "net/quic/crypto/channel_id.h"
 #include "net/quic/crypto/common_cert_set.h"
 #include "net/quic/crypto/crypto_framer.h"
 #include "net/quic/crypto/quic_encrypter.h"
 #include "net/quic/quic_utils.h"
 
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
 using base::StringPiece;
+using std::find;
+using std::make_pair;
 using std::map;
 using std::string;
 using std::vector;
 
 namespace net {
 
-QuicCryptoClientConfig::QuicCryptoClientConfig() {}
+namespace {
+
+// Tracks the reason (the state of the server config) for sending inchoate
+// ClientHello to the server.
+void RecordInchoateClientHelloReason(
+    QuicCryptoClientConfig::CachedState::ServerConfigState state) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Net.QuicInchoateClientHelloReason", state,
+      QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT);
+}
+
+// Tracks the state of the QUIC server information loaded from the disk cache.
+void RecordDiskCacheServerConfigState(
+    QuicCryptoClientConfig::CachedState::ServerConfigState state) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Net.QuicServerInfo.DiskCacheState", state,
+      QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT);
+}
+
+}  // namespace
+
+QuicCryptoClientConfig::QuicCryptoClientConfig()
+    : disable_ecdsa_(false) {
+  SetDefaults();
+}
 
 QuicCryptoClientConfig::~QuicCryptoClientConfig() {
   STLDeleteValues(&cached_states_);
@@ -41,7 +67,13 @@ QuicCryptoClientConfig::CachedState::CachedState()
 QuicCryptoClientConfig::CachedState::~CachedState() {}
 
 bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
-  if (server_config_.empty() || !server_config_valid_) {
+  if (server_config_.empty()) {
+    RecordInchoateClientHelloReason(SERVER_CONFIG_EMPTY);
+    return false;
+  }
+
+  if (!server_config_valid_) {
+    RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID);
     return false;
   }
 
@@ -49,22 +81,35 @@ bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
   if (!scfg) {
     // Should be impossible short of cache corruption.
     DCHECK(false);
+    RecordInchoateClientHelloReason(SERVER_CONFIG_CORRUPTED);
     return false;
   }
 
   uint64 expiry_seconds;
-  if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR ||
-      now.ToUNIXSeconds() >= expiry_seconds) {
+  if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+    RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID_EXPIRY);
+    return false;
+  }
+  if (now.ToUNIXSeconds() >= expiry_seconds) {
+    UMA_HISTOGRAM_CUSTOM_TIMES(
+        "Net.QuicClientHelloServerConfig.InvalidDuration",
+        base::TimeDelta::FromSeconds(now.ToUNIXSeconds() - expiry_seconds),
+        base::TimeDelta::FromMinutes(1), base::TimeDelta::FromDays(20), 50);
+    RecordInchoateClientHelloReason(SERVER_CONFIG_EXPIRED);
     return false;
   }
 
   return true;
 }
 
+bool QuicCryptoClientConfig::CachedState::IsEmpty() const {
+  return server_config_.empty();
+}
+
 const CryptoHandshakeMessage*
 QuicCryptoClientConfig::CachedState::GetServerConfig() const {
   if (server_config_.empty()) {
-    return NULL;
+    return nullptr;
   }
 
   if (!scfg_.get()) {
@@ -74,7 +119,8 @@ QuicCryptoClientConfig::CachedState::GetServerConfig() const {
   return scfg_.get();
 }
 
-QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig(
+QuicCryptoClientConfig::CachedState::ServerConfigState
+QuicCryptoClientConfig::CachedState::SetServerConfig(
     StringPiece server_config, QuicWallTime now, string* error_details) {
   const bool matches_existing = server_config == server_config_;
 
@@ -92,18 +138,18 @@ QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig(
 
   if (!new_scfg) {
     *error_details = "SCFG invalid";
-    return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+    return SERVER_CONFIG_INVALID;
   }
 
   uint64 expiry_seconds;
   if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
     *error_details = "SCFG missing EXPY";
-    return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+    return SERVER_CONFIG_INVALID_EXPIRY;
   }
 
   if (now.ToUNIXSeconds() >= expiry_seconds) {
     *error_details = "SCFG has expired";
-    return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED;
+    return SERVER_CONFIG_EXPIRED;
   }
 
   if (!matches_existing) {
@@ -111,7 +157,7 @@ QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig(
     SetProofInvalid();
     scfg_.reset(new_scfg_storage.release());
   }
-  return QUIC_NO_ERROR;
+  return SERVER_CONFIG_VALID;
 }
 
 void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() {
@@ -144,6 +190,17 @@ void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs,
   server_config_sig_ = signature.as_string();
 }
 
+void QuicCryptoClientConfig::CachedState::Clear() {
+  server_config_.clear();
+  source_address_token_.clear();
+  certs_.clear();
+  server_config_sig_.clear();
+  server_config_valid_ = false;
+  proof_verify_details_.reset();
+  scfg_.reset();
+  ++generation_counter_;
+}
+
 void QuicCryptoClientConfig::CachedState::ClearProof() {
   SetProofInvalid();
   certs_.clear();
@@ -159,6 +216,34 @@ void QuicCryptoClientConfig::CachedState::SetProofInvalid() {
   ++generation_counter_;
 }
 
+bool QuicCryptoClientConfig::CachedState::Initialize(
+    StringPiece server_config,
+    StringPiece source_address_token,
+    const vector<string>& certs,
+    StringPiece signature,
+    QuicWallTime now) {
+  DCHECK(server_config_.empty());
+
+  if (server_config.empty()) {
+    RecordDiskCacheServerConfigState(SERVER_CONFIG_EMPTY);
+    return false;
+  }
+
+  string error_details;
+  ServerConfigState state = SetServerConfig(server_config, now,
+                                            &error_details);
+  RecordDiskCacheServerConfigState(state);
+  if (state != SERVER_CONFIG_VALID) {
+    DVLOG(1) << "SetServerConfig failed with " << error_details;
+    return false;
+  }
+
+  signature.CopyToString(&server_config_sig_);
+  source_address_token.CopyToString(&source_address_token_);
+  certs_ = certs;
+  return true;
+}
+
 const string& QuicCryptoClientConfig::CachedState::server_config() const {
   return server_config_;
 }
@@ -208,38 +293,54 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom(
   certs_ = other.certs_;
   server_config_sig_ = other.server_config_sig_;
   server_config_valid_ = other.server_config_valid_;
+  if (other.proof_verify_details_.get() != nullptr) {
+    proof_verify_details_.reset(other.proof_verify_details_->Clone());
+  }
+  ++generation_counter_;
 }
 
 void QuicCryptoClientConfig::SetDefaults() {
-  // Version must be 0.
-  // TODO(agl): this version stuff is obsolete now.
-  version = QuicCryptoConfig::CONFIG_VERSION;
-
   // Key exchange methods.
   kexs.resize(2);
   kexs[0] = kC255;
   kexs[1] = kP256;
 
-  // Authenticated encryption algorithms.
-  aead.resize(1);
-  aead[0] = kAESG;
+  // Authenticated encryption algorithms. Prefer ChaCha20 by default.
+  aead.clear();
+  if (ChaCha20Poly1305Encrypter::IsSupported()) {
+    aead.push_back(kCC12);
+  }
+  aead.push_back(kAESG);
+
+  disable_ecdsa_ = false;
 }
 
 QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate(
-    const string& server_hostname) {
-  map<string, CachedState*>::const_iterator it =
-      cached_states_.find(server_hostname);
+    const QuicServerId& server_id) {
+  CachedStateMap::const_iterator it = cached_states_.find(server_id);
   if (it != cached_states_.end()) {
     return it->second;
   }
 
   CachedState* cached = new CachedState;
-  cached_states_.insert(make_pair(server_hostname, cached));
+  cached_states_.insert(make_pair(server_id, cached));
+  bool cache_populated = PopulateFromCanonicalConfig(server_id, cached);
+  UMA_HISTOGRAM_BOOLEAN(
+      "Net.QuicCryptoClientConfig.PopulatedFromCanonicalConfig",
+      cache_populated);
   return cached;
 }
 
+void QuicCryptoClientConfig::ClearCachedStates() {
+  for (CachedStateMap::const_iterator it = cached_states_.begin();
+       it != cached_states_.end(); ++it) {
+    it->second->Clear();
+  }
+}
+
 void QuicCryptoClientConfig::FillInchoateClientHello(
-    const string& server_hostname,
+    const QuicServerId& server_id,
+    const QuicVersion preferred_version,
     const CachedState* cached,
     QuicCryptoNegotiatedParameters* out_params,
     CryptoHandshakeMessage* out) const {
@@ -248,35 +349,25 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
 
   // Server name indication. We only send SNI if it's a valid domain name, as
   // per the spec.
-  if (CryptoUtils::IsValidSNI(server_hostname)) {
-    out->SetStringPiece(kSNI, server_hostname);
+  if (CryptoUtils::IsValidSNI(server_id.host())) {
+    out->SetStringPiece(kSNI, server_id.host());
+  }
+  out->SetValue(kVER, QuicVersionToQuicTag(preferred_version));
+
+  if (!user_agent_id_.empty()) {
+    out->SetStringPiece(kUAID, user_agent_id_);
   }
-  out->SetValue(kVERS, version);
 
   if (!cached->source_address_token().empty()) {
     out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token());
   }
 
-  if (proof_verifier_.get()) {
-    // Don't request ECDSA proofs on platforms that do not support ECDSA
-    // certificates.
-    bool disableECDSA = false;
-#if defined(OS_WIN)
-    if (base::win::GetVersion() < base::win::VERSION_VISTA)
-      disableECDSA = true;
-#endif
-    if (disableECDSA) {
+  if (server_id.is_https()) {
+    if (disable_ecdsa_) {
       out->SetTaglist(kPDMD, kX59R, 0);
     } else {
       out->SetTaglist(kPDMD, kX509, 0);
     }
-
-    if (!cached->proof_valid()) {
-      // If we are expecting a certificate chain, double the size of the client
-      // hello so that the response from the server can be larger - hopefully
-      // including the whole certificate chain.
-      out->set_minimum_size(kClientHelloMinimumSize * 2);
-    }
   }
 
   if (common_cert_sets) {
@@ -301,17 +392,20 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
 }
 
 QuicErrorCode QuicCryptoClientConfig::FillClientHello(
-    const string& server_hostname,
-    QuicGuid guid,
+    const QuicServerId& server_id,
+    QuicConnectionId connection_id,
+    const QuicVersion preferred_version,
     const CachedState* cached,
     QuicWallTime now,
     QuicRandom* rand,
+    const ChannelIDKey* channel_id_key,
     QuicCryptoNegotiatedParameters* out_params,
     CryptoHandshakeMessage* out,
     string* error_details) const {
-  DCHECK(error_details != NULL);
+  DCHECK(error_details != nullptr);
 
-  FillInchoateClientHello(server_hostname, cached, out_params, out);
+  FillInchoateClientHello(server_id, preferred_version, cached,
+                          out_params, out);
 
   const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
   if (!scfg) {
@@ -339,13 +433,18 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
     return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
   }
 
+  // AEAD: the work loads on the client and server are symmetric. Since the
+  // client is more likely to be CPU-constrained, break the tie by favoring
+  // the client's preference.
+  // Key exchange: the client does more work than the server, so favor the
+  // client's preference.
   size_t key_exchange_index;
   if (!QuicUtils::FindMutualTag(
-          aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY,
-          &out_params->aead, NULL) ||
+          aead, their_aeads, num_their_aeads, QuicUtils::LOCAL_PRIORITY,
+          &out_params->aead, nullptr) ||
       !QuicUtils::FindMutualTag(
           kexs, their_key_exchanges, num_their_key_exchanges,
-          QuicUtils::PEER_PRIORITY, &out_params->key_exchange,
+          QuicUtils::LOCAL_PRIORITY, &out_params->key_exchange,
           &key_exchange_index)) {
     *error_details = "Unsupported AEAD or KEXS";
     return QUIC_CRYPTO_NO_SUPPORT;
@@ -394,22 +493,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
   }
   out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value());
 
-  bool do_channel_id = false;
-  if (channel_id_signer_.get()) {
-    const QuicTag* their_proof_demands;
-    size_t num_their_proof_demands;
-    if (scfg->GetTaglist(kPDMD, &their_proof_demands,
-                         &num_their_proof_demands) == QUIC_NO_ERROR) {
-      for (size_t i = 0; i < num_their_proof_demands; i++) {
-        if (their_proof_demands[i] == kCHID) {
-          do_channel_id = true;
-          break;
-        }
-      }
-    }
-  }
-
-  if (do_channel_id) {
+  if (channel_id_key) {
     // In order to calculate the encryption key for the CETV block we need to
     // serialise the client hello as it currently is (i.e. without the CETV
     // block). For this, the client hello is serialized without padding.
@@ -423,14 +507,15 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
     const QuicData& client_hello_serialized = out->GetSerialized();
     hkdf_input.append(QuicCryptoConfig::kCETVLabel,
                       strlen(QuicCryptoConfig::kCETVLabel) + 1);
-    hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+    hkdf_input.append(reinterpret_cast<char*>(&connection_id),
+                      sizeof(connection_id));
     hkdf_input.append(client_hello_serialized.data(),
                       client_hello_serialized.length());
     hkdf_input.append(cached->server_config());
 
-    string key, signature;
-    if (!channel_id_signer_->Sign(server_hostname, hkdf_input,
-                                  &key, &signature)) {
+    string key = channel_id_key->SerializeKey();
+    string signature;
+    if (!channel_id_key->Sign(hkdf_input, &signature)) {
       *error_details = "Channel ID signature failed";
       return QUIC_INVALID_CHANNEL_ID_SIGNATURE;
     }
@@ -442,7 +527,8 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
     if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret,
                                  out_params->aead, out_params->client_nonce,
                                  out_params->server_nonce, hkdf_input,
-                                 CryptoUtils::CLIENT, &crypters)) {
+                                 CryptoUtils::CLIENT, &crypters,
+                                 nullptr /* subkey secret */)) {
       *error_details = "Symmetric key setup failed";
       return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
     }
@@ -463,9 +549,13 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
     out->set_minimum_size(orig_min_size);
   }
 
+  // Derive the symmetric keys and set up the encrypters and decrypters.
+  // Set the following members of out_params:
+  //   out_params->hkdf_input_suffix
+  //   out_params->initial_crypters
   out_params->hkdf_input_suffix.clear();
-  out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid),
-                                       sizeof(guid));
+  out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id),
+                                       sizeof(connection_id));
   const QuicData& client_hello_serialized = out->GetSerialized();
   out_params->hkdf_input_suffix.append(client_hello_serialized.data(),
                                        client_hello_serialized.length());
@@ -480,7 +570,8 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
   if (!CryptoUtils::DeriveKeys(
            out_params->initial_premaster_secret, out_params->aead,
            out_params->client_nonce, out_params->server_nonce, hkdf_input,
-           CryptoUtils::CLIENT, &out_params->initial_crypters)) {
+           CryptoUtils::CLIENT, &out_params->initial_crypters,
+           nullptr /* subkey secret */)) {
     *error_details = "Symmetric key setup failed";
     return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
   }
@@ -488,46 +579,42 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
   return QUIC_NO_ERROR;
 }
 
-QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
-    const CryptoHandshakeMessage& rej,
+QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig(
+    const CryptoHandshakeMessage& message,
     QuicWallTime now,
+    const vector<string>& cached_certs,
     CachedState* cached,
-    QuicCryptoNegotiatedParameters* out_params,
     string* error_details) {
-  DCHECK(error_details != NULL);
-
-  if (rej.tag() != kREJ) {
-    *error_details = "Message is not REJ";
-    return QUIC_CRYPTO_INTERNAL_ERROR;
-  }
+  DCHECK(error_details != nullptr);
 
   StringPiece scfg;
-  if (!rej.GetStringPiece(kSCFG, &scfg)) {
+  if (!message.GetStringPiece(kSCFG, &scfg)) {
     *error_details = "Missing SCFG";
     return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
   }
 
-  QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details);
-  if (error != QUIC_NO_ERROR) {
-    return error;
+  CachedState::ServerConfigState state = cached->SetServerConfig(
+      scfg, now, error_details);
+  if (state == CachedState::SERVER_CONFIG_EXPIRED) {
+    return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED;
+  }
+  // TODO(rtenneti): Return more specific error code than returning
+  // QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER.
+  if (state != CachedState::SERVER_CONFIG_VALID) {
+    return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
   }
 
   StringPiece token;
-  if (rej.GetStringPiece(kSourceAddressTokenTag, &token)) {
+  if (message.GetStringPiece(kSourceAddressTokenTag, &token)) {
     cached->set_source_address_token(token);
   }
 
-  StringPiece nonce;
-  if (rej.GetStringPiece(kServerNonceTag, &nonce)) {
-    out_params->server_nonce = nonce.as_string();
-  }
-
   StringPiece proof, cert_bytes;
-  bool has_proof = rej.GetStringPiece(kPROF, &proof);
-  bool has_cert = rej.GetStringPiece(kCertificateTag, &cert_bytes);
+  bool has_proof = message.GetStringPiece(kPROF, &proof);
+  bool has_cert = message.GetStringPiece(kCertificateTag, &cert_bytes);
   if (has_proof && has_cert) {
     vector<string> certs;
-    if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs,
+    if (!CertCompressor::DecompressChain(cert_bytes, cached_certs,
                                          common_cert_sets, &certs)) {
       *error_details = "Certificate data invalid";
       return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
@@ -535,7 +622,12 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
 
     cached->SetProof(certs, proof);
   } else {
-    cached->ClearProof();
+    if (proof_verifier() != nullptr) {
+      // Secure QUIC: clear existing proof as we have been sent a new SCFG
+      // without matching proof/certs.
+      cached->ClearProof();
+    }
+
     if (has_proof && !has_cert) {
       *error_details = "Certificate missing";
       return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
@@ -550,19 +642,96 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
   return QUIC_NO_ERROR;
 }
 
+QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
+    const CryptoHandshakeMessage& rej,
+    QuicWallTime now,
+    CachedState* cached,
+    bool is_https,
+    QuicCryptoNegotiatedParameters* out_params,
+    string* error_details) {
+  DCHECK(error_details != nullptr);
+
+  if (rej.tag() != kREJ) {
+    *error_details = "Message is not REJ";
+    return QUIC_CRYPTO_INTERNAL_ERROR;
+  }
+
+  QuicErrorCode error = CacheNewServerConfig(rej, now, out_params->cached_certs,
+                                             cached, error_details);
+  if (error != QUIC_NO_ERROR) {
+    return error;
+  }
+
+  StringPiece nonce;
+  if (rej.GetStringPiece(kServerNonceTag, &nonce)) {
+    out_params->server_nonce = nonce.as_string();
+  }
+
+  const uint32* reject_reasons;
+  size_t num_reject_reasons;
+  COMPILE_ASSERT(sizeof(QuicTag) == sizeof(uint32), header_out_of_sync);
+  if (rej.GetTaglist(kRREJ, &reject_reasons,
+                     &num_reject_reasons) == QUIC_NO_ERROR) {
+    uint32 packed_error = 0;
+    for (size_t i = 0; i < num_reject_reasons; ++i) {
+      // HANDSHAKE_OK is 0 and don't report that as error.
+      if (reject_reasons[i] == HANDSHAKE_OK || reject_reasons[i] >= 32) {
+        continue;
+      }
+      HandshakeFailureReason reason =
+          static_cast<HandshakeFailureReason>(reject_reasons[i]);
+      packed_error |= 1 << (reason - 1);
+    }
+    DVLOG(1) << "Reasons for rejection: " << packed_error;
+    if (is_https) {
+      UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Secure",
+                                  packed_error);
+    } else {
+      UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Insecure",
+                                  packed_error);
+    }
+  }
+
+  return QUIC_NO_ERROR;
+}
+
 QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
     const CryptoHandshakeMessage& server_hello,
-    QuicGuid guid,
+    QuicConnectionId connection_id,
+    const QuicVersionVector& negotiated_versions,
     CachedState* cached,
     QuicCryptoNegotiatedParameters* out_params,
     string* error_details) {
-  DCHECK(error_details != NULL);
+  DCHECK(error_details != nullptr);
 
   if (server_hello.tag() != kSHLO) {
     *error_details = "Bad tag";
     return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
   }
 
+  const QuicTag* supported_version_tags;
+  size_t num_supported_versions;
+
+  if (server_hello.GetTaglist(kVER, &supported_version_tags,
+                              &num_supported_versions) != QUIC_NO_ERROR) {
+    *error_details = "server hello missing version list";
+    return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+  }
+  if (!negotiated_versions.empty()) {
+    bool mismatch = num_supported_versions != negotiated_versions.size();
+    for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) {
+      mismatch = QuicTagToQuicVersion(supported_version_tags[i]) !=
+          negotiated_versions[i];
+    }
+    // The server sent a list of supported versions, and the connection
+    // reports that there was a version negotiation during the handshake.
+      // Ensure that these two lists are identical.
+    if (mismatch) {
+      *error_details = "Downgrade attack detected";
+      return QUIC_VERSION_NEGOTIATION_MISMATCH;
+    }
+  }
+
   // Learn about updated source address tokens.
   StringPiece token;
   if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) {
@@ -593,7 +762,8 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
   if (!CryptoUtils::DeriveKeys(
            out_params->forward_secure_premaster_secret, out_params->aead,
            out_params->client_nonce, out_params->server_nonce, hkdf_input,
-           CryptoUtils::CLIENT, &out_params->forward_secure_crypters)) {
+           CryptoUtils::CLIENT, &out_params->forward_secure_crypters,
+           &out_params->subkey_secret)) {
     *error_details = "Symmetric key setup failed";
     return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
   }
@@ -601,6 +771,23 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
   return QUIC_NO_ERROR;
 }
 
+QuicErrorCode QuicCryptoClientConfig::ProcessServerConfigUpdate(
+    const CryptoHandshakeMessage& server_config_update,
+    QuicWallTime now,
+    CachedState* cached,
+    QuicCryptoNegotiatedParameters* out_params,
+    string* error_details) {
+  DCHECK(error_details != nullptr);
+
+  if (server_config_update.tag() != kSCUP) {
+    *error_details = "ServerConfigUpdate must have kSCUP tag.";
+    return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+  }
+
+  return CacheNewServerConfig(server_config_update, now,
+                              out_params->cached_certs, cached, error_details);
+}
+
 ProofVerifier* QuicCryptoClientConfig::proof_verifier() const {
   return proof_verifier_.get();
 }
@@ -609,25 +796,82 @@ void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) {
   proof_verifier_.reset(verifier);
 }
 
-ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const {
-  return channel_id_signer_.get();
+ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const {
+  return channel_id_source_.get();
 }
 
-void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) {
-  channel_id_signer_.reset(signer);
+void QuicCryptoClientConfig::SetChannelIDSource(ChannelIDSource* source) {
+  channel_id_source_.reset(source);
 }
 
 void QuicCryptoClientConfig::InitializeFrom(
-    const std::string& server_hostname,
-    const std::string& canonical_server_hostname,
+    const QuicServerId& server_id,
+    const QuicServerId& canonical_server_id,
     QuicCryptoClientConfig* canonical_crypto_config) {
   CachedState* canonical_cached =
-      canonical_crypto_config->LookupOrCreate(canonical_server_hostname);
+      canonical_crypto_config->LookupOrCreate(canonical_server_id);
   if (!canonical_cached->proof_valid()) {
     return;
   }
-  CachedState* cached = LookupOrCreate(server_hostname);
+  CachedState* cached = LookupOrCreate(server_id);
   cached->InitializeFrom(*canonical_cached);
 }
 
+void QuicCryptoClientConfig::AddCanonicalSuffix(const string& suffix) {
+  canonical_suffixes_.push_back(suffix);
+}
+
+void QuicCryptoClientConfig::PreferAesGcm() {
+  DCHECK(!aead.empty());
+  if (aead.size() <= 1) {
+    return;
+  }
+  QuicTagVector::iterator pos = find(aead.begin(), aead.end(), kAESG);
+  if (pos != aead.end()) {
+    aead.erase(pos);
+    aead.insert(aead.begin(), kAESG);
+  }
+}
+
+void QuicCryptoClientConfig::DisableEcdsa() {
+  disable_ecdsa_ = true;
+}
+
+bool QuicCryptoClientConfig::PopulateFromCanonicalConfig(
+    const QuicServerId& server_id,
+    CachedState* server_state) {
+  DCHECK(server_state->IsEmpty());
+  size_t i = 0;
+  for (; i < canonical_suffixes_.size(); ++i) {
+    if (EndsWith(server_id.host(), canonical_suffixes_[i], false)) {
+      break;
+    }
+  }
+  if (i == canonical_suffixes_.size())
+    return false;
+
+  QuicServerId suffix_server_id(canonical_suffixes_[i], server_id.port(),
+                                server_id.is_https(),
+                                server_id.privacy_mode());
+  if (!ContainsKey(canonical_server_map_, suffix_server_id)) {
+    // This is the first host we've seen which matches the suffix, so make it
+    // canonical.
+    canonical_server_map_[suffix_server_id] = server_id;
+    return false;
+  }
+
+  const QuicServerId& canonical_server_id =
+      canonical_server_map_[suffix_server_id];
+  CachedState* canonical_state = cached_states_[canonical_server_id];
+  if (!canonical_state->proof_valid()) {
+    return false;
+  }
+
+  // Update canonical version to point at the "most recent" entry.
+  canonical_server_map_[suffix_server_id] = server_id;
+
+  server_state->InitializeFrom(*canonical_state);
+  return true;
+}
+
 }  // namespace net