Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(809)

Unified Diff: net/quic/crypto/crypto_handshake.cc

Issue 15937012: Land Recent QUIC changes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Small bug fixes Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/quic/crypto/crypto_handshake.cc
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc
index 445361adc1fec51993ac3cbf3c2ea0d78c497fbb..acc150481f1ae6031d6cd68866d0ebd195ccd8d0 100644
--- a/net/quic/crypto/crypto_handshake.cc
+++ b/net/quic/crypto/crypto_handshake.cc
@@ -14,6 +14,7 @@
#include "crypto/secure_hash.h"
#include "net/base/net_util.h"
#include "net/quic/crypto/cert_compressor.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/crypto_utils.h"
@@ -45,7 +46,7 @@ CryptoHandshakeMessage::CryptoHandshakeMessage(
tag_value_map_(other.tag_value_map_),
minimum_size_(other.minimum_size_) {
// Don't copy serialized_. scoped_ptr doesn't have a copy constructor.
- // The new object can reconstruct serialized_ lazily.
+ // The new object can lazily reconstruct serialized_.
}
CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
@@ -75,6 +76,10 @@ const QuicData& CryptoHandshakeMessage::GetSerialized() const {
return *serialized_.get();
}
+void CryptoHandshakeMessage::MarkDirty() {
+ serialized_.reset();
+}
+
void CryptoHandshakeMessage::Insert(QuicTagValueMap::const_iterator begin,
QuicTagValueMap::const_iterator end) {
tag_value_map_.insert(begin, end);
@@ -84,7 +89,7 @@ void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) {
// Warning, if sizeof(QuicTag) > sizeof(int) then this function will break
// because the terminating 0 will only be promoted to int.
COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int),
- crypto_tag_not_be_larger_than_int_or_varargs_will_break);
+ crypto_tag_may_not_be_larger_than_int_or_varargs_will_break);
vector<QuicTag> tags;
va_list ap;
@@ -110,6 +115,10 @@ void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) {
tag_value_map_[tag] = value.as_string();
}
+void CryptoHandshakeMessage::Erase(QuicTag tag) {
+ tag_value_map_.erase(tag);
+}
+
QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
const QuicTag** out_tags,
size_t* out_len) const {
@@ -179,15 +188,6 @@ QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag,
}
}
-bool CryptoHandshakeMessage::GetString(QuicTag tag, string* out) const {
- QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- if (it == tag_value_map_.end()) {
- return false;
- }
- *out = it->second;
- return true;
-}
-
QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag,
uint16* out) const {
return GetPOD(tag, out, sizeof(uint16));
@@ -218,6 +218,9 @@ size_t CryptoHandshakeMessage::size() const {
}
void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
+ if (min_bytes == minimum_size_) {
+ return;
+ }
serialized_.reset();
minimum_size_ = min_bytes;
}
@@ -327,17 +330,22 @@ QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {}
CrypterPair::CrypterPair() {}
+
CrypterPair::~CrypterPair() {}
// static
const char QuicCryptoConfig::kInitialLabel[] = "QUIC key expansion";
+// static
+const char QuicCryptoConfig::kCETVLabel[] = "QUIC CETV block";
+
+// static
const char QuicCryptoConfig::kForwardSecureLabel[] =
"QUIC forward secure key expansion";
QuicCryptoConfig::QuicCryptoConfig()
: version(0),
- common_cert_sets(new CommonCertSetsQUIC) {
+ common_cert_sets(CommonCertSets::GetInstanceQUIC()) {
}
QuicCryptoConfig::~QuicCryptoConfig() {}
@@ -353,8 +361,25 @@ QuicCryptoClientConfig::CachedState::CachedState()
QuicCryptoClientConfig::CachedState::~CachedState() {}
-bool QuicCryptoClientConfig::CachedState::is_complete() const {
- return !server_config_.empty() && server_config_valid_;
+bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
+ if (server_config_.empty() || !server_config_valid_) {
+ return false;
+ }
+
+ const CryptoHandshakeMessage* scfg = GetServerConfig();
+ if (!scfg) {
+ // Should be impossible short of cache corruption.
+ DCHECK(false);
+ return false;
+ }
+
+ uint64 expiry_seconds;
+ if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR ||
+ now.ToUNIXSeconds() >= expiry_seconds) {
+ return false;
+ }
+
+ return true;
}
const CryptoHandshakeMessage*
@@ -370,27 +395,57 @@ QuicCryptoClientConfig::CachedState::GetServerConfig() const {
return scfg_.get();
}
-bool QuicCryptoClientConfig::CachedState::SetServerConfig(StringPiece scfg) {
- if (scfg == server_config_) {
- return true;
+QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig(
+ StringPiece server_config, QuicWallTime now, string* error_details) {
+ const bool matches_existing = server_config == server_config_;
+
+ // Even if the new server config matches the existing one, we still wish to
+ // reject it if it has expired.
+ scoped_ptr<CryptoHandshakeMessage> new_scfg_storage;
+ const CryptoHandshakeMessage* new_scfg;
+
+ if (!matches_existing) {
+ new_scfg_storage.reset(CryptoFramer::ParseMessage(server_config));
+ new_scfg = new_scfg_storage.get();
+ } else {
+ new_scfg = GetServerConfig();
}
- scfg_.reset(CryptoFramer::ParseMessage(scfg));
- if (!scfg_.get()) {
- return false;
+ if (!new_scfg) {
+ *error_details = "SCFG invalid";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ uint64 expiry_seconds;
+ if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+ *error_details = "SCFG missing EXPY";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
- server_config_ = scfg.as_string();
+
+ if (now.ToUNIXSeconds() >= expiry_seconds) {
+ *error_details = "SCFG has expired";
+ return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED;
+ }
+
+ if (!matches_existing) {
+ server_config_ = server_config.as_string();
+ server_config_valid_ = false;
+ scfg_.reset(new_scfg_storage.release());
+ }
+ return QUIC_NO_ERROR;
+}
+
+void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() {
+ server_config_.clear();
+ scfg_.reset();
server_config_valid_ = false;
- return true;
}
void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs,
StringPiece signature) {
- bool has_changed = signature != server_config_sig_;
+ bool has_changed =
+ signature != server_config_sig_ || certs_.size() != certs.size();
- if (certs_.size() != certs.size()) {
- has_changed = true;
- }
if (!has_changed) {
for (size_t i = 0; i < certs_.size(); i++) {
if (certs_[i] != certs[i]) {
@@ -442,6 +497,7 @@ void QuicCryptoClientConfig::CachedState::set_source_address_token(
void QuicCryptoClientConfig::SetDefaults() {
// Version must be 0.
+ // TODO(agl): this version stuff is obsolete now.
version = QuicCryptoConfig::CONFIG_VERSION;
// Key exchange methods.
@@ -490,7 +546,7 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
out->SetTaglist(kPDMD, kX509, 0);
}
- if (common_cert_sets.get()) {
+ if (common_cert_sets) {
out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes());
}
@@ -527,22 +583,11 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
if (!scfg) {
// This should never happen as our caller should have checked
- // cached->is_complete() before calling this function.
+ // cached->IsComplete() before calling this function.
*error_details = "Handshake not ready";
return QUIC_CRYPTO_INTERNAL_ERROR;
}
- uint64 expiry_seconds;
- if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
- *error_details = "SCFG missing EXPY";
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (static_cast<uint64>(now.ToUNIXSeconds()) >= expiry_seconds) {
- *error_details = "SCFG expired";
- return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED;
- }
-
StringPiece scid;
if (!scfg->GetStringPiece(kSCID, &scid)) {
*error_details = "SCFG missing SCID";
@@ -550,13 +595,6 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
}
out->SetStringPiece(kSCID, scid);
- // Calculate the mutual algorithms that the connection is going to use.
- if (scfg->GetUint16(kVERS, &out_params->version) != QUIC_NO_ERROR ||
- out_params->version != QuicCryptoConfig::CONFIG_VERSION) {
- *error_details = "Bad version";
- return QUIC_CRYPTO_VERSION_NOT_SUPPORTED;
- }
-
const QuicTag* their_aeads;
const QuicTag* their_key_exchanges;
size_t num_their_aeads, num_their_key_exchanges;
@@ -569,9 +607,9 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
}
size_t key_exchange_index;
- if (!QuicUtils::FindMutualTag(aead, their_aeads, num_their_aeads,
- QuicUtils::PEER_PRIORITY, &out_params->aead,
- NULL) ||
+ if (!QuicUtils::FindMutualTag(
+ aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY,
+ &out_params->aead, NULL) ||
!QuicUtils::FindMutualTag(
kexs, their_key_exchanges, num_their_key_exchanges,
QuicUtils::PEER_PRIORITY, &out_params->key_exchange,
@@ -607,8 +645,8 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
Curve25519KeyExchange::NewPrivateKey(rand)));
break;
case kP256:
- out_params->client_key_exchange
- .reset(P256KeyExchange::New(P256KeyExchange::NewPrivateKey()));
+ out_params->client_key_exchange.reset(P256KeyExchange::New(
+ P256KeyExchange::NewPrivateKey()));
break;
default:
DCHECK(false);
@@ -623,6 +661,68 @@ 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) {
+ // 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.
+ const size_t orig_min_size = out->minimum_size();
+ out->set_minimum_size(0);
+
+ CryptoHandshakeMessage cetv;
+ cetv.set_tag(kCETV);
+
+ string hkdf_input;
+ 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(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)) {
+ *error_details = "Channel ID signature failed";
+ return QUIC_INTERNAL_ERROR;
+ }
+
+ cetv.SetStringPiece(kCIDK, key);
+ cetv.SetStringPiece(kCIDS, signature);
+
+ CrypterPair crypters;
+ CryptoUtils::DeriveKeys(out_params->initial_premaster_secret,
+ out_params->aead, out_params->client_nonce,
+ out_params->server_nonce, hkdf_input,
+ CryptoUtils::CLIENT, &crypters);
+
+ const QuicData& cetv_plaintext = cetv.GetSerialized();
+ scoped_ptr<QuicData> cetv_ciphertext(crypters.encrypter->EncryptPacket(
+ 0 /* sequence number */,
+ StringPiece() /* associated data */,
+ cetv_plaintext.AsStringPiece()));
+
+ out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece());
+ out->MarkDirty();
+
+ out->set_minimum_size(orig_min_size);
+ }
+
out_params->hkdf_input_suffix.clear();
out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid),
sizeof(guid));
@@ -648,19 +748,25 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
CachedState* cached,
const CryptoHandshakeMessage& rej,
+ QuicWallTime now,
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;
+ }
+
StringPiece scfg;
if (!rej.GetStringPiece(kSCFG, &scfg)) {
*error_details = "Missing SCFG";
return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
}
- if (!cached->SetServerConfig(scfg)) {
- *error_details = "Invalid SCFG";
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
}
StringPiece token;
@@ -669,8 +775,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
}
StringPiece nonce;
- if (rej.GetStringPiece(kServerNonceTag, &nonce) && nonce.size() ==
- kNonceSize) {
+ if (rej.GetStringPiece(kServerNonceTag, &nonce)) {
out_params->server_nonce = nonce.as_string();
}
@@ -679,7 +784,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
rej.GetStringPiece(kCertificateTag, &cert_bytes)) {
vector<string> certs;
if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs,
- common_cert_sets.get(), &certs)) {
+ common_cert_sets, &certs)) {
*error_details = "Certificate data invalid";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
@@ -739,4 +844,8 @@ void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) {
proof_verifier_.reset(verifier);
}
+void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) {
+ channel_id_signer_.reset(signer);
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698