summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-23 20:18:55 +0000
committerrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-23 20:18:55 +0000
commitfe053f9301b7003d0bbe82baf9001d5b95564b9e (patch)
treec5356ff0f9fa4754f00d30944f60544edbf20c46 /net
parent7252963941dffa25e28168c82ab262ae889ffe1d (diff)
downloadchromium_src-fe053f9301b7003d0bbe82baf9001d5b95564b9e.zip
chromium_src-fe053f9301b7003d0bbe82baf9001d5b95564b9e.tar.gz
chromium_src-fe053f9301b7003d0bbe82baf9001d5b95564b9e.tar.bz2
Land Recent QUIC Changes
QUIC: step 8, server certificate support. Merge internal change: 44460951 Returning early from framer callbacks on error. Merge internal change: 44428665 Rename QUIC_VERSION_NOT_SUPPORTED to QUIC_CRYPTO_VERSION_NOT_SUPPORTED. Merge internal change: 44422561 QUIC: split the server config into its own file. This change moves QuicCryptoServerConfig into a separate file so that Chromium need only link it into tests. Merge internal change: 44397707 QUIC: remove ifs around error_details They were never needed and clutter up the code. Merge internal change: 44275147 QUIC: add tests for 0-RTT handshaking using strike-register. This change fixes a couple of issues and adds a test that performs a 0-RTT handshake. Merge internal change: 44272981 R=rch@chromium.org Review URL: https://codereview.chromium.org/14411004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195897 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/net.gyp9
-rw-r--r--net/quic/crypto/crypto_handshake.cc607
-rw-r--r--net/quic/crypto/crypto_handshake.h224
-rw-r--r--net/quic/crypto/crypto_handshake_test.cc1
-rw-r--r--net/quic/crypto/crypto_protocol.h6
-rw-r--r--net/quic/crypto/crypto_server_config.cc586
-rw-r--r--net/quic/crypto/crypto_server_config.h164
-rw-r--r--net/quic/crypto/crypto_server_config_protobuf.cc18
-rw-r--r--net/quic/crypto/crypto_server_config_protobuf.h71
-rw-r--r--net/quic/crypto/crypto_utils.cc7
-rw-r--r--net/quic/crypto/proof_source.h48
-rw-r--r--net/quic/crypto/source_address_token.cc46
-rw-r--r--net/quic/crypto/source_address_token.h48
-rw-r--r--net/quic/quic_config.cc18
-rw-r--r--net/quic/quic_connection.cc2
-rw-r--r--net/quic/quic_crypto_client_stream.cc4
-rw-r--r--net/quic/quic_crypto_client_stream.h5
-rw-r--r--net/quic/quic_crypto_server_stream.cc1
-rw-r--r--net/quic/quic_crypto_server_stream_test.cc86
-rw-r--r--net/quic/quic_protocol.h2
-rw-r--r--net/quic/quic_utils.cc2
-rw-r--r--net/quic/test_tools/crypto_test_utils.cc108
-rw-r--r--net/quic/test_tools/crypto_test_utils.h22
-rw-r--r--net/quic/test_tools/mock_random.cc9
-rw-r--r--net/quic/test_tools/mock_random.h13
-rw-r--r--net/quic/test_tools/quic_test_utils.cc16
-rw-r--r--net/quic/test_tools/quic_test_utils.h9
-rw-r--r--net/quic/test_tools/simple_quic_framer.cc7
-rw-r--r--net/quic/test_tools/simple_quic_framer.h4
-rw-r--r--net/tools/quic/quic_dispatcher_test.cc1
-rw-r--r--net/tools/quic/quic_server.h1
-rw-r--r--net/tools/quic/test_tools/quic_test_utils.cc6
-rw-r--r--net/tools/quic/test_tools/quic_test_utils.h4
33 files changed, 1298 insertions, 857 deletions
diff --git a/net/net.gyp b/net/net.gyp
index 0a6560d..3a78c4d 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -712,6 +712,10 @@
'quic/crypto/crypto_handshake.cc',
'quic/crypto/crypto_handshake.h',
'quic/crypto/crypto_protocol.h',
+ 'quic/crypto/crypto_server_config.cc',
+ 'quic/crypto/crypto_server_config.h',
+ 'quic/crypto/crypto_server_config_protobuf.cc',
+ 'quic/crypto/crypto_server_config_protobuf.h',
'quic/crypto/crypto_utils.cc',
'quic/crypto/crypto_utils.h',
'quic/crypto/curve25519_key_exchange.cc',
@@ -724,6 +728,7 @@
'quic/crypto/p256_key_exchange.h',
'quic/crypto/p256_key_exchange_nss.cc',
'quic/crypto/p256_key_exchange_openssl.cc',
+ 'quic/crypto/proof_source.h',
'quic/crypto/quic_decrypter.cc',
'quic/crypto/quic_decrypter.h',
'quic/crypto/quic_encrypter.cc',
@@ -733,6 +738,8 @@
'quic/crypto/scoped_evp_cipher_ctx.h',
'quic/crypto/strike_register.cc',
'quic/crypto/strike_register.h',
+ 'quic/crypto/source_address_token.cc',
+ 'quic/crypto/source_address_token.h',
'quic/quic_bandwidth.cc',
'quic/quic_bandwidth.h',
'quic/quic_blocked_writer_interface.h',
@@ -2516,6 +2523,7 @@
'type': 'static_library',
'dependencies': [
'../base/base.gyp:base',
+ '../crypto/crypto.gyp:crypto',
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
'../build/temp_gyp/googleurl.gyp:googleurl',
'../third_party/openssl/openssl.gyp:openssl',
@@ -2587,6 +2595,7 @@
'type': '<(gtest_target_type)',
'dependencies': [
'../base/base.gyp:test_support_base',
+ '../crypto/crypto.gyp:crypto',
'../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
'net',
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc
index 1d9b01b..59ecd42 100644
--- a/net/quic/crypto/crypto_handshake.cc
+++ b/net/quic/crypto/crypto_handshake.cc
@@ -10,11 +10,8 @@
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
-#include "crypto/hkdf.h"
#include "crypto/secure_hash.h"
#include "net/base/net_util.h"
-#include "net/quic/crypto/aes_128_gcm_decrypter.h"
-#include "net/quic/crypto/aes_128_gcm_encrypter.h"
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/curve25519_key_exchange.h"
@@ -23,7 +20,6 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/quic_random.h"
-#include "net/quic/crypto/strike_register.h"
#include "net/quic/quic_clock.h"
#include "net/quic/quic_protocol.h"
@@ -35,23 +31,6 @@ using std::vector;
namespace net {
-// kVersion contains the one (and, for the moment, only) version number that we
-// implement.
-static const uint16 kVersion = 0;
-
-// kLabel is constant that is used in key derivation to tie the resulting key
-// to this protocol.
-static const char kLabel[] = "QUIC key expansion";
-
-using crypto::SecureHash;
-
-QuicServerConfigProtobuf::QuicServerConfigProtobuf() {
-}
-
-QuicServerConfigProtobuf::~QuicServerConfigProtobuf() {
- STLDeleteElements(&keys_);
-}
-
CryptoHandshakeMessage::CryptoHandshakeMessage() : tag_(0) {}
CryptoHandshakeMessage::CryptoHandshakeMessage(
@@ -284,6 +263,7 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
case kKEXS:
case kAEAD:
case kCGST:
+ case kPDMD:
// tag lists
if (it->second.size() % sizeof(CryptoTag) == 0) {
for (size_t j = 0; j < it->second.size(); j += sizeof(CryptoTag)) {
@@ -324,35 +304,6 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
return ret;
}
-SourceAddressToken::SourceAddressToken() {
-}
-
-SourceAddressToken::~SourceAddressToken() {
-}
-
-string SourceAddressToken::SerializeAsString() const {
- return ip_ + " " + base::Int64ToString(timestamp_);
-}
-
-bool SourceAddressToken::ParseFromArray(unsigned char* plaintext,
- size_t plaintext_length) {
- string data(reinterpret_cast<const char*>(plaintext), plaintext_length);
- std::vector<std::string> results;
- base::SplitString(data, ' ', &results);
- if (results.size() < 2) {
- return false;
- }
-
- int64 timestamp;
- if (!base::StringToInt64(results[1], &timestamp)) {
- return false;
- }
-
- ip_ = results[0];
- timestamp_ = timestamp;
- return true;
-}
-
QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
: version(0),
key_exchange(0),
@@ -363,6 +314,9 @@ QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {
}
+// static
+const char QuicCryptoConfig::kLabel[] = "QUIC key expansion";
+
QuicCryptoConfig::QuicCryptoConfig()
: version(0) {
}
@@ -428,7 +382,7 @@ void QuicCryptoClientConfig::CachedState::set_source_address_token(
void QuicCryptoClientConfig::SetDefaults() {
// Version must be 0.
- version = kVersion;
+ version = QuicCryptoConfig::CONFIG_VERSION;
// Key exchange methods.
kexs.resize(2);
@@ -468,6 +422,8 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
if (cached && !cached->source_address_token().empty()) {
out->SetStringPiece(kSRCT, cached->source_address_token());
}
+
+ out->SetTaglist(kPDMD, kX509, 0);
}
QuicErrorCode QuicCryptoClientConfig::FillClientHello(
@@ -479,34 +435,30 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
QuicCryptoNegotiatedParameters* out_params,
CryptoHandshakeMessage* out,
string* error_details) const {
+ DCHECK(error_details != NULL);
+
FillInchoateClientHello(server_hostname, cached, out);
const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
if (!scfg) {
// This should never happen as our caller should have checked
// cached->is_complete() before calling this function.
- if (error_details) {
- *error_details = "Handshake not ready";
- }
+ *error_details = "Handshake not ready";
return QUIC_CRYPTO_INTERNAL_ERROR;
}
StringPiece scid;
if (!scfg->GetStringPiece(kSCID, &scid)) {
- if (error_details) {
- *error_details = "SCFG missing SCID";
- }
+ *error_details = "SCFG missing SCID";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
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 != kVersion) {
- if (error_details) {
- *error_details = "Bad version";
- }
- return QUIC_VERSION_NOT_SUPPORTED;
+ out_params->version != QuicCryptoConfig::CONFIG_VERSION) {
+ *error_details = "Bad version";
+ return QUIC_CRYPTO_VERSION_NOT_SUPPORTED;
}
const CryptoTag* their_aeads;
@@ -516,9 +468,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
&num_their_aeads) != QUIC_NO_ERROR ||
scfg->GetTaglist(kKEXS, &their_key_exchanges,
&num_their_key_exchanges) != QUIC_NO_ERROR) {
- if (error_details) {
- *error_details = "Missing AEAD or KEXS";
- }
+ *error_details = "Missing AEAD or KEXS";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
@@ -533,9 +483,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
CryptoUtils::PEER_PRIORITY,
&out_params->key_exchange,
&key_exchange_index)) {
- if (error_details) {
- *error_details = "Unsupported AEAD or KEXS";
- }
+ *error_details = "Unsupported AEAD or KEXS";
return QUIC_CRYPTO_NO_SUPPORT;
}
out->SetTaglist(kAEAD, out_params->aead, 0);
@@ -544,18 +492,14 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
StringPiece public_value;
if (scfg->GetNthValue16(kPUBS, key_exchange_index, &public_value) !=
QUIC_NO_ERROR) {
- if (error_details) {
- *error_details = "Missing public value";
- }
+ *error_details = "Missing public value";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
StringPiece orbit;
if (!scfg->GetStringPiece(kORBT, &orbit) ||
orbit.size() != kOrbitSize) {
- if (error_details) {
- *error_details = "SCFG missing OBIT";
- }
+ *error_details = "SCFG missing OBIT";
return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
}
@@ -576,22 +520,19 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
break;
default:
DCHECK(false);
- if (error_details) {
- *error_details = "Configured to support an unknown key exchange";
- }
+ *error_details = "Configured to support an unknown key exchange";
return QUIC_CRYPTO_INTERNAL_ERROR;
}
if (!key_exchange->CalculateSharedKey(public_value,
&out_params->premaster_secret)) {
- if (error_details) {
- *error_details = "Key exchange failure";
- }
+ *error_details = "Key exchange failure";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
out->SetStringPiece(kPUBS, key_exchange->public_value());
- string hkdf_input(kLabel, arraysize(kLabel));
+ string hkdf_input(QuicCryptoConfig::kLabel,
+ strlen(QuicCryptoConfig::kLabel) + 1);
hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
const QuicData& client_hello_serialized = out->GetSerialized();
@@ -609,8 +550,9 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
const CryptoHandshakeMessage& rej,
QuicCryptoNegotiatedParameters* out_params,
string* error_details) {
- CachedState* cached;
+ DCHECK(error_details != NULL);
+ CachedState* cached;
map<string, CachedState*>::const_iterator it =
cached_states_.find(server_hostname);
if (it == cached_states_.end()) {
@@ -622,16 +564,12 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
StringPiece scfg;
if (!rej.GetStringPiece(kSCFG, &scfg)) {
- if (error_details) {
- *error_details = "Missing SCFG";
- }
+ *error_details = "Missing SCFG";
return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
}
if (!cached->SetServerConfig(scfg)) {
- if (error_details) {
- *error_details = "Invalid SCFG";
- }
+ *error_details = "Invalid SCFG";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
@@ -654,6 +592,8 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
const string& nonce,
QuicCryptoNegotiatedParameters* out_params,
string* error_details) {
+ DCHECK(error_details != NULL);
+
if (server_hello.tag() != kSHLO) {
*error_details = "Bad tag";
return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
@@ -666,495 +606,4 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
return QUIC_NO_ERROR;
}
-// static
-const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
-
-QuicCryptoServerConfig::QuicCryptoServerConfig(
- StringPiece source_address_token_secret)
- // AES-GCM is used to encrypt and authenticate source address tokens. The
- // full, 96-bit nonce is used but we must ensure that an attacker cannot
- // obtain two source address tokens with the same nonce. This occurs with
- // probability 0.5 after 2**48 values. We assume that obtaining 2**48
- // source address tokens is not possible: at a rate of 10M packets per
- // second, it would still take the attacker a year to obtain the needed
- // number of packets.
- //
- // TODO(agl): switch to an encrypter with a larger nonce space (i.e.
- // Salsa20+Poly1305).
- : strike_register_lock_(),
- source_address_token_encrypter_(new Aes128GcmEncrypter),
- source_address_token_decrypter_(new Aes128GcmDecrypter) {
- crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */,
- "QUIC source address token key",
- source_address_token_encrypter_->GetKeySize(),
- 0 /* no fixed IV needed */);
- source_address_token_encrypter_->SetKey(hkdf.server_write_key());
- source_address_token_decrypter_->SetKey(hkdf.server_write_key());
-}
-
-QuicCryptoServerConfig::~QuicCryptoServerConfig() {
- STLDeleteValues(&configs_);
-}
-
-// static
-QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
- QuicRandom* rand,
- const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags) {
- CryptoHandshakeMessage msg;
-
- const string curve25519_private_key =
- Curve25519KeyExchange::NewPrivateKey(rand);
- scoped_ptr<Curve25519KeyExchange> curve25519(
- Curve25519KeyExchange::New(curve25519_private_key));
- StringPiece curve25519_public_value = curve25519->public_value();
-
- const string p256_private_key =
- P256KeyExchange::NewPrivateKey();
- scoped_ptr<P256KeyExchange> p256(
- P256KeyExchange::New(p256_private_key));
- StringPiece p256_public_value = p256->public_value();
-
- string encoded_public_values;
- // First two bytes encode the length of the public value.
- encoded_public_values.push_back(curve25519_public_value.size());
- encoded_public_values.push_back(curve25519_public_value.size() >> 8);
- encoded_public_values.append(curve25519_public_value.data(),
- curve25519_public_value.size());
- encoded_public_values.push_back(p256_public_value.size());
- encoded_public_values.push_back(p256_public_value.size() >> 8);
- encoded_public_values.append(p256_public_value.data(),
- p256_public_value.size());
-
- msg.set_tag(kSCFG);
- msg.SetTaglist(kKEXS, kC255, kP256, 0);
- msg.SetTaglist(kAEAD, kAESG, 0);
- msg.SetValue(kVERS, static_cast<uint16>(0));
- msg.SetStringPiece(kPUBS, encoded_public_values);
- msg.Insert(extra_tags.tag_value_map().begin(),
- extra_tags.tag_value_map().end());
-
- char scid_bytes[16];
- rand->RandBytes(scid_bytes, sizeof(scid_bytes));
- msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes)));
-
- char orbit_bytes[kOrbitSize];
- rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
- msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes)));
-
- scoped_ptr<QuicData> serialized(
- CryptoFramer::ConstructHandshakeMessage(msg));
-
- scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf);
- config->set_config(serialized->AsStringPiece());
- QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key();
- curve25519_key->set_tag(kC255);
- curve25519_key->set_private_key(curve25519_private_key);
- QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key();
- p256_key->set_tag(kP256);
- p256_key->set_private_key(p256_private_key);
-
- return config.release();
-}
-
-CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
- QuicServerConfigProtobuf* protobuf) {
- scoped_ptr<CryptoHandshakeMessage> msg(
- CryptoFramer::ParseMessage(protobuf->config()));
-
- if (!msg.get()) {
- LOG(WARNING) << "Failed to parse server config message";
- return NULL;
- }
- if (msg->tag() != kSCFG) {
- LOG(WARNING) << "Server config message has tag "
- << msg->tag() << " expected "
- << kSCFG;
- return NULL;
- }
-
- scoped_ptr<Config> config(new Config);
- config->serialized = protobuf->config();
-
- StringPiece scid;
- if (!msg->GetStringPiece(kSCID, &scid)) {
- LOG(WARNING) << "Server config message is missing SCID";
- return NULL;
- }
- config->id = scid.as_string();
-
- const CryptoTag* aead_tags;
- size_t aead_len;
- if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) {
- LOG(WARNING) << "Server config message is missing AEAD";
- return NULL;
- }
- config->aead = vector<CryptoTag>(aead_tags, aead_tags + aead_len);
-
- const CryptoTag* kexs_tags;
- size_t kexs_len;
- if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) {
- LOG(WARNING) << "Server config message is missing KEXS";
- return NULL;
- }
-
- StringPiece orbit;
- if (!msg->GetStringPiece(kORBT, &orbit)) {
- LOG(WARNING) << "Server config message is missing OBIT";
- return NULL;
- }
-
- if (orbit.size() != kOrbitSize) {
- LOG(WARNING) << "Orbit value in server config is the wrong length."
- " Got " << orbit.size() << " want " << kOrbitSize;
- return NULL;
- }
- COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size);
- memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
-
- if (kexs_len != protobuf->key_size()) {
- LOG(WARNING) << "Server config has "
- << kexs_len
- << " key exchange methods configured, but "
- << protobuf->key_size()
- << " private keys";
- return NULL;
- }
-
- for (size_t i = 0; i < kexs_len; i++) {
- const CryptoTag tag = kexs_tags[i];
- string private_key;
-
- config->kexs.push_back(tag);
-
- for (size_t j = 0; j < protobuf->key_size(); j++) {
- const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i);
- if (key.tag() == tag) {
- private_key = key.private_key();
- break;
- }
- }
-
- if (private_key.empty()) {
- LOG(WARNING) << "Server config contains key exchange method without "
- "corresponding private key: "
- << tag;
- return NULL;
- }
-
- scoped_ptr<KeyExchange> ka;
- switch (tag) {
- case kC255:
- ka.reset(Curve25519KeyExchange::New(private_key));
- if (!ka.get()) {
- LOG(WARNING) << "Server config contained an invalid curve25519"
- " private key.";
- return NULL;
- }
- break;
- case kP256:
- ka.reset(P256KeyExchange::New(private_key));
- if (!ka.get()) {
- LOG(WARNING) << "Server config contained an invalid P-256"
- " private key.";
- return NULL;
- }
- break;
- default:
- LOG(WARNING) << "Server config message contains unknown key exchange "
- "method: "
- << tag;
- return NULL;
- }
-
- for (vector<KeyExchange*>::const_iterator i = config->key_exchanges.begin();
- i != config->key_exchanges.end(); ++i) {
- if ((*i)->tag() == tag) {
- LOG(WARNING) << "Duplicate key exchange in config: " << tag;
- return NULL;
- }
- }
-
- config->key_exchanges.push_back(ka.release());
- }
-
- if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) {
- LOG(WARNING) << "Server config message is missing version";
- return NULL;
- }
-
- if (config->version != kVersion) {
- LOG(WARNING) << "Server config specifies an unsupported version";
- return NULL;
- }
-
- scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
- sha256->Update(protobuf->config().data(), protobuf->config().size());
- char id_bytes[16];
- sha256->Finish(id_bytes, sizeof(id_bytes));
- const string id(id_bytes, sizeof(id_bytes));
-
- configs_[id] = config.release();
- active_config_ = id;
-
- return msg.release();
-}
-
-CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
- QuicRandom* rand,
- const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags) {
- scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig(
- rand, clock, extra_tags));
- return AddConfig(config.get());
-}
-
-QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
- const CryptoHandshakeMessage& client_hello,
- QuicGuid guid,
- const IPEndPoint& client_ip,
- QuicTime::Delta now_since_unix_epoch,
- QuicRandom* rand,
- QuicCryptoNegotiatedParameters *params,
- CryptoHandshakeMessage* out,
- string* error_details) const {
- CHECK(!configs_.empty());
- // FIXME(agl): we should use the client's SCID, not just the active config.
- map<ServerConfigID, Config*>::const_iterator it =
- configs_.find(active_config_);
- if (it == configs_.end()) {
- *error_details = "No valid server config loaded";
- return QUIC_CRYPTO_INTERNAL_ERROR;
- }
- const Config* const config(it->second);
-
- bool valid_source_address_token = false;
- StringPiece srct;
- if (client_hello.GetStringPiece(kSRCT, &srct) &&
- ValidateSourceAddressToken(srct, client_ip, now_since_unix_epoch)) {
- valid_source_address_token = true;
- }
-
- const string fresh_source_address_token =
- NewSourceAddressToken(client_ip, rand, now_since_unix_epoch);
-
- // If we previously sent a REJ to this client then we may have stored a
- // server nonce in |params|. In which case, we know that the connection
- // is unique because the server nonce will be mixed into the key generation.
- bool unique_by_server_nonce = !params->server_nonce.empty();
- // If we can't ensure uniqueness by a server nonce, then we will try and use
- // the strike register.
- bool unique_by_strike_register = false;
-
- StringPiece client_nonce;
- bool client_nonce_well_formed = false;
- if (client_hello.GetStringPiece(kNONC, &client_nonce) &&
- client_nonce.size() == kNonceSize) {
- client_nonce_well_formed = true;
- if (!unique_by_server_nonce) {
- base::AutoLock auto_lock(strike_register_lock_);
-
- if (strike_register_.get() == NULL) {
- strike_register_.reset(new StrikeRegister(
- // TODO(agl): these magic numbers should come from config.
- 1024 /* max entries */,
- static_cast<uint32>(now_since_unix_epoch.ToSeconds()),
- 600 /* window secs */, config->orbit));
- }
- unique_by_strike_register = strike_register_->Insert(
- reinterpret_cast<const uint8*>(client_nonce.data()),
- static_cast<uint32>(now_since_unix_epoch.ToSeconds()));
- }
- }
-
- StringPiece scid;
- if (!client_hello.GetStringPiece(kSCID, &scid) ||
- scid.as_string() != config->id ||
- !valid_source_address_token ||
- !client_nonce_well_formed ||
- (!unique_by_strike_register &&
- !unique_by_server_nonce)) {
- // If the client didn't provide a server config ID, or gave the wrong one,
- // then the handshake cannot possibly complete. We reject the handshake and
- // give the client enough information to do better next time.
- out->Clear();
- out->set_tag(kREJ);
- out->SetStringPiece(kSCFG, config->serialized);
- out->SetStringPiece(kSRCT, fresh_source_address_token);
- if (params->server_nonce.empty()) {
- CryptoUtils::GenerateNonce(
- now_since_unix_epoch, rand,
- StringPiece(reinterpret_cast<const char*>(config->orbit),
- sizeof(config->orbit)),
- &params->server_nonce);
- }
- out->SetStringPiece(kNONC, params->server_nonce);
- return QUIC_NO_ERROR;
- }
-
- const CryptoTag* their_aeads;
- const CryptoTag* their_key_exchanges;
- size_t num_their_aeads, num_their_key_exchanges;
- if (client_hello.GetTaglist(kAEAD, &their_aeads,
- &num_their_aeads) != QUIC_NO_ERROR ||
- client_hello.GetTaglist(kKEXS, &their_key_exchanges,
- &num_their_key_exchanges) != QUIC_NO_ERROR ||
- num_their_aeads != 1 ||
- num_their_key_exchanges != 1) {
- if (error_details) {
- *error_details = "Missing or invalid AEAD or KEXS";
- }
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- size_t key_exchange_index;
- if (!CryptoUtils::FindMutualTag(config->aead,
- their_aeads, num_their_aeads,
- CryptoUtils::LOCAL_PRIORITY,
- &params->aead,
- NULL) ||
- !CryptoUtils::FindMutualTag(config->kexs,
- their_key_exchanges, num_their_key_exchanges,
- CryptoUtils::LOCAL_PRIORITY,
- &params->key_exchange,
- &key_exchange_index)) {
- if (error_details) {
- *error_details = "Unsupported AEAD or KEXS";
- }
- return QUIC_CRYPTO_NO_SUPPORT;
- }
-
- StringPiece public_value;
- if (!client_hello.GetStringPiece(kPUBS, &public_value)) {
- if (error_details) {
- *error_details = "Missing public value";
- }
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey(
- public_value, &params->premaster_secret)) {
- if (error_details) {
- *error_details = "Invalid public value";
- }
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- params->server_config_id = scid.as_string();
-
- string hkdf_input(kLabel, arraysize(kLabel));
- hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
-
- const QuicData& client_hello_serialized = client_hello.GetSerialized();
- hkdf_input.append(client_hello_serialized.data(),
- client_hello_serialized.length());
- hkdf_input.append(config->serialized);
-
- CryptoUtils::DeriveKeys(params, client_nonce, hkdf_input,
- CryptoUtils::SERVER);
-
- out->set_tag(kSHLO);
- out->SetStringPiece(kSRCT, fresh_source_address_token);
- return QUIC_NO_ERROR;
-}
-
-string QuicCryptoServerConfig::NewSourceAddressToken(
- const IPEndPoint& ip,
- QuicRandom* rand,
- QuicTime::Delta now_since_epoch) const {
- SourceAddressToken source_address_token;
- source_address_token.set_ip(ip.ToString());
- source_address_token.set_timestamp(now_since_epoch.ToSeconds());
-
- string plaintext = source_address_token.SerializeAsString();
- char nonce[12];
- DCHECK_EQ(sizeof(nonce),
- source_address_token_encrypter_->GetNoncePrefixSize() +
- sizeof(QuicPacketSequenceNumber));
- rand->RandBytes(nonce, sizeof(nonce));
-
- size_t ciphertext_size =
- source_address_token_encrypter_->GetCiphertextSize(plaintext.size());
- string result;
- result.resize(sizeof(nonce) + ciphertext_size);
- memcpy(&result[0], &nonce, sizeof(nonce));
-
- if (!source_address_token_encrypter_->Encrypt(
- StringPiece(nonce, sizeof(nonce)), StringPiece(), plaintext,
- reinterpret_cast<unsigned char*>(&result[sizeof(nonce)]))) {
- DCHECK(false);
- return string();
- }
-
- return result;
-}
-
-bool QuicCryptoServerConfig::ValidateSourceAddressToken(
- StringPiece token,
- const IPEndPoint& ip,
- QuicTime::Delta now_since_epoch) const {
- char nonce[12];
- DCHECK_EQ(sizeof(nonce),
- source_address_token_encrypter_->GetNoncePrefixSize() +
- sizeof(QuicPacketSequenceNumber));
-
- if (token.size() <= sizeof(nonce)) {
- return false;
- }
- memcpy(&nonce, token.data(), sizeof(nonce));
- token.remove_prefix(sizeof(nonce));
-
- unsigned char plaintext_stack[128];
- scoped_ptr<unsigned char[]> plaintext_heap;
- unsigned char* plaintext;
- if (token.size() <= sizeof(plaintext_stack)) {
- plaintext = plaintext_stack;
- } else {
- plaintext_heap.reset(new unsigned char[token.size()]);
- plaintext = plaintext_heap.get();
- }
- size_t plaintext_length;
-
- if (!source_address_token_decrypter_->Decrypt(
- StringPiece(nonce, sizeof(nonce)), StringPiece(), token,
- plaintext, &plaintext_length)) {
- return false;
- }
-
- SourceAddressToken source_address_token;
- if (!source_address_token.ParseFromArray(plaintext, plaintext_length)) {
- return false;
- }
-
- if (source_address_token.ip() != ip.ToString()) {
- // It's for a different IP address.
- return false;
- }
-
- const QuicTime::Delta delta(now_since_epoch.Subtract(
- QuicTime::Delta::FromSeconds(source_address_token.timestamp())));
- const int64 delta_secs = delta.ToSeconds();
-
- // TODO(agl): consider whether and how these magic values should be moved to
- // a config.
- if (delta_secs < -3600) {
- // We only allow timestamps to be from an hour in the future.
- return false;
- }
-
- if (delta_secs > 86400) {
- // We allow one day into the past.
- return false;
- }
-
- return true;
-}
-
-QuicCryptoServerConfig::Config::Config() {
-}
-
-QuicCryptoServerConfig::Config::~Config() {
- STLDeleteElements(&key_exchanges);
-}
-
} // namespace net
diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h
index c597b74..0be50d0 100644
--- a/net/quic/crypto/crypto_handshake.h
+++ b/net/quic/crypto/crypto_handshake.h
@@ -11,8 +11,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
-#include "base/synchronization/lock.h"
-#include "net/base/ip_endpoint.h"
#include "net/base/net_export.h"
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_time.h"
@@ -24,12 +22,6 @@ class QuicClock;
class QuicDecrypter;
class QuicEncrypter;
class QuicRandom;
-class QuicServerConfigProtobuf;
-class StrikeRegister;
-
-namespace test {
-class QuicCryptoServerConfigPeer;
-} // namespace test
// An intermediate format of a handshake message that's convenient for a
// CryptoFramer to serialize from or parse into.
@@ -126,90 +118,6 @@ class NET_EXPORT_PRIVATE CryptoHandshakeMessage {
mutable scoped_ptr<QuicData> serialized_;
};
-// TODO(rch): sync with server more rationally
-class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
- public:
- class NET_EXPORT_PRIVATE PrivateKey {
- public:
- CryptoTag tag() const {
- return tag_;
- }
- void set_tag(CryptoTag tag) {
- tag_ = tag;
- }
- std::string private_key() const {
- return private_key_;
- }
- void set_private_key(std::string key) {
- private_key_ = key;
- }
-
- private:
- CryptoTag tag_;
- std::string private_key_;
- };
-
- QuicServerConfigProtobuf();
- ~QuicServerConfigProtobuf();
-
- size_t key_size() const {
- return keys_.size();
- }
-
- const PrivateKey& key(size_t i) const {
- DCHECK_GT(keys_.size(), i);
- return *keys_[i];
- }
-
- std::string config() const {
- return config_;
- }
-
- void set_config(base::StringPiece config) {
- config_ = config.as_string();
- }
-
- QuicServerConfigProtobuf::PrivateKey* add_key() {
- keys_.push_back(new PrivateKey);
- return keys_.back();
- }
-
- private:
- std::vector<PrivateKey*> keys_;
- std::string config_;
-};
-
-// TODO(rtenneti): sync with server more rationally.
-class NET_EXPORT_PRIVATE SourceAddressToken {
- public:
- SourceAddressToken();
- ~SourceAddressToken();
-
- std::string SerializeAsString() const;
-
- bool ParseFromArray(unsigned char* plaintext, size_t plaintext_length);
-
- std::string ip() const {
- return ip_;
- }
-
- int64 timestamp() const {
- return timestamp_;
- }
-
- void set_ip(base::StringPiece ip) {
- ip_ = ip.as_string();
- }
-
- void set_timestamp(int64 timestamp) {
- timestamp_ = timestamp;
- }
-
- private:
- std::string ip_;
- int64 timestamp_;
-};
-
// Parameters negotiated by the crypto handshake.
struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters {
// Initializes the members to 0 or empty values.
@@ -229,6 +137,16 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters {
// QuicCryptoConfig contains common configuration between clients and servers.
class NET_EXPORT_PRIVATE QuicCryptoConfig {
public:
+ enum {
+ // CONFIG_VERSION is the one (and, for the moment, only) version number that
+ // we implement.
+ CONFIG_VERSION = 0,
+ };
+
+ // kLabel is constant that is used in key derivation to tie the resulting key
+ // to this protocol.
+ static const char kLabel[];
+
QuicCryptoConfig();
~QuicCryptoConfig();
@@ -344,128 +262,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
std::map<std::string, CachedState*> cached_states_;
};
-// QuicCryptoServerConfig contains the crypto configuration of a QUIC server.
-// Unlike a client, a QUIC server can have multiple configurations active in
-// order to support clients resuming with a previous configuration.
-// TODO(agl): when adding configurations at runtime is added, this object will
-// need to consider locking.
-class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
- public:
- // |source_address_token_secret|: secret key material used for encrypting and
- // decrypting source address tokens. It can be of any length as it is fed
- // into a KDF before use. In tests, use TESTING.
- explicit QuicCryptoServerConfig(
- base::StringPiece source_address_token_secret);
- ~QuicCryptoServerConfig();
-
- // TESTING is a magic parameter for passing to the constructor in tests.
- static const char TESTING[];
-
- // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable
- // for using in tests. |extra_tags| contains additional key/value pairs that
- // will be inserted into the config.
- static QuicServerConfigProtobuf* DefaultConfig(
- QuicRandom* rand,
- const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags);
-
- // AddConfig adds a QuicServerConfigProtobuf to the availible configurations.
- // It returns the SCFG message from the config if successful. The caller
- // takes ownership of the CryptoHandshakeMessage.
- CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf);
-
- // AddDefaultConfig creates a config and then calls AddConfig to
- // add it. Any tags in |extra_tags| will be copied into the config.
- CryptoHandshakeMessage* AddDefaultConfig(
- QuicRandom* rand,
- const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags);
-
- // ProcessClientHello processes |client_hello| and decides whether to accept
- // or reject the connection. If the connection is to be accepted, |out| is
- // set to the contents of the ServerHello, |out_params| is completed and
- // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ message and
- // an error code is returned.
- //
- // client_hello: the incoming client hello message.
- // guid: the GUID for the connection, which is used in key derivation.
- // client_ip: the IP address of the client, which is used to generate and
- // validate source-address tokens.
- // now_since_epoch: the current time, as a delta since the unix epoch,
- // which is used to validate client nonces.
- // rand: an entropy source
- // params: the state of the handshake. This may be updated with a server
- // nonce when we send a rejection. After a successful handshake, this will
- // contain the state of the connection.
- // out: the resulting handshake message (either REJ or SHLO)
- // error_details: used to store a string describing any error.
- QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- QuicGuid guid,
- const IPEndPoint& client_ip,
- QuicTime::Delta now_since_epoch,
- QuicRandom* rand,
- QuicCryptoNegotiatedParameters* params,
- CryptoHandshakeMessage* out,
- std::string* error_details) const;
-
- private:
- friend class test::QuicCryptoServerConfigPeer;
-
- // Config represents a server config: a collection of preferences and
- // Diffie-Hellman public values.
- struct Config : public QuicCryptoConfig {
- Config();
- ~Config();
-
- // serialized contains the bytes of this server config, suitable for sending
- // on the wire.
- std::string serialized;
- // id contains the SCID of this server config.
- std::string id;
- // orbit contains the orbit value for this config: an opaque identifier
- // used to identify clusters of server frontends.
- unsigned char orbit[kOrbitSize];
-
- // key_exchanges contains key exchange objects with the private keys
- // already loaded. The values correspond, one-to-one, with the tags in
- // |kexs| from the parent class.
- std::vector<KeyExchange*> key_exchanges;
-
- // tag_value_map contains the raw key/value pairs for the config.
- CryptoTagValueMap tag_value_map;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Config);
- };
-
- // NewSourceAddressToken returns a fresh source address token for the given
- // IP address.
- std::string NewSourceAddressToken(const IPEndPoint& ip,
- QuicRandom* rand,
- QuicTime::Delta now_since_epoch) const;
-
- // ValidateSourceAddressToken returns true if the source address token in
- // |token| is a valid and timely token for the IP address |ip| given that the
- // current time is |now|.
- bool ValidateSourceAddressToken(base::StringPiece token,
- const IPEndPoint& ip,
- QuicTime::Delta now_since_epoch) const;
-
- std::map<ServerConfigID, Config*> configs_;
-
- ServerConfigID active_config_;
-
- mutable base::Lock strike_register_lock_;
- // strike_register_ contains a data structure that keeps track of previously
- // observed client nonces in order to prevent replay attacks.
- mutable scoped_ptr<StrikeRegister> strike_register_;
-
- // These members are used to encrypt and decrypt the source address tokens
- // that we receive from and send to clients.
- scoped_ptr<QuicEncrypter> source_address_token_encrypter_;
- scoped_ptr<QuicDecrypter> source_address_token_decrypter_;
-};
-
} // namespace net
#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc
index 154e364..7ff0b1f 100644
--- a/net/quic/crypto/crypto_handshake_test.cc
+++ b/net/quic/crypto/crypto_handshake_test.cc
@@ -5,6 +5,7 @@
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/aes_128_gcm_encrypter.h"
+#include "net/quic/crypto/crypto_server_config.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_time.h"
#include "net/quic/test_tools/mock_clock.h"
diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h
index f24dcbe..dd684400 100644
--- a/net/quic/crypto/crypto_protocol.h
+++ b/net/quic/crypto/crypto_protocol.h
@@ -40,6 +40,9 @@ const CryptoTag kAESG = MAKE_TAG('A', 'E', 'S', 'G'); // AES128 + GCM
const CryptoTag kQBIC = MAKE_TAG('Q', 'B', 'I', 'C'); // TCP cubic
const CryptoTag kINAR = MAKE_TAG('I', 'N', 'A', 'R'); // Inter arrival
+// Proof types (i.e. certificate types)
+const CryptoTag kX509 = MAKE_TAG('X', '5', '0', '9'); // X.509 certificate
+
// Client hello tags
const CryptoTag kVERS = MAKE_TAG('V', 'E', 'R', 'S'); // Version
const CryptoTag kNONC = MAKE_TAG('N', 'O', 'N', 'C'); // The connection nonce
@@ -58,6 +61,9 @@ const CryptoTag kPUBS = MAKE_TAG('P', 'U', 'B', 'S'); // Public key values
const CryptoTag kSCID = MAKE_TAG('S', 'C', 'I', 'D'); // Server config id
const CryptoTag kSRCT = MAKE_TAG('S', 'R', 'C', 'T'); // Source-address token
const CryptoTag kORBT = MAKE_TAG('O', 'B', 'I', 'T'); // Server orbit.
+const CryptoTag kPDMD = MAKE_TAG('P', 'D', 'M', 'D'); // Proof demand.
+const CryptoTag kCERT = MAKE_TAG('C', 'E', 'R', 'T'); // Certificate chain
+const CryptoTag kPROF = MAKE_TAG('P', 'R', 'O', 'F'); // Proof (signature).
const size_t kMaxEntries = 16; // Max number of entries in a message.
diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc
new file mode 100644
index 0000000..10dcf73
--- /dev/null
+++ b/net/quic/crypto/crypto_server_config.cc
@@ -0,0 +1,586 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_server_config.h"
+
+#include "base/stl_util.h"
+#include "crypto/hkdf.h"
+#include "crypto/secure_hash.h"
+#include "net/quic/crypto/aes_128_gcm_decrypter.h"
+#include "net/quic/crypto/aes_128_gcm_encrypter.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_server_config_protobuf.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/curve25519_key_exchange.h"
+#include "net/quic/crypto/key_exchange.h"
+#include "net/quic/crypto/p256_key_exchange.h"
+#include "net/quic/crypto/proof_source.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/source_address_token.h"
+#include "net/quic/crypto/strike_register.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+
+using base::StringPiece;
+using crypto::SecureHash;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace net {
+
+// static
+const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
+
+QuicCryptoServerConfig::QuicCryptoServerConfig(
+ StringPiece source_address_token_secret)
+ // AES-GCM is used to encrypt and authenticate source address tokens. The
+ // full, 96-bit nonce is used but we must ensure that an attacker cannot
+ // obtain two source address tokens with the same nonce. This occurs with
+ // probability 0.5 after 2**48 values. We assume that obtaining 2**48
+ // source address tokens is not possible: at a rate of 10M packets per
+ // second, it would still take the attacker a year to obtain the needed
+ // number of packets.
+ //
+ // TODO(agl): switch to an encrypter with a larger nonce space (i.e.
+ // Salsa20+Poly1305).
+ : strike_register_lock_(),
+ source_address_token_encrypter_(new Aes128GcmEncrypter),
+ source_address_token_decrypter_(new Aes128GcmDecrypter) {
+ crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */,
+ "QUIC source address token key",
+ source_address_token_encrypter_->GetKeySize(),
+ 0 /* no fixed IV needed */);
+ source_address_token_encrypter_->SetKey(hkdf.server_write_key());
+ source_address_token_decrypter_->SetKey(hkdf.server_write_key());
+}
+
+QuicCryptoServerConfig::~QuicCryptoServerConfig() {
+ STLDeleteValues(&configs_);
+}
+
+// static
+QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const CryptoHandshakeMessage& extra_tags) {
+ CryptoHandshakeMessage msg;
+
+ const string curve25519_private_key =
+ Curve25519KeyExchange::NewPrivateKey(rand);
+ scoped_ptr<Curve25519KeyExchange> curve25519(
+ Curve25519KeyExchange::New(curve25519_private_key));
+ StringPiece curve25519_public_value = curve25519->public_value();
+
+ const string p256_private_key =
+ P256KeyExchange::NewPrivateKey();
+ scoped_ptr<P256KeyExchange> p256(
+ P256KeyExchange::New(p256_private_key));
+ StringPiece p256_public_value = p256->public_value();
+
+ string encoded_public_values;
+ // First two bytes encode the length of the public value.
+ encoded_public_values.push_back(curve25519_public_value.size());
+ encoded_public_values.push_back(curve25519_public_value.size() >> 8);
+ encoded_public_values.append(curve25519_public_value.data(),
+ curve25519_public_value.size());
+ encoded_public_values.push_back(p256_public_value.size());
+ encoded_public_values.push_back(p256_public_value.size() >> 8);
+ encoded_public_values.append(p256_public_value.data(),
+ p256_public_value.size());
+
+ msg.set_tag(kSCFG);
+ msg.SetTaglist(kKEXS, kC255, kP256, 0);
+ msg.SetTaglist(kAEAD, kAESG, 0);
+ msg.SetValue(kVERS, static_cast<uint16>(0));
+ msg.SetStringPiece(kPUBS, encoded_public_values);
+ msg.Insert(extra_tags.tag_value_map().begin(),
+ extra_tags.tag_value_map().end());
+
+ char scid_bytes[16];
+ rand->RandBytes(scid_bytes, sizeof(scid_bytes));
+ msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes)));
+
+ char orbit_bytes[kOrbitSize];
+ rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
+ msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes)));
+
+ scoped_ptr<QuicData> serialized(
+ CryptoFramer::ConstructHandshakeMessage(msg));
+
+ scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf);
+ config->set_config(serialized->AsStringPiece());
+ QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key();
+ curve25519_key->set_tag(kC255);
+ curve25519_key->set_private_key(curve25519_private_key);
+ QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key();
+ p256_key->set_tag(kP256);
+ p256_key->set_private_key(p256_private_key);
+
+ return config.release();
+}
+
+CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
+ QuicServerConfigProtobuf* protobuf) {
+ scoped_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(protobuf->config()));
+
+ if (!msg.get()) {
+ LOG(WARNING) << "Failed to parse server config message";
+ return NULL;
+ }
+ if (msg->tag() != kSCFG) {
+ LOG(WARNING) << "Server config message has tag "
+ << msg->tag() << " expected "
+ << kSCFG;
+ return NULL;
+ }
+
+ scoped_ptr<Config> config(new Config);
+ config->serialized = protobuf->config();
+
+ StringPiece scid;
+ if (!msg->GetStringPiece(kSCID, &scid)) {
+ LOG(WARNING) << "Server config message is missing SCID";
+ return NULL;
+ }
+ config->id = scid.as_string();
+
+ const CryptoTag* aead_tags;
+ size_t aead_len;
+ if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) {
+ LOG(WARNING) << "Server config message is missing AEAD";
+ return NULL;
+ }
+ config->aead = vector<CryptoTag>(aead_tags, aead_tags + aead_len);
+
+ const CryptoTag* kexs_tags;
+ size_t kexs_len;
+ if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) {
+ LOG(WARNING) << "Server config message is missing KEXS";
+ return NULL;
+ }
+
+ StringPiece orbit;
+ if (!msg->GetStringPiece(kORBT, &orbit)) {
+ LOG(WARNING) << "Server config message is missing OBIT";
+ return NULL;
+ }
+
+ if (orbit.size() != kOrbitSize) {
+ LOG(WARNING) << "Orbit value in server config is the wrong length."
+ " Got " << orbit.size() << " want " << kOrbitSize;
+ return NULL;
+ }
+ COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size);
+ memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
+
+ if (kexs_len != protobuf->key_size()) {
+ LOG(WARNING) << "Server config has "
+ << kexs_len
+ << " key exchange methods configured, but "
+ << protobuf->key_size()
+ << " private keys";
+ return NULL;
+ }
+
+ for (size_t i = 0; i < kexs_len; i++) {
+ const CryptoTag tag = kexs_tags[i];
+ string private_key;
+
+ config->kexs.push_back(tag);
+
+ for (size_t j = 0; j < protobuf->key_size(); j++) {
+ const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i);
+ if (key.tag() == tag) {
+ private_key = key.private_key();
+ break;
+ }
+ }
+
+ if (private_key.empty()) {
+ LOG(WARNING) << "Server config contains key exchange method without "
+ "corresponding private key: "
+ << tag;
+ return NULL;
+ }
+
+ scoped_ptr<KeyExchange> ka;
+ switch (tag) {
+ case kC255:
+ ka.reset(Curve25519KeyExchange::New(private_key));
+ if (!ka.get()) {
+ LOG(WARNING) << "Server config contained an invalid curve25519"
+ " private key.";
+ return NULL;
+ }
+ break;
+ case kP256:
+ ka.reset(P256KeyExchange::New(private_key));
+ if (!ka.get()) {
+ LOG(WARNING) << "Server config contained an invalid P-256"
+ " private key.";
+ return NULL;
+ }
+ break;
+ default:
+ LOG(WARNING) << "Server config message contains unknown key exchange "
+ "method: "
+ << tag;
+ return NULL;
+ }
+
+ for (vector<KeyExchange*>::const_iterator i = config->key_exchanges.begin();
+ i != config->key_exchanges.end(); ++i) {
+ if ((*i)->tag() == tag) {
+ LOG(WARNING) << "Duplicate key exchange in config: " << tag;
+ return NULL;
+ }
+ }
+
+ config->key_exchanges.push_back(ka.release());
+ }
+
+ if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) {
+ LOG(WARNING) << "Server config message is missing version";
+ return NULL;
+ }
+
+ if (config->version != QuicCryptoConfig::CONFIG_VERSION) {
+ LOG(WARNING) << "Server config specifies an unsupported version";
+ return NULL;
+ }
+
+ scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
+ sha256->Update(protobuf->config().data(), protobuf->config().size());
+ char id_bytes[16];
+ sha256->Finish(id_bytes, sizeof(id_bytes));
+ const string id(id_bytes, sizeof(id_bytes));
+
+ configs_[id] = config.release();
+ active_config_ = id;
+
+ return msg.release();
+}
+
+CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const CryptoHandshakeMessage& extra_tags) {
+ scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig(
+ rand, clock, extra_tags));
+ return AddConfig(config.get());
+}
+
+QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ QuicGuid guid,
+ const IPEndPoint& client_ip,
+ QuicTime::Delta now_since_unix_epoch,
+ QuicRandom* rand,
+ QuicCryptoNegotiatedParameters *params,
+ CryptoHandshakeMessage* out,
+ string* error_details) const {
+ DCHECK(error_details);
+
+ CHECK(!configs_.empty());
+
+ // FIXME(agl): we should use the client's SCID, not just the active config.
+ map<ServerConfigID, Config*>::const_iterator it =
+ configs_.find(active_config_);
+ if (it == configs_.end()) {
+ *error_details = "No valid server config loaded";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+ const Config* const config(it->second);
+
+ bool valid_source_address_token = false;
+ StringPiece srct;
+ if (client_hello.GetStringPiece(kSRCT, &srct) &&
+ ValidateSourceAddressToken(srct, client_ip, now_since_unix_epoch)) {
+ valid_source_address_token = true;
+ }
+
+ const string fresh_source_address_token =
+ NewSourceAddressToken(client_ip, rand, now_since_unix_epoch);
+
+ // If we previously sent a REJ to this client then we may have stored a
+ // server nonce in |params|. In which case, we know that the connection
+ // is unique because the server nonce will be mixed into the key generation.
+ bool unique_by_server_nonce = !params->server_nonce.empty();
+ // If we can't ensure uniqueness by a server nonce, then we will try and use
+ // the strike register.
+ bool unique_by_strike_register = false;
+
+ StringPiece client_nonce;
+ bool client_nonce_well_formed = false;
+ if (client_hello.GetStringPiece(kNONC, &client_nonce) &&
+ client_nonce.size() == kNonceSize) {
+ client_nonce_well_formed = true;
+ base::AutoLock auto_lock(strike_register_lock_);
+
+ if (strike_register_.get() == NULL) {
+ strike_register_.reset(new StrikeRegister(
+ // TODO(agl): these magic numbers should come from config.
+ 1024 /* max entries */,
+ static_cast<uint32>(now_since_unix_epoch.ToSeconds()),
+ 600 /* window secs */, config->orbit));
+ }
+ unique_by_strike_register = strike_register_->Insert(
+ reinterpret_cast<const uint8*>(client_nonce.data()),
+ static_cast<uint32>(now_since_unix_epoch.ToSeconds()));
+ }
+
+ out->Clear();
+
+ StringPiece sni;
+ client_hello.GetStringPiece(kSNI, &sni);
+
+ StringPiece scid;
+ if (!client_hello.GetStringPiece(kSCID, &scid) ||
+ scid.as_string() != config->id ||
+ !valid_source_address_token ||
+ !client_nonce_well_formed ||
+ (!unique_by_strike_register &&
+ !unique_by_server_nonce)) {
+ // If the client didn't provide a server config ID, or gave the wrong one,
+ // then the handshake cannot possibly complete. We reject the handshake and
+ // give the client enough information to do better next time.
+ out->set_tag(kREJ);
+ out->SetStringPiece(kSCFG, config->serialized);
+ out->SetStringPiece(kSRCT, fresh_source_address_token);
+ if (params->server_nonce.empty()) {
+ CryptoUtils::GenerateNonce(
+ now_since_unix_epoch, rand,
+ StringPiece(reinterpret_cast<const char*>(config->orbit),
+ sizeof(config->orbit)),
+ &params->server_nonce);
+ }
+ out->SetStringPiece(kNONC, params->server_nonce);
+
+ // The client may have requested a certificate chain.
+ const CryptoTag* their_proof_demands;
+ size_t num_their_proof_demands;
+
+ if (valid_source_address_token &&
+ proof_source_.get() != NULL &&
+ !sni.empty() &&
+ client_hello.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] != kX509) {
+ continue;
+ }
+
+ // TODO(agl): in the future we will hopefully have a cached-info like
+ // mechanism where we can omit certificates that the client already has.
+ // In that case, the certificate chain may be small enough to include
+ // without a source-address token. But, for now, we always send the full
+ // chain and we always need a valid source-address token.
+
+ const vector<string>* certs;
+ string signature;
+ if (!proof_source_->GetProof(sni.as_string(), config->serialized,
+ &certs, &signature)) {
+ break;
+ }
+
+ // TODO(agl): compress and omit certificates where possible based on
+ // the client's cached certificates.
+ size_t cert_bytes = 0;
+ for (vector<string>::const_iterator i = certs->begin();
+ i != certs->end(); ++i) {
+ cert_bytes += i->size();
+ }
+ // There's a three byte length-prefix for each certificate.
+ cert_bytes += certs->size()*3;
+ scoped_ptr<char[]> buf(new char[cert_bytes]);
+
+ size_t j = 0;
+ for (vector<string>::const_iterator i = certs->begin();
+ i != certs->end(); ++i) {
+ size_t len = i->size();
+ buf[j++] = len;
+ buf[j++] = len >> 8;
+ buf[j++] = len >> 16;
+ memcpy(&buf[j], i->data(), i->size());
+ j += i->size();
+ }
+
+ DCHECK_EQ(j, cert_bytes);
+
+ out->SetStringPiece(kCERT, StringPiece(buf.get(), cert_bytes));
+ out->SetStringPiece(kPROF, signature);
+ break;
+ }
+ }
+
+ return QUIC_NO_ERROR;
+ }
+
+ const CryptoTag* their_aeads;
+ const CryptoTag* their_key_exchanges;
+ size_t num_their_aeads, num_their_key_exchanges;
+ if (client_hello.GetTaglist(kAEAD, &their_aeads,
+ &num_their_aeads) != QUIC_NO_ERROR ||
+ client_hello.GetTaglist(kKEXS, &their_key_exchanges,
+ &num_their_key_exchanges) != QUIC_NO_ERROR ||
+ num_their_aeads != 1 ||
+ num_their_key_exchanges != 1) {
+ *error_details = "Missing or invalid AEAD or KEXS";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ size_t key_exchange_index;
+ if (!CryptoUtils::FindMutualTag(config->aead,
+ their_aeads, num_their_aeads,
+ CryptoUtils::LOCAL_PRIORITY,
+ &params->aead,
+ NULL) ||
+ !CryptoUtils::FindMutualTag(config->kexs,
+ their_key_exchanges, num_their_key_exchanges,
+ CryptoUtils::LOCAL_PRIORITY,
+ &params->key_exchange,
+ &key_exchange_index)) {
+ *error_details = "Unsupported AEAD or KEXS";
+ return QUIC_CRYPTO_NO_SUPPORT;
+ }
+
+ StringPiece public_value;
+ if (!client_hello.GetStringPiece(kPUBS, &public_value)) {
+ *error_details = "Missing public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey(
+ public_value, &params->premaster_secret)) {
+ *error_details = "Invalid public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ params->server_config_id = scid.as_string();
+
+ string hkdf_input(QuicCryptoConfig::kLabel,
+ strlen(QuicCryptoConfig::kLabel) + 1);
+ hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+
+ const QuicData& client_hello_serialized = client_hello.GetSerialized();
+ hkdf_input.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ hkdf_input.append(config->serialized);
+
+ CryptoUtils::DeriveKeys(params, client_nonce, hkdf_input,
+ CryptoUtils::SERVER);
+
+ out->set_tag(kSHLO);
+ out->SetStringPiece(kSRCT, fresh_source_address_token);
+ return QUIC_NO_ERROR;
+}
+
+void QuicCryptoServerConfig::SetProofSource(ProofSource* proof_source) {
+ proof_source_.reset(proof_source);
+}
+
+string QuicCryptoServerConfig::NewSourceAddressToken(
+ const IPEndPoint& ip,
+ QuicRandom* rand,
+ QuicTime::Delta now_since_epoch) const {
+ SourceAddressToken source_address_token;
+ source_address_token.set_ip(ip.ToString());
+ source_address_token.set_timestamp(now_since_epoch.ToSeconds());
+
+ string plaintext = source_address_token.SerializeAsString();
+ char nonce[12];
+ DCHECK_EQ(sizeof(nonce),
+ source_address_token_encrypter_->GetNoncePrefixSize() +
+ sizeof(QuicPacketSequenceNumber));
+ rand->RandBytes(nonce, sizeof(nonce));
+
+ size_t ciphertext_size =
+ source_address_token_encrypter_->GetCiphertextSize(plaintext.size());
+ string result;
+ result.resize(sizeof(nonce) + ciphertext_size);
+ memcpy(&result[0], &nonce, sizeof(nonce));
+
+ if (!source_address_token_encrypter_->Encrypt(
+ StringPiece(nonce, sizeof(nonce)), StringPiece(), plaintext,
+ reinterpret_cast<unsigned char*>(&result[sizeof(nonce)]))) {
+ DCHECK(false);
+ return string();
+ }
+
+ return result;
+}
+
+bool QuicCryptoServerConfig::ValidateSourceAddressToken(
+ StringPiece token,
+ const IPEndPoint& ip,
+ QuicTime::Delta now_since_epoch) const {
+ char nonce[12];
+ DCHECK_EQ(sizeof(nonce),
+ source_address_token_encrypter_->GetNoncePrefixSize() +
+ sizeof(QuicPacketSequenceNumber));
+
+ if (token.size() <= sizeof(nonce)) {
+ return false;
+ }
+ memcpy(&nonce, token.data(), sizeof(nonce));
+ token.remove_prefix(sizeof(nonce));
+
+ unsigned char plaintext_stack[128];
+ scoped_ptr<unsigned char[]> plaintext_heap;
+ unsigned char* plaintext;
+ if (token.size() <= sizeof(plaintext_stack)) {
+ plaintext = plaintext_stack;
+ } else {
+ plaintext_heap.reset(new unsigned char[token.size()]);
+ plaintext = plaintext_heap.get();
+ }
+ size_t plaintext_length;
+
+ if (!source_address_token_decrypter_->Decrypt(
+ StringPiece(nonce, sizeof(nonce)), StringPiece(), token,
+ plaintext, &plaintext_length)) {
+ return false;
+ }
+
+ SourceAddressToken source_address_token;
+ if (!source_address_token.ParseFromArray(plaintext, plaintext_length)) {
+ return false;
+ }
+
+ if (source_address_token.ip() != ip.ToString()) {
+ // It's for a different IP address.
+ return false;
+ }
+
+ const QuicTime::Delta delta(now_since_epoch.Subtract(
+ QuicTime::Delta::FromSeconds(source_address_token.timestamp())));
+ const int64 delta_secs = delta.ToSeconds();
+
+ // TODO(agl): consider whether and how these magic values should be moved to
+ // a config.
+ if (delta_secs < -3600) {
+ // We only allow timestamps to be from an hour in the future.
+ return false;
+ }
+
+ if (delta_secs > 86400) {
+ // We allow one day into the past.
+ return false;
+ }
+
+ return true;
+}
+
+QuicCryptoServerConfig::Config::Config() {
+}
+
+QuicCryptoServerConfig::Config::~Config() {
+ STLDeleteElements(&key_exchanges);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/crypto_server_config.h
new file mode 100644
index 0000000..4986603
--- /dev/null
+++ b/net/quic/crypto/crypto_server_config.h
@@ -0,0 +1,164 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_
+#define NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class KeyExchange;
+class ProofSource;
+class QuicClock;
+class QuicDecrypter;
+class QuicEncrypter;
+class QuicRandom;
+class QuicServerConfigProtobuf;
+class StrikeRegister;
+
+namespace test {
+class QuicCryptoServerConfigPeer;
+} // namespace test
+
+// QuicCryptoServerConfig contains the crypto configuration of a QUIC server.
+// Unlike a client, a QUIC server can have multiple configurations active in
+// order to support clients resuming with a previous configuration.
+// TODO(agl): when adding configurations at runtime is added, this object will
+// need to consider locking.
+class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
+ public:
+ // |source_address_token_secret|: secret key material used for encrypting and
+ // decrypting source address tokens. It can be of any length as it is fed
+ // into a KDF before use. In tests, use TESTING.
+ explicit QuicCryptoServerConfig(
+ base::StringPiece source_address_token_secret);
+ ~QuicCryptoServerConfig();
+
+ // TESTING is a magic parameter for passing to the constructor in tests.
+ static const char TESTING[];
+
+ // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable
+ // for using in tests. |extra_tags| contains additional key/value pairs that
+ // will be inserted into the config.
+ static QuicServerConfigProtobuf* DefaultConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const CryptoHandshakeMessage& extra_tags);
+
+ // AddConfig adds a QuicServerConfigProtobuf to the availible configurations.
+ // It returns the SCFG message from the config if successful. The caller
+ // takes ownership of the CryptoHandshakeMessage.
+ CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf);
+
+ // AddDefaultConfig creates a config and then calls AddConfig to
+ // add it. Any tags in |extra_tags| will be copied into the config.
+ CryptoHandshakeMessage* AddDefaultConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const CryptoHandshakeMessage& extra_tags);
+
+ // ProcessClientHello processes |client_hello| and decides whether to accept
+ // or reject the connection. If the connection is to be accepted, |out| is
+ // set to the contents of the ServerHello, |out_params| is completed and
+ // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ message and
+ // an error code is returned.
+ //
+ // client_hello: the incoming client hello message.
+ // guid: the GUID for the connection, which is used in key derivation.
+ // client_ip: the IP address of the client, which is used to generate and
+ // validate source-address tokens.
+ // now_since_epoch: the current time, as a delta since the unix epoch,
+ // which is used to validate client nonces.
+ // rand: an entropy source
+ // params: the state of the handshake. This may be updated with a server
+ // nonce when we send a rejection. After a successful handshake, this will
+ // contain the state of the connection.
+ // out: the resulting handshake message (either REJ or SHLO)
+ // error_details: used to store a string describing any error.
+ QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
+ QuicGuid guid,
+ const IPEndPoint& client_ip,
+ QuicTime::Delta now_since_epoch,
+ QuicRandom* rand,
+ QuicCryptoNegotiatedParameters* params,
+ CryptoHandshakeMessage* out,
+ std::string* error_details) const;
+
+ // SetProofSource installs |proof_source| as the ProofSource for handshakes.
+ // This object takes ownership of |proof_source|.
+ void SetProofSource(ProofSource* proof_source);
+
+ private:
+ friend class test::QuicCryptoServerConfigPeer;
+
+ // Config represents a server config: a collection of preferences and
+ // Diffie-Hellman public values.
+ struct Config : public QuicCryptoConfig {
+ Config();
+ ~Config();
+
+ // serialized contains the bytes of this server config, suitable for sending
+ // on the wire.
+ std::string serialized;
+ // id contains the SCID of this server config.
+ std::string id;
+ // orbit contains the orbit value for this config: an opaque identifier
+ // used to identify clusters of server frontends.
+ unsigned char orbit[kOrbitSize];
+
+ // key_exchanges contains key exchange objects with the private keys
+ // already loaded. The values correspond, one-to-one, with the tags in
+ // |kexs| from the parent class.
+ std::vector<KeyExchange*> key_exchanges;
+
+ // tag_value_map contains the raw key/value pairs for the config.
+ CryptoTagValueMap tag_value_map;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Config);
+ };
+
+ // NewSourceAddressToken returns a fresh source address token for the given
+ // IP address.
+ std::string NewSourceAddressToken(const IPEndPoint& ip,
+ QuicRandom* rand,
+ QuicTime::Delta now_since_epoch) const;
+
+ // ValidateSourceAddressToken returns true if the source address token in
+ // |token| is a valid and timely token for the IP address |ip| given that the
+ // current time is |now|.
+ bool ValidateSourceAddressToken(base::StringPiece token,
+ const IPEndPoint& ip,
+ QuicTime::Delta now_since_epoch) const;
+
+ std::map<ServerConfigID, Config*> configs_;
+
+ ServerConfigID active_config_;
+
+ mutable base::Lock strike_register_lock_;
+ // strike_register_ contains a data structure that keeps track of previously
+ // observed client nonces in order to prevent replay attacks.
+ mutable scoped_ptr<StrikeRegister> strike_register_;
+
+ // These members are used to encrypt and decrypt the source address tokens
+ // that we receive from and send to clients.
+ scoped_ptr<QuicEncrypter> source_address_token_encrypter_;
+ scoped_ptr<QuicDecrypter> source_address_token_decrypter_;
+
+ // proof_source_ contains an object that can provide certificate chains and
+ // signatures.
+ scoped_ptr<ProofSource> proof_source_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_
diff --git a/net/quic/crypto/crypto_server_config_protobuf.cc b/net/quic/crypto/crypto_server_config_protobuf.cc
new file mode 100644
index 0000000..f2a1a2d
--- /dev/null
+++ b/net/quic/crypto/crypto_server_config_protobuf.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_server_config_protobuf.h"
+
+#include "base/stl_util.h"
+
+namespace net {
+
+QuicServerConfigProtobuf::QuicServerConfigProtobuf() {
+}
+
+QuicServerConfigProtobuf::~QuicServerConfigProtobuf() {
+ STLDeleteElements(&keys_);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_server_config_protobuf.h b/net/quic/crypto/crypto_server_config_protobuf.h
new file mode 100644
index 0000000..ee555ab
--- /dev/null
+++ b/net/quic/crypto/crypto_server_config_protobuf.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_
+#define NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/quic/crypto/crypto_protocol.h"
+
+namespace net {
+
+// TODO(rch): sync with server more rationally.
+class QuicServerConfigProtobuf {
+ public:
+ class PrivateKey {
+ public:
+ CryptoTag tag() const {
+ return tag_;
+ }
+ void set_tag(CryptoTag tag) {
+ tag_ = tag;
+ }
+ std::string private_key() const {
+ return private_key_;
+ }
+ void set_private_key(std::string key) {
+ private_key_ = key;
+ }
+
+ private:
+ CryptoTag tag_;
+ std::string private_key_;
+ };
+
+ QuicServerConfigProtobuf();
+ ~QuicServerConfigProtobuf();
+
+ size_t key_size() const {
+ return keys_.size();
+ }
+
+ const PrivateKey& key(size_t i) const {
+ DCHECK_GT(keys_.size(), i);
+ return *keys_[i];
+ }
+
+ std::string config() const {
+ return config_;
+ }
+
+ void set_config(base::StringPiece config) {
+ config_ = config.as_string();
+ }
+
+ QuicServerConfigProtobuf::PrivateKey* add_key() {
+ keys_.push_back(new PrivateKey);
+ return keys_.back();
+ }
+
+ private:
+ std::vector<PrivateKey*> keys_;
+ std::string config_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_
diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc
index 6e52898..e7a2376 100644
--- a/net/quic/crypto/crypto_utils.cc
+++ b/net/quic/crypto/crypto_utils.cc
@@ -72,7 +72,12 @@ void CryptoUtils::GenerateNonce(QuicTime::Delta now,
nonce->reserve(kNonceSize);
nonce->resize(kNonceSize);
uint32 gmt_unix_time = now.ToSeconds();
- memcpy(&(*nonce)[0], &gmt_unix_time, sizeof(gmt_unix_time));
+ // The time in the nonce must be encoded in big-endian because the
+ // strike-register depends on the nonces being ordered by time.
+ (*nonce)[0] = static_cast<char>(gmt_unix_time >> 24);
+ (*nonce)[1] = static_cast<char>(gmt_unix_time >> 16);
+ (*nonce)[2] = static_cast<char>(gmt_unix_time >> 8);
+ (*nonce)[3] = static_cast<char>(gmt_unix_time);
size_t bytes_written = sizeof(gmt_unix_time);
if (orbit.size() == 8) {
diff --git a/net/quic/crypto/proof_source.h b/net/quic/crypto/proof_source.h
new file mode 100644
index 0000000..75b2ba0
--- /dev/null
+++ b/net/quic/crypto/proof_source.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_PROOF_SOURCE_H_
+#define NET_QUIC_CRYPTO_PROOF_SOURCE_H_
+
+#include <string>
+#include <vector>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// ProofSource is an interface by which a QUIC server can obtain certificate
+// chains and signatures that prove its identity.
+class NET_EXPORT_PRIVATE ProofSource {
+ public:
+ virtual ~ProofSource() {}
+
+ // GetProof finds a certificate chain for |hostname|, sets |out_certs| to
+ // point to it (in leaf-first order), calculates a signature of
+ // |server_config| using that chain and puts the result in |out_signature|.
+ //
+ // The signature uses SHA-256 as the hash function and PSS padding when the
+ // key is RSA.
+ //
+ // |out_certs| is a pointer to a pointer, not a pointer to an array.
+ //
+ // The number of certificate chains is expected to be small and fixed thus
+ // the ProofSource retains ownership of the contents of |out_certs|. The
+ // expectation is that they will be cached forever.
+ //
+ // The signature values should be cached because |server_config| will be
+ // somewhat static. However, since they aren't bounded, the ProofSource may
+ // wish to evicit entries from that cache, thus the caller takes ownership of
+ // |*out_signature|.
+ //
+ // This function may be called concurrently.
+ virtual bool GetProof(const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>** out_certs,
+ std::string* out_signature) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_PROOF_SOURCE_H_
diff --git a/net/quic/crypto/source_address_token.cc b/net/quic/crypto/source_address_token.cc
new file mode 100644
index 0000000..d664a08
--- /dev/null
+++ b/net/quic/crypto/source_address_token.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/source_address_token.h"
+
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+
+using std::string;
+using std::vector;
+
+namespace net {
+
+SourceAddressToken::SourceAddressToken() {
+}
+
+SourceAddressToken::~SourceAddressToken() {
+}
+
+string SourceAddressToken::SerializeAsString() const {
+ return ip_ + " " + base::Int64ToString(timestamp_);
+}
+
+bool SourceAddressToken::ParseFromArray(unsigned char* plaintext,
+ size_t plaintext_length) {
+ string data(reinterpret_cast<const char*>(plaintext), plaintext_length);
+ vector<string> results;
+ base::SplitString(data, ' ', &results);
+ if (results.size() < 2) {
+ return false;
+ }
+
+ int64 timestamp;
+ if (!base::StringToInt64(results[1], &timestamp)) {
+ return false;
+ }
+
+ ip_ = results[0];
+ timestamp_ = timestamp;
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/source_address_token.h b/net/quic/crypto/source_address_token.h
new file mode 100644
index 0000000..5f8388b
--- /dev/null
+++ b/net/quic/crypto/source_address_token.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_
+#define NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+
+namespace net {
+
+// TODO(rtenneti): sync with server more rationally.
+class SourceAddressToken {
+ public:
+ SourceAddressToken();
+ ~SourceAddressToken();
+
+ std::string SerializeAsString() const;
+
+ bool ParseFromArray(unsigned char* plaintext, size_t plaintext_length);
+
+ std::string ip() const {
+ return ip_;
+ }
+
+ int64 timestamp() const {
+ return timestamp_;
+ }
+
+ void set_ip(base::StringPiece ip) {
+ ip_ = ip.as_string();
+ }
+
+ void set_timestamp(int64 timestamp) {
+ timestamp_ = timestamp;
+ }
+
+ private:
+ std::string ip_;
+ int64 timestamp_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_
diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc
index f16f06f..70e0b45 100644
--- a/net/quic/quic_config.cc
+++ b/net/quic/quic_config.cc
@@ -71,6 +71,8 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake(
CryptoUtils::Priority priority,
QuicNegotiatedParameters* out_params,
string* error_details) const {
+ DCHECK(error_details != NULL);
+
const CryptoTag* their_congestion_controls;
size_t num_their_congestion_controls;
QuicErrorCode error;
@@ -78,9 +80,7 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake(
error = msg.GetTaglist(kCGST, &their_congestion_controls,
&num_their_congestion_controls);
if (error != QUIC_NO_ERROR) {
- if (error_details) {
- *error_details = "Missing CGST";
- }
+ *error_details = "Missing CGST";
return error;
}
@@ -90,18 +90,14 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake(
priority,
&out_params->congestion_control,
NULL)) {
- if (error_details) {
- *error_details = "Unsuported CGST";
- }
+ *error_details = "Unsuported CGST";
return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP;
}
uint32 idle;
error = msg.GetUint32(kICSL, &idle);
if (error != QUIC_NO_ERROR) {
- if (error_details) {
- *error_details = "Missing ICSL";
- }
+ *error_details = "Missing ICSL";
return error;
}
@@ -122,9 +118,7 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake(
out_params->keepalive_timeout = QuicTime::Delta::Zero();
break;
default:
- if (error_details) {
- *error_details = "Bad KATO";
- }
+ *error_details = "Bad KATO";
return error;
}
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index efb3e96..cb3033b 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -180,6 +180,7 @@ bool QuicConnection::OnProtocolVersionMismatch(
LOG(DFATAL) << "Framer called OnProtocolVersionMismatch for server. "
<< "Closing connection.";
CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return false;
}
DCHECK_NE(quic_version_, received_version);
@@ -229,6 +230,7 @@ void QuicConnection::OnVersionNegotiationPacket(
LOG(DFATAL) << "Framer parsed VersionNegotiationPacket for server."
<< "Closing connection.";
CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return;
}
if (debug_visitor_) {
debug_visitor_->OnVersionNegotiationPacket(packet);
diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc
index aa43b6d..ffef124 100644
--- a/net/quic/quic_crypto_client_stream.cc
+++ b/net/quic/quic_crypto_client_stream.cc
@@ -49,6 +49,10 @@ QuicCryptoClientStream::crypto_negotiated_params() const {
return crypto_negotiated_params_;
}
+int QuicCryptoClientStream::num_sent_client_hellos() const {
+ return num_client_hellos_;
+}
+
// kMaxClientHellos is the maximum number of times that we'll send a client
// hello. The value 3 accounts for:
// * One failure due to an incorrect or missing source-address token.
diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h
index 2ad122c..3381c8a 100644
--- a/net/quic/quic_crypto_client_stream.h
+++ b/net/quic/quic_crypto_client_stream.h
@@ -40,6 +40,11 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
const QuicNegotiatedParameters& negotiated_params() const;
const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const;
+ // num_sent_client_hellos returns the number of client hello messages that
+ // have been sent. If the handshake has completed then this is one greater
+ // than the number of round-trips needed for the handshake.
+ int num_sent_client_hellos() const;
+
private:
friend class test::CryptoTestUtils;
diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc
index a0104e4..5fa33e1 100644
--- a/net/quic/quic_crypto_server_stream.cc
+++ b/net/quic/quic_crypto_server_stream.cc
@@ -5,6 +5,7 @@
#include "net/quic/quic_crypto_server_stream.h"
#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_server_config.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_protocol.h"
diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc
index b567f85..c9b779c 100644
--- a/net/quic/quic_crypto_server_stream_test.cc
+++ b/net/quic/quic_crypto_server_stream_test.cc
@@ -12,9 +12,12 @@
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_server_config.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_crypto_server_stream.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_session.h"
#include "net/quic/test_tools/crypto_test_utils.h"
@@ -73,6 +76,12 @@ class QuicCryptoServerStreamTest : public ::testing::Test {
session_(connection_, true),
crypto_config_(QuicCryptoServerConfig::TESTING),
stream_(config_, crypto_config_, &session_) {
+ // We advance the clock initially because the default time is zero and the
+ // strike register worries that we've just overflowed a uint32 time.
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(100000));
+ // TODO(rtenneti): Enable testing of ProofSource.
+ // crypto_config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting());
+
CryptoTestUtils::SetupCryptoServerConfigForTest(
connection_->clock(), connection_->random_generator(), &config_,
&crypto_config_);
@@ -83,8 +92,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test {
message_data_.reset(framer.ConstructHandshakeMessage(message_));
}
- void CompleteCryptoHandshake() {
- CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_);
+ int CompleteCryptoHandshake() {
+ return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_);
}
protected:
@@ -115,10 +124,81 @@ TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) {
return;
}
- CompleteCryptoHandshake();
+ EXPECT_EQ(2, CompleteCryptoHandshake());
EXPECT_TRUE(stream_.handshake_complete());
}
+TEST_F(QuicCryptoServerStreamTest, ZeroRTT) {
+ if (!Aes128GcmEncrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ QuicGuid guid(1);
+ IPAddressNumber ip;
+ ParseIPLiteralToNumber("127.0.0.1", &ip);
+ IPEndPoint addr(ip, 0);
+ PacketSavingConnection* client_conn =
+ new PacketSavingConnection(guid, addr, false);
+ PacketSavingConnection* server_conn =
+ new PacketSavingConnection(guid, addr, false);
+ client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
+ server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
+
+ scoped_ptr<TestSession> client_session(new TestSession(client_conn, true));
+ scoped_ptr<TestSession> server_session(new TestSession(server_conn, true));
+
+ QuicConfig client_config;
+ QuicCryptoClientConfig client_crypto_config;
+
+ client_config.SetDefaults();
+ client_crypto_config.SetDefaults();
+
+ scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream(
+ "test.example.com", client_config, client_session.get(),
+ &client_crypto_config));
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ CHECK(client->CryptoConnect());
+ CHECK_EQ(1u, client_conn->packets_.size());
+
+ scoped_ptr<QuicCryptoServerStream> server(
+ new QuicCryptoServerStream(config_, crypto_config_,
+ server_session.get()));
+
+ CryptoTestUtils::CommunicateHandshakeMessages(
+ client_conn, client.get(), server_conn, server.get());
+ EXPECT_EQ(2, client->num_sent_client_hellos());
+
+ // Now do another handshake, hopefully in 0-RTT.
+ LOG(INFO) << "Resetting for 0-RTT handshake attempt";
+
+ client_conn = new PacketSavingConnection(guid, addr, false);
+ server_conn = new PacketSavingConnection(guid, addr, false);
+ // We need to advance time past the strike-server window so that it's
+ // authoritative in this time span.
+ client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1002000));
+ server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1002000));
+
+ // This causes the client's nonce to be different and thus stops the
+ // strike-register from rejecting the repeated nonce.
+ client_conn->random_generator()->Reseed(NULL, 0);
+ client_session.reset(new TestSession(client_conn, true));
+ server_session.reset(new TestSession(server_conn, true));
+ client.reset(new QuicCryptoClientStream(
+ "test.example.com", client_config, client_session.get(),
+ &client_crypto_config));
+ server.reset(new QuicCryptoServerStream(config_, crypto_config_,
+ server_session.get()));
+
+ CHECK(client->CryptoConnect());
+
+ CryptoTestUtils::CommunicateHandshakeMessages(
+ client_conn, client.get(), server_conn, server.get());
+ EXPECT_EQ(1, client->num_sent_client_hellos());
+}
+
TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) {
if (!Aes128GcmEncrypter::IsSupported()) {
LOG(INFO) << "AES GCM not supported. Test skipped.";
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index b3f11cb..d373203 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -216,7 +216,7 @@ enum QuicErrorCode {
// An internal error occured in crypto processing.
QUIC_CRYPTO_INTERNAL_ERROR,
// A crypto handshake message specified an unsupported version.
- QUIC_VERSION_NOT_SUPPORTED,
+ QUIC_CRYPTO_VERSION_NOT_SUPPORTED,
// There was no intersection between the crypto primitives supported by the
// peer and ourselves.
QUIC_CRYPTO_NO_SUPPORT,
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc
index ac46977..aa2019b 100644
--- a/net/quic/quic_utils.cc
+++ b/net/quic/quic_utils.cc
@@ -77,7 +77,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH)
RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE);
RETURN_STRING_LITERAL(QUIC_CRYPTO_INTERNAL_ERROR);
- RETURN_STRING_LITERAL(QUIC_VERSION_NOT_SUPPORTED);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_VERSION_NOT_SUPPORTED);
RETURN_STRING_LITERAL(QUIC_CRYPTO_NO_SUPPORT);
RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE);
RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER);
diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc
index c2d5988..4c59d1f 100644
--- a/net/quic/test_tools/crypto_test_utils.cc
+++ b/net/quic/test_tools/crypto_test_utils.cc
@@ -6,6 +6,7 @@
#include "base/strings/string_piece.h"
#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_server_config.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/quic_random.h"
@@ -18,6 +19,7 @@
using base::StringPiece;
using std::string;
+using std::vector;
namespace net {
namespace test {
@@ -36,39 +38,95 @@ class TestSession : public QuicSession {
MOCK_METHOD0(CreateOutgoingReliableStream, ReliableQuicStream*());
};
-// CommunicateHandshakeMessages moves messages from |a| to |b| and back until
-// |a|'s handshake has completed.
-void CommunicateHandshakeMessages(
- PacketSavingConnection* a_conn,
- QuicCryptoStream* a,
- PacketSavingConnection* b_conn,
- QuicCryptoStream* b) {
- scoped_ptr<SimpleQuicFramer> framer;
+// CryptoFramerVisitor is a framer visitor that records handshake messages.
+class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
+ public:
+ CryptoFramerVisitor()
+ : error_(false) {
+ }
+
+ void OnError(CryptoFramer* framer) {
+ error_ = true;
+ }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) {
+ messages_.push_back(message);
+ }
+
+ bool error() const {
+ return error_;
+ }
+
+ const vector<CryptoHandshakeMessage>& messages() const {
+ return messages_;
+ }
- for (size_t i = 0; !a->handshake_complete(); i++) {
- framer.reset(new SimpleQuicFramer);
+ private:
+ bool error_;
+ vector<CryptoHandshakeMessage> messages_;
+};
+
+// MovePackets parses crypto handshake messages from packet number
+// |*inout_packet_index| through to the last packet and has |dest_stream|
+// process them. |*inout_packet_index| is updated with an index one greater
+// than the last packet processed.
+void MovePackets(PacketSavingConnection* source_conn,
+ size_t *inout_packet_index,
+ QuicCryptoStream* dest_stream) {
+ SimpleQuicFramer framer;
+ CryptoFramer crypto_framer;
+ CryptoFramerVisitor crypto_visitor;
- ASSERT_LT(i, a_conn->packets_.size());
- ASSERT_TRUE(framer->ProcessPacket(*a_conn->packets_[i]));
- ASSERT_EQ(1u, framer->stream_frames().size());
+ crypto_framer.set_visitor(&crypto_visitor);
- scoped_ptr<CryptoHandshakeMessage> a_msg(framer->HandshakeMessage(0));
- b->OnHandshakeMessage(*(a_msg.get()));
+ size_t index = *inout_packet_index;
+ for (; index < source_conn->packets_.size(); index++) {
+ ASSERT_TRUE(framer.ProcessPacket(*source_conn->packets_[index]));
+ for (vector<QuicStreamFrame>::const_iterator
+ i = framer.stream_frames().begin();
+ i != framer.stream_frames().end(); ++i) {
+ ASSERT_TRUE(crypto_framer.ProcessInput(i->data));
+ ASSERT_FALSE(crypto_visitor.error());
+ }
+ }
+ *inout_packet_index = index;
- framer.reset(new SimpleQuicFramer);
- ASSERT_LT(i, b_conn->packets_.size());
- ASSERT_TRUE(framer->ProcessPacket(*b_conn->packets_[i]));
- ASSERT_EQ(1u, framer->stream_frames().size());
+ ASSERT_EQ(0u, crypto_framer.InputBytesRemaining());
- scoped_ptr<CryptoHandshakeMessage> b_msg(framer->HandshakeMessage(0));
- a->OnHandshakeMessage(*(b_msg.get()));
+ for (vector<CryptoHandshakeMessage>::const_iterator
+ i = crypto_visitor.messages().begin();
+ i != crypto_visitor.messages().end(); ++i) {
+ dest_stream->OnHandshakeMessage(*i);
}
}
} // anonymous namespace
// static
-void CryptoTestUtils::HandshakeWithFakeServer(
+void CryptoTestUtils::CommunicateHandshakeMessages(
+ PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b) {
+ size_t a_i = 0, b_i = 0;
+ while (!a->handshake_complete()) {
+ ASSERT_GT(a_conn->packets_.size(), a_i);
+ LOG(INFO) << "Processing " << a_conn->packets_.size() - a_i
+ << " packets a->b";
+ MovePackets(a_conn, &a_i, b);
+
+ ASSERT_GT(b_conn->packets_.size(), b_i);
+ LOG(INFO) << "Processing " << b_conn->packets_.size() - b_i
+ << " packets b->a";
+ if (b_conn->packets_.size() - b_i == 2) {
+ LOG(INFO) << "here";
+ }
+ MovePackets(b_conn, &b_i, a);
+ }
+}
+
+// static
+int CryptoTestUtils::HandshakeWithFakeServer(
PacketSavingConnection* client_conn,
QuicCryptoClientStream* client) {
QuicGuid guid(1);
@@ -94,10 +152,12 @@ void CryptoTestUtils::HandshakeWithFakeServer(
CommunicateHandshakeMessages(client_conn, client, server_conn, &server);
CompareClientAndServerKeys(client, &server);
+
+ return client->num_sent_client_hellos();
}
// static
-void CryptoTestUtils::HandshakeWithFakeClient(
+int CryptoTestUtils::HandshakeWithFakeClient(
PacketSavingConnection* server_conn,
QuicCryptoServerStream* server) {
QuicGuid guid(1);
@@ -121,6 +181,8 @@ void CryptoTestUtils::HandshakeWithFakeClient(
CommunicateHandshakeMessages(client_conn, &client, server_conn, server);
CompareClientAndServerKeys(&client, server);
+
+ return client.num_sent_client_hellos();
}
// static
diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h
index 73ff480..28202ad 100644
--- a/net/quic/test_tools/crypto_test_utils.h
+++ b/net/quic/test_tools/crypto_test_utils.h
@@ -14,11 +14,13 @@
namespace net {
+class ProofSource;
class QuicClock;
class QuicConfig;
class QuicCryptoClientStream;
class QuicCryptoServerConfig;
class QuicCryptoServerStream;
+class QuicCryptoStream;
class QuicRandom;
namespace test {
@@ -27,11 +29,13 @@ class PacketSavingConnection;
class CryptoTestUtils {
public:
- static void HandshakeWithFakeServer(PacketSavingConnection* client_conn,
- QuicCryptoClientStream* client);
+ // returns: the number of client hellos that the client sent.
+ static int HandshakeWithFakeServer(PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client);
- static void HandshakeWithFakeClient(PacketSavingConnection* server_conn,
- QuicCryptoServerStream* server);
+ // returns: the number of client hellos that the client sent.
+ static int HandshakeWithFakeClient(PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server);
// SetupCryptoServerConfigForTest configures |config| and |crypto_config|
// with sensible defaults for testing.
@@ -41,10 +45,20 @@ class CryptoTestUtils {
QuicConfig* config,
QuicCryptoServerConfig* crypto_config);
+ // CommunicateHandshakeMessages moves messages from |a| to |b| and back until
+ // |a|'s handshake has completed.
+ static void CommunicateHandshakeMessages(PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b);
+
// Returns the value for the tag |tag| in the tag value map of |message|.
static std::string GetValueForTag(const CryptoHandshakeMessage& message,
CryptoTag tag);
+ // Returns a |ProofSource| that serves up test certificates.
+ static ProofSource* ProofSourceForTesting();
+
private:
static void CompareClientAndServerKeys(QuicCryptoClientStream* client,
QuicCryptoServerStream* server);
diff --git a/net/quic/test_tools/mock_random.cc b/net/quic/test_tools/mock_random.cc
index f7b8ea0..a8b8956 100644
--- a/net/quic/test_tools/mock_random.cc
+++ b/net/quic/test_tools/mock_random.cc
@@ -6,12 +6,16 @@
namespace net {
+MockRandom::MockRandom()
+ : increment_(0) {
+}
+
void MockRandom::RandBytes(void* data, size_t len) {
- memset(data, 'r', len);
+ memset(data, 'r' + increment_, len);
}
uint64 MockRandom::RandUint64() {
- return 0xDEADBEEF;
+ return 0xDEADBEEF + increment_;
}
bool MockRandom::RandBool() {
@@ -19,6 +23,7 @@ bool MockRandom::RandBool() {
}
void MockRandom::Reseed(const void* additional_entropy, size_t entropy_len) {
+ increment_++;
}
} // namespace net
diff --git a/net/quic/test_tools/mock_random.h b/net/quic/test_tools/mock_random.h
index b583182..1278297 100644
--- a/net/quic/test_tools/mock_random.h
+++ b/net/quic/test_tools/mock_random.h
@@ -12,16 +12,23 @@ namespace net {
class MockRandom : public QuicRandom {
public:
+ MockRandom();
+
// QuicRandom:
- // Fills the |data| buffer with 'r'.
+ // Fills the |data| buffer with a repeating byte, initially 'r'.
virtual void RandBytes(void* data, size_t len) OVERRIDE;
- // Returns 0xDEADBEEF.
+ // Returns 0xDEADBEEF + the current increment.
virtual uint64 RandUint64() OVERRIDE;
// Returns false.
virtual bool RandBool() OVERRIDE;
- // Does nothing.
+ // Reseed advances |increment_| which causes the value returned by
+ // |RandUint64| to increment and the byte that |RandBytes| fills with, to
+ // advance.
virtual void Reseed(const void* additional_entropy,
size_t entropy_len) OVERRIDE;
+
+ private:
+ uint8 increment_;
};
} // namespace net
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index 8196373..aa8eb37 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -127,22 +127,34 @@ QuicRandom* MockHelper::GetRandomGenerator() {
return &random_generator_;
}
+void MockHelper::AdvanceTime(QuicTime::Delta delta) {
+ clock_.AdvanceTime(delta);
+}
+
MockConnection::MockConnection(QuicGuid guid,
IPEndPoint address,
bool is_server)
- : QuicConnection(guid, address, new MockHelper(), is_server) {
+ : QuicConnection(guid, address, new MockHelper(), is_server),
+ has_mock_helper_(true) {
}
MockConnection::MockConnection(QuicGuid guid,
IPEndPoint address,
QuicConnectionHelperInterface* helper,
bool is_server)
- : QuicConnection(guid, address, helper, is_server) {
+ : QuicConnection(guid, address, helper, is_server),
+ has_mock_helper_(false) {
}
MockConnection::~MockConnection() {
}
+void MockConnection::AdvanceTime(QuicTime::Delta delta) {
+ CHECK(has_mock_helper_) << "Cannot advance time unless a MockClock is being"
+ " used";
+ static_cast<MockHelper*>(helper())->AdvanceTime(delta);
+}
+
PacketSavingConnection::PacketSavingConnection(QuicGuid guid,
IPEndPoint address,
bool is_server)
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 2f1f382..73a3cd7 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -184,6 +184,7 @@ class MockHelper : public QuicConnectionHelperInterface {
MOCK_METHOD1(SetConnection, void(QuicConnection* connection));
const QuicClock* GetClock() const;
QuicRandom* GetRandomGenerator();
+ void AdvanceTime(QuicTime::Delta delta);
MOCK_METHOD2(WritePacketToWire, int(const QuicEncryptedPacket& packet,
int* error));
MOCK_METHOD0(IsWriteBlockedDataBuffered, bool());
@@ -196,7 +197,7 @@ class MockHelper : public QuicConnectionHelperInterface {
MOCK_METHOD0(UnregisterSendAlarmIfRegistered, void());
MOCK_METHOD0(ClearAckAlarm, void());
private:
- const MockClock clock_;
+ MockClock clock_;
MockRandom random_generator_;
};
@@ -210,6 +211,10 @@ class MockConnection : public QuicConnection {
bool is_server);
virtual ~MockConnection();
+ // If the constructor that uses a MockHelper has been used then this method
+ // will advance the time of the MockClock.
+ void AdvanceTime(QuicTime::Delta delta);
+
MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
const QuicEncryptedPacket& packet));
@@ -234,6 +239,8 @@ class MockConnection : public QuicConnection {
}
private:
+ const bool has_mock_helper_;
+
DISALLOW_COPY_AND_ASSIGN(MockConnection);
};
diff --git a/net/quic/test_tools/simple_quic_framer.cc b/net/quic/test_tools/simple_quic_framer.cc
index a007186..898cf35 100644
--- a/net/quic/test_tools/simple_quic_framer.cc
+++ b/net/quic/test_tools/simple_quic_framer.cc
@@ -173,13 +173,6 @@ const vector<QuicRstStreamFrame>& SimpleQuicFramer::rst_stream_frames() const {
return visitor_->rst_stream_frames();
}
-CryptoHandshakeMessage* SimpleQuicFramer::HandshakeMessage(size_t index) const {
- if (index >= visitor_->stream_frames().size()) {
- return NULL;
- }
- return CryptoFramer::ParseMessage(visitor_->stream_frames()[index].data);
-}
-
const vector<QuicCongestionFeedbackFrame>&
SimpleQuicFramer::feedback_frames() const {
return visitor_->feedback_frames();
diff --git a/net/quic/test_tools/simple_quic_framer.h b/net/quic/test_tools/simple_quic_framer.h
index 07a443f..bda2b89 100644
--- a/net/quic/test_tools/simple_quic_framer.h
+++ b/net/quic/test_tools/simple_quic_framer.h
@@ -43,10 +43,6 @@ class SimpleQuicFramer {
const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
const std::vector<QuicStreamFrame>& stream_frames() const;
const QuicFecData& fec_data() const;
- // HandshakeMessage returns the index'th stream frame as a freshly allocated
- // handshake message which the caller takes ownership of. If parsing fails
- // then NULL is returned.
- CryptoHandshakeMessage* HandshakeMessage(size_t index) const;
private:
QuicFramer framer_;
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
index e0824bb..ba77cbd 100644
--- a/net/tools/quic/quic_dispatcher_test.cc
+++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -8,6 +8,7 @@
#include "base/strings/string_piece.h"
#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_server_config.h"
#include "net/quic/quic_crypto_stream.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/tools/flip_server/epoll_server.h"
diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h
index 28ea551..99a9d7f 100644
--- a/net/tools/quic/quic_server.h
+++ b/net/tools/quic/quic_server.h
@@ -10,6 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_server_config.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_framer.h"
#include "net/tools/flip_server/epoll_server.h"
diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc
index 152d3aa..980d6bf 100644
--- a/net/tools/quic/test_tools/quic_test_utils.cc
+++ b/net/tools/quic/test_tools/quic_test_utils.cc
@@ -41,6 +41,12 @@ MockConnection::MockConnection(QuicGuid guid,
MockConnection::~MockConnection() {
}
+void MockConnection::AdvanceTime(QuicTime::Delta delta) {
+ CHECK(has_mock_helper_) << "Cannot advance time unless a MockClock is being"
+ " used";
+ static_cast<MockHelper*>(helper())->AdvanceTime(delta);
+}
+
} // namespace test
} // namespace tools
} // namespace net
diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h
index 6e0fe72..8af1f45 100644
--- a/net/tools/quic/test_tools/quic_test_utils.h
+++ b/net/tools/quic/test_tools/quic_test_utils.h
@@ -31,6 +31,10 @@ class MockConnection : public QuicConnection {
QuicConnectionHelperInterface* helper, bool is_server);
virtual ~MockConnection();
+ // If the constructor that uses a MockHelper has been used then this method
+ // will advance the time of the MockClock.
+ void AdvanceTime(QuicTime::Delta delta);
+
MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
const QuicEncryptedPacket& packet));