Index: net/quic/crypto/crypto_server_config.cc |
diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc |
index 953f57cbb77e1ffab1e009514e54506351ea07ae..e4ee4009f1023a7b65ab43a391a9a104077ecc3f 100644 |
--- a/net/quic/crypto/crypto_server_config.cc |
+++ b/net/quic/crypto/crypto_server_config.cc |
@@ -14,6 +14,7 @@ |
#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/ephemeral_key_source.h" |
#include "net/quic/crypto/key_exchange.h" |
#include "net/quic/crypto/p256_key_exchange.h" |
#include "net/quic/crypto/proof_source.h" |
@@ -52,7 +53,7 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( |
: 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 */, |
+ 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 */); |
@@ -68,7 +69,8 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() { |
QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( |
QuicRandom* rand, |
const QuicClock* clock, |
- const CryptoHandshakeMessage& extra_tags) { |
+ const CryptoHandshakeMessage& extra_tags, |
+ uint64 expiry_time) { |
CryptoHandshakeMessage msg; |
const string curve25519_private_key = |
@@ -77,20 +79,20 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( |
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)); |
+ 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. |
+ // First three 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.push_back(curve25519_public_value.size() >> 16); |
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.push_back(p256_public_value.size() >> 16); |
encoded_public_values.append(p256_public_value.data(), |
p256_public_value.size()); |
@@ -102,6 +104,16 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( |
msg.Insert(extra_tags.tag_value_map().begin(), |
extra_tags.tag_value_map().end()); |
+ if (expiry_time == 0) { |
+ const QuicWallTime now = clock->WallNow(); |
+ const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds( |
+ 60 * 60 * 24 * 180 /* 180 days, ~six months */)); |
+ const uint64 expiry_seconds = expiry.ToUNIXSeconds(); |
+ msg.SetValue(kEXPY, expiry_seconds); |
+ } else { |
+ msg.SetValue(kEXPY, expiry_time); |
+ } |
+ |
char scid_bytes[16]; |
rand->RandBytes(scid_bytes, sizeof(scid_bytes)); |
msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes))); |
@@ -110,8 +122,7 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( |
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<QuicData> serialized(CryptoFramer::ConstructHandshakeMessage(msg)); |
scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf); |
config->set_config(serialized->AsStringPiece()); |
@@ -135,9 +146,8 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( |
return NULL; |
} |
if (msg->tag() != kSCFG) { |
- LOG(WARNING) << "Server config message has tag " |
- << msg->tag() << " expected " |
- << kSCFG; |
+ LOG(WARNING) << "Server config message has tag " << msg->tag() |
+ << " expected " << kSCFG; |
return NULL; |
} |
@@ -151,15 +161,15 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( |
} |
config->id = scid.as_string(); |
- const CryptoTag* aead_tags; |
+ const QuicTag* 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); |
+ config->aead = vector<QuicTag>(aead_tags, aead_tags + aead_len); |
- const CryptoTag* kexs_tags; |
+ const QuicTag* 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"; |
@@ -181,16 +191,14 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( |
memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); |
if (kexs_len != protobuf->key_size()) { |
- LOG(WARNING) << "Server config has " |
- << kexs_len |
+ LOG(WARNING) << "Server config has " << kexs_len |
<< " key exchange methods configured, but " |
- << protobuf->key_size() |
- << " private keys"; |
+ << protobuf->key_size() << " private keys"; |
return NULL; |
} |
for (size_t i = 0; i < kexs_len; i++) { |
- const CryptoTag tag = kexs_tags[i]; |
+ const QuicTag tag = kexs_tags[i]; |
string private_key; |
config->kexs.push_back(tag); |
@@ -205,34 +213,32 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( |
if (private_key.empty()) { |
LOG(WARNING) << "Server config contains key exchange method without " |
- "corresponding private key: " |
- << tag; |
+ "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."; |
+ 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; |
- } |
- 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(); |
@@ -271,9 +277,10 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( |
CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( |
QuicRandom* rand, |
const QuicClock* clock, |
- const CryptoHandshakeMessage& extra_tags) { |
+ const CryptoHandshakeMessage& extra_tags, |
+ uint64 expiry_time) { |
scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig( |
- rand, clock, extra_tags)); |
+ rand, clock, extra_tags, expiry_time)); |
return AddConfig(config.get()); |
} |
@@ -281,7 +288,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
const CryptoHandshakeMessage& client_hello, |
QuicGuid guid, |
const IPEndPoint& client_ip, |
- QuicTime::Delta now_since_unix_epoch, |
+ const QuicClock* clock, |
QuicRandom* rand, |
QuicCryptoNegotiatedParameters *params, |
CryptoHandshakeMessage* out, |
@@ -299,15 +306,16 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
} |
const Config* const config(it->second); |
+ const QuicWallTime now = clock->WallNow(); |
bool valid_source_address_token = false; |
StringPiece srct; |
- if (client_hello.GetStringPiece(kSRCT, &srct) && |
- ValidateSourceAddressToken(srct, client_ip, now_since_unix_epoch)) { |
+ if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct) && |
+ ValidateSourceAddressToken(srct, client_ip, now)) { |
valid_source_address_token = true; |
} |
const string fresh_source_address_token = |
- NewSourceAddressToken(client_ip, rand, now_since_unix_epoch); |
+ NewSourceAddressToken(client_ip, rand, now); |
// 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 |
@@ -328,14 +336,18 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
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()), |
+ static_cast<uint32>(now.ToUNIXSeconds()), |
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())); |
+ static_cast<uint32>(now.ToUNIXSeconds())); |
} |
+ StringPiece server_nonce; |
+ client_hello.GetStringPiece(kServerNonceTag, &server_nonce); |
+ const bool server_nonce_matches = server_nonce == params->server_nonce; |
+ |
out->Clear(); |
StringPiece sni; |
@@ -346,6 +358,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
scid.as_string() != config->id || |
!valid_source_address_token || |
!client_nonce_well_formed || |
+ !server_nonce_matches || |
(!unique_by_strike_register && |
!unique_by_server_nonce)) { |
// If the client didn't provide a server config ID, or gave the wrong one, |
@@ -353,22 +366,20 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
// 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); |
+ out->SetStringPiece(kSourceAddressTokenTag, 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)), |
+ now, rand, StringPiece(reinterpret_cast<const char*>(config->orbit), |
+ sizeof(config->orbit)), |
¶ms->server_nonce); |
} |
- out->SetStringPiece(kNONC, params->server_nonce); |
+ out->SetStringPiece(kServerNonceTag, params->server_nonce); |
// The client may have requested a certificate chain. |
- const CryptoTag* their_proof_demands; |
+ const QuicTag* their_proof_demands; |
size_t num_their_proof_demands; |
- if (proof_source_.get() != NULL && |
- !sni.empty() && |
+ if (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++) { |
@@ -398,7 +409,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
static const size_t kMaxUnverifiedSize = 400; |
if (valid_source_address_token || |
signature.size() + compressed.size() < kMaxUnverifiedSize) { |
- out->SetStringPiece(kCERT, compressed); |
+ out->SetStringPiece(kCertificateTag, compressed); |
out->SetStringPiece(kPROF, signature); |
} |
break; |
@@ -408,8 +419,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
return QUIC_NO_ERROR; |
} |
- const CryptoTag* their_aeads; |
- const CryptoTag* their_key_exchanges; |
+ const QuicTag* their_aeads; |
+ const QuicTag* 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 || |
@@ -422,16 +433,13 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
} |
size_t key_exchange_index; |
- if (!CryptoUtils::FindMutualTag(config->aead, |
- their_aeads, num_their_aeads, |
- CryptoUtils::LOCAL_PRIORITY, |
- ¶ms->aead, |
+ if (!CryptoUtils::FindMutualTag(config->aead, their_aeads, num_their_aeads, |
+ CryptoUtils::LOCAL_PRIORITY, ¶ms->aead, |
NULL) || |
- !CryptoUtils::FindMutualTag(config->kexs, |
- their_key_exchanges, num_their_key_exchanges, |
- CryptoUtils::LOCAL_PRIORITY, |
- ¶ms->key_exchange, |
- &key_exchange_index)) { |
+ !CryptoUtils::FindMutualTag( |
+ config->kexs, their_key_exchanges, num_their_key_exchanges, |
+ CryptoUtils::LOCAL_PRIORITY, ¶ms->key_exchange, |
+ &key_exchange_index)) { |
*error_details = "Unsupported AEAD or KEXS"; |
return QUIC_CRYPTO_NO_SUPPORT; |
} |
@@ -442,28 +450,67 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; |
} |
- if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey( |
- public_value, ¶ms->premaster_secret)) { |
+ const KeyExchange* key_exchange = config->key_exchanges[key_exchange_index]; |
+ if (!key_exchange->CalculateSharedKey(public_value, |
+ ¶ms->initial_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)); |
- |
+ string hkdf_suffix; |
const QuicData& client_hello_serialized = client_hello.GetSerialized(); |
- hkdf_input.append(client_hello_serialized.data(), |
- client_hello_serialized.length()); |
- hkdf_input.append(config->serialized); |
+ hkdf_suffix.reserve(sizeof(guid) + client_hello_serialized.length() + |
+ config->serialized.size()); |
+ hkdf_suffix.append(reinterpret_cast<char*>(&guid), sizeof(guid)); |
+ hkdf_suffix.append(client_hello_serialized.data(), |
+ client_hello_serialized.length()); |
+ hkdf_suffix.append(config->serialized); |
+ |
+ string hkdf_input; |
+ size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; |
+ hkdf_input.reserve(label_len + hkdf_suffix.size()); |
+ hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); |
+ hkdf_input.append(hkdf_suffix); |
+ |
+ CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, |
+ client_nonce, params->server_nonce, hkdf_input, |
+ CryptoUtils::SERVER, ¶ms->initial_crypters); |
+ |
+ string forward_secure_public_value; |
+ if (ephemeral_key_source_.get()) { |
+ params->forward_secure_premaster_secret = |
+ ephemeral_key_source_->CalculateForwardSecureKey( |
+ key_exchange, rand, clock->ApproximateNow(), public_value, |
+ &forward_secure_public_value); |
+ } else { |
+ scoped_ptr<KeyExchange> forward_secure_key_exchange( |
+ key_exchange->NewKeyPair(rand)); |
+ forward_secure_public_value = |
+ forward_secure_key_exchange->public_value().as_string(); |
+ if (!forward_secure_key_exchange->CalculateSharedKey( |
+ public_value, ¶ms->forward_secure_premaster_secret)) { |
+ *error_details = "Invalid public value"; |
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; |
+ } |
+ } |
- CryptoUtils::DeriveKeys(params, client_nonce, hkdf_input, |
- CryptoUtils::SERVER); |
+ string forward_secure_hkdf_input; |
+ label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; |
+ forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size()); |
+ forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, |
+ label_len); |
+ forward_secure_hkdf_input.append(hkdf_suffix); |
+ |
+ CryptoUtils::DeriveKeys(params->forward_secure_premaster_secret, params->aead, |
+ client_nonce, params->server_nonce, |
+ forward_secure_hkdf_input, CryptoUtils::SERVER, |
+ ¶ms->forward_secure_crypters); |
out->set_tag(kSHLO); |
- out->SetStringPiece(kSRCT, fresh_source_address_token); |
+ out->SetStringPiece(kSourceAddressTokenTag, fresh_source_address_token); |
+ out->SetStringPiece(kPUBS, forward_secure_public_value); |
return QUIC_NO_ERROR; |
} |
@@ -471,19 +518,24 @@ void QuicCryptoServerConfig::SetProofSource(ProofSource* proof_source) { |
proof_source_.reset(proof_source); |
} |
+void QuicCryptoServerConfig::SetEphemeralKeySource( |
+ EphemeralKeySource* ephemeral_key_source) { |
+ ephemeral_key_source_.reset(ephemeral_key_source); |
+} |
+ |
string QuicCryptoServerConfig::NewSourceAddressToken( |
const IPEndPoint& ip, |
QuicRandom* rand, |
- QuicTime::Delta now_since_epoch) const { |
+ QuicWallTime now) const { |
SourceAddressToken source_address_token; |
source_address_token.set_ip(ip.ToString()); |
- source_address_token.set_timestamp(now_since_epoch.ToSeconds()); |
+ source_address_token.set_timestamp(now.ToUNIXSeconds()); |
string plaintext = source_address_token.SerializeAsString(); |
char nonce[12]; |
DCHECK_EQ(sizeof(nonce), |
- source_address_token_encrypter_->GetNoncePrefixSize() + |
- sizeof(QuicPacketSequenceNumber)); |
+ source_address_token_encrypter_->GetNoncePrefixSize() + |
+ sizeof(QuicPacketSequenceNumber)); |
rand->RandBytes(nonce, sizeof(nonce)); |
size_t ciphertext_size = |
@@ -493,8 +545,8 @@ string QuicCryptoServerConfig::NewSourceAddressToken( |
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)]))) { |
+ StringPiece(nonce, sizeof(nonce)), StringPiece(), plaintext, |
+ reinterpret_cast<unsigned char*>(&result[sizeof(nonce)]))) { |
DCHECK(false); |
return string(); |
} |
@@ -505,11 +557,11 @@ string QuicCryptoServerConfig::NewSourceAddressToken( |
bool QuicCryptoServerConfig::ValidateSourceAddressToken( |
StringPiece token, |
const IPEndPoint& ip, |
- QuicTime::Delta now_since_epoch) const { |
+ QuicWallTime now) const { |
char nonce[12]; |
DCHECK_EQ(sizeof(nonce), |
- source_address_token_encrypter_->GetNoncePrefixSize() + |
- sizeof(QuicPacketSequenceNumber)); |
+ source_address_token_encrypter_->GetNoncePrefixSize() + |
+ sizeof(QuicPacketSequenceNumber)); |
if (token.size() <= sizeof(nonce)) { |
return false; |
@@ -529,8 +581,8 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken( |
size_t plaintext_length; |
if (!source_address_token_decrypter_->Decrypt( |
- StringPiece(nonce, sizeof(nonce)), StringPiece(), token, |
- plaintext, &plaintext_length)) { |
+ StringPiece(nonce, sizeof(nonce)), StringPiece(), token, plaintext, |
+ &plaintext_length)) { |
return false; |
} |
@@ -544,18 +596,18 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken( |
return false; |
} |
- const QuicTime::Delta delta(now_since_epoch.Subtract( |
- QuicTime::Delta::FromSeconds(source_address_token.timestamp()))); |
- const int64 delta_secs = delta.ToSeconds(); |
+ const QuicWallTime timestamp( |
+ QuicWallTime::FromUNIXSeconds(source_address_token.timestamp())); |
+ const QuicTime::Delta delta(now.AbsoluteDifference(timestamp)); |
// TODO(agl): consider whether and how these magic values should be moved to |
// a config. |
- if (delta_secs < -3600) { |
+ if (now.IsBefore(timestamp) && delta.ToSeconds() > 3600) { |
// We only allow timestamps to be from an hour in the future. |
return false; |
} |
- if (delta_secs > 86400) { |
+ if (now.IsAfter(timestamp) && delta.ToSeconds() > 86400) { |
// We allow one day into the past. |
return false; |
} |
@@ -563,11 +615,8 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken( |
return true; |
} |
-QuicCryptoServerConfig::Config::Config() { |
-} |
+QuicCryptoServerConfig::Config::Config() {} |
-QuicCryptoServerConfig::Config::~Config() { |
- STLDeleteElements(&key_exchanges); |
-} |
+QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); } |
} // namespace net |