diff options
50 files changed, 1184 insertions, 435 deletions
diff --git a/net/net.gyp b/net/net.gyp index f7f5b1d..9eb53fd 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -708,12 +708,12 @@ 'quic/congestion_control/tcp_cubic_sender.h', 'quic/congestion_control/tcp_receiver.cc', 'quic/congestion_control/tcp_receiver.h', - 'quic/crypto/aes_128_gcm_decrypter.h', - 'quic/crypto/aes_128_gcm_decrypter_nss.cc', - 'quic/crypto/aes_128_gcm_decrypter_openssl.cc', - 'quic/crypto/aes_128_gcm_encrypter.h', - 'quic/crypto/aes_128_gcm_encrypter_nss.cc', - 'quic/crypto/aes_128_gcm_encrypter_openssl.cc', + 'quic/crypto/aes_128_gcm_12_decrypter.h', + 'quic/crypto/aes_128_gcm_12_decrypter_nss.cc', + 'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc', + 'quic/crypto/aes_128_gcm_12_encrypter.h', + 'quic/crypto/aes_128_gcm_12_encrypter_nss.cc', + 'quic/crypto/aes_128_gcm_12_encrypter_openssl.cc', 'quic/crypto/cert_compressor.cc', 'quic/crypto/cert_compressor.h', 'quic/crypto/common_cert_set.cc', @@ -1175,8 +1175,8 @@ 'cert/x509_util_nss.h', 'ocsp/nss_ocsp.cc', 'ocsp/nss_ocsp.h', - 'quic/crypto/aes_128_gcm_decrypter_nss.cc', - 'quic/crypto/aes_128_gcm_encrypter_nss.cc', + 'quic/crypto/aes_128_gcm_12_decrypter_nss.cc', + 'quic/crypto/aes_128_gcm_12_encrypter_nss.cc', 'quic/crypto/p256_key_exchange_nss.cc', 'socket/nss_ssl_util.cc', 'socket/nss_ssl_util.h', @@ -1207,8 +1207,8 @@ 'cert/x509_certificate_openssl.cc', 'cert/x509_util_openssl.cc', 'cert/x509_util_openssl.h', - 'quic/crypto/aes_128_gcm_decrypter_openssl.cc', - 'quic/crypto/aes_128_gcm_encrypter_openssl.cc', + 'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc', + 'quic/crypto/aes_128_gcm_12_encrypter_openssl.cc', 'quic/crypto/p256_key_exchange_openssl.cc', 'quic/crypto/scoped_evp_cipher_ctx.h', 'socket/ssl_client_socket_openssl.cc', @@ -1643,12 +1643,14 @@ 'quic/congestion_control/quic_max_sized_map_test.cc', 'quic/congestion_control/tcp_cubic_sender_test.cc', 'quic/congestion_control/tcp_receiver_test.cc', - 'quic/crypto/aes_128_gcm_decrypter_test.cc', - 'quic/crypto/aes_128_gcm_encrypter_test.cc', + 'quic/crypto/aes_128_gcm_12_decrypter_test.cc', + 'quic/crypto/aes_128_gcm_12_encrypter_test.cc', 'quic/crypto/cert_compressor_test.cc', 'quic/crypto/common_cert_set_test.cc', 'quic/crypto/crypto_framer_test.cc', 'quic/crypto/crypto_handshake_test.cc', + 'quic/crypto/crypto_server_test.cc', + 'quic/crypto/crypto_utils_test.cc', 'quic/crypto/curve25519_key_exchange_test.cc', 'quic/crypto/null_decrypter_test.cc', 'quic/crypto/null_encrypter_test.cc', diff --git a/net/quic/crypto/aes_128_gcm_decrypter.h b/net/quic/crypto/aes_128_gcm_12_decrypter.h index cf7e412..0a066659 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter.h +++ b/net/quic/crypto/aes_128_gcm_12_decrypter.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_QUIC_CRYPTO_AES_128_GCM_DECRYPTER_H_ -#define NET_QUIC_CRYPTO_AES_128_GCM_DECRYPTER_H_ +#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ #include <string> @@ -13,19 +13,24 @@ namespace net { namespace test { -class Aes128GcmDecrypterPeer; +class Aes128Gcm12DecrypterPeer; } // namespace test -// An Aes128GcmDecrypter is a QuicDecrypter that implements the -// AEAD_AES_128_GCM algorithm specified in RFC 5116. Create an instance by +// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the +// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by // calling QuicDecrypter::Create(kAESG). // -// It uses an authentication tag of 16 bytes (128 bits). The fixed prefix +// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix // of the nonce is four bytes. -class NET_EXPORT_PRIVATE Aes128GcmDecrypter : public QuicDecrypter { +class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter { public: - Aes128GcmDecrypter(); - virtual ~Aes128GcmDecrypter() {} + enum { + // Authentication tags are truncated to 96 bits. + kAuthTagSize = 12, + }; + + Aes128Gcm12Decrypter(); + virtual ~Aes128Gcm12Decrypter() {} // Returns true if the underlying crypto library supports AES GCM. static bool IsSupported(); @@ -54,4 +59,4 @@ class NET_EXPORT_PRIVATE Aes128GcmDecrypter : public QuicDecrypter { } // namespace net -#endif // NET_QUIC_CRYPTO_AES_128_GCM_DECRYPTER_H_ +#endif // NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ diff --git a/net/quic/crypto/aes_128_gcm_decrypter_nss.cc b/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc index 424cdef..c2a9788 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter_nss.cc +++ b/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/quic/crypto/aes_128_gcm_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_decrypter.h" #include <nss.h> #include <pk11pub.h> @@ -106,7 +106,6 @@ base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = const size_t kKeySize = 16; const size_t kNoncePrefixSize = 4; -const size_t kAuthTagSize = 16; // Calls PK11_Decrypt if it's available. Otherwise, emulates CKM_AES_GCM using // CKM_AES_CTR and the GaloisHash class. @@ -140,7 +139,8 @@ SECStatus My_Decrypt(PK11SymKey* key, const CK_GCM_PARAMS* gcm_params = reinterpret_cast<CK_GCM_PARAMS*>(param->data); - DCHECK_EQ(gcm_params->ulTagBits, kAuthTagSize * 8); + DCHECK_EQ(gcm_params->ulTagBits, + static_cast<CK_ULONG>(Aes128Gcm12Decrypter::kAuthTagSize * 8)); if (gcm_params->ulIvLen != 12u) { DLOG(INFO) << "ulIvLen is not equal to 12"; PORT_SetError(SEC_ERROR_INPUT_LEN); @@ -205,7 +205,7 @@ SECStatus My_Decrypt(PK11SymKey* key, return SECFailure; } - if (enc_len < kAuthTagSize) { + if (enc_len < Aes128Gcm12Decrypter::kAuthTagSize) { PORT_SetError(SEC_ERROR_INPUT_LEN); return SECFailure; } @@ -214,15 +214,16 @@ SECStatus My_Decrypt(PK11SymKey* key, // NSS 3.14.1 or later (NSS bug // https://bugzilla.mozilla.org/show_bug.cgi?id=808218). if (PK11_CipherOp(ctx.get(), out, &output_len, max_len, - const_cast<unsigned char*>(enc), - enc_len - kAuthTagSize) != SECSuccess) { + const_cast<unsigned char*>(enc), + enc_len - Aes128Gcm12Decrypter::kAuthTagSize) != SECSuccess) { DLOG(INFO) << "PK11_CipherOp failed"; return SECFailure; } PK11_Finalize(ctx.get()); - if (static_cast<unsigned int>(output_len) != enc_len - kAuthTagSize) { + if (static_cast<unsigned int>(output_len) != + enc_len - Aes128Gcm12Decrypter::kAuthTagSize) { DLOG(INFO) << "Wrong output length"; PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; @@ -231,13 +232,14 @@ SECStatus My_Decrypt(PK11SymKey* key, crypto::GaloisHash ghash(ghash_key); ghash.UpdateAdditional(gcm_params->pAAD, gcm_params->ulAADLen); ghash.UpdateCiphertext(enc, output_len); - unsigned char auth_tag[kAuthTagSize]; - ghash.Finish(auth_tag, kAuthTagSize); - for (unsigned int i = 0; i < kAuthTagSize; i++) { + unsigned char auth_tag[Aes128Gcm12Decrypter::kAuthTagSize]; + ghash.Finish(auth_tag, Aes128Gcm12Decrypter::kAuthTagSize); + for (unsigned int i = 0; i < Aes128Gcm12Decrypter::kAuthTagSize; i++) { auth_tag[i] ^= tag_mask[i]; } - if (NSS_SecureMemcmp(auth_tag, enc + output_len, kAuthTagSize) != 0) { + if (NSS_SecureMemcmp(auth_tag, enc + output_len, + Aes128Gcm12Decrypter::kAuthTagSize) != 0) { PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } @@ -248,19 +250,19 @@ SECStatus My_Decrypt(PK11SymKey* key, } // namespace -Aes128GcmDecrypter::Aes128GcmDecrypter() { +Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() { ignore_result(g_gcm_support_checker.Get()); } // static -bool Aes128GcmDecrypter::IsSupported() { +bool Aes128Gcm12Decrypter::IsSupported() { // NSS 3.15 supports CKM_AES_GCM directly. // NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM. // Versions earlier than NSS 3.14 are not supported. return NSS_VersionCheck("3.14") != PR_FALSE; } -bool Aes128GcmDecrypter::SetKey(StringPiece key) { +bool Aes128Gcm12Decrypter::SetKey(StringPiece key) { DCHECK_EQ(key.size(), sizeof(key_)); if (key.size() != sizeof(key_)) { return false; @@ -269,7 +271,7 @@ bool Aes128GcmDecrypter::SetKey(StringPiece key) { return true; } -bool Aes128GcmDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { +bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) { DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); if (nonce_prefix.size() != kNoncePrefixSize) { return false; @@ -278,11 +280,11 @@ bool Aes128GcmDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool Aes128GcmDecrypter::Decrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece ciphertext, - unsigned char* output, - size_t* output_length) { +bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + unsigned char* output, + size_t* output_length) { if (ciphertext.length() < kAuthTagSize || nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { return false; @@ -346,7 +348,7 @@ bool Aes128GcmDecrypter::Decrypt(StringPiece nonce, return true; } -QuicData* Aes128GcmDecrypter::DecryptPacket( +QuicData* Aes128Gcm12Decrypter::DecryptPacket( QuicPacketSequenceNumber sequence_number, StringPiece associated_data, StringPiece ciphertext) { @@ -368,11 +370,11 @@ QuicData* Aes128GcmDecrypter::DecryptPacket( return new QuicData(plaintext.release(), plaintext_size, true); } -StringPiece Aes128GcmDecrypter::GetKey() const { +StringPiece Aes128Gcm12Decrypter::GetKey() const { return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); } -StringPiece Aes128GcmDecrypter::GetNoncePrefix() const { +StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const { return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); } diff --git a/net/quic/crypto/aes_128_gcm_decrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc index 0800cb5..cf0f5c5 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter_openssl.cc +++ b/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/quic/crypto/aes_128_gcm_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_decrypter.h" #include <openssl/evp.h> @@ -17,16 +17,15 @@ namespace { const size_t kKeySize = 16; const size_t kNoncePrefixSize = 4; -const size_t kAuthTagSize = 16; } // namespace -Aes128GcmDecrypter::Aes128GcmDecrypter() {} +Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {} // static -bool Aes128GcmDecrypter::IsSupported() { return true; } +bool Aes128Gcm12Decrypter::IsSupported() { return true; } -bool Aes128GcmDecrypter::SetKey(StringPiece key) { +bool Aes128Gcm12Decrypter::SetKey(StringPiece key) { DCHECK_EQ(key.size(), sizeof(key_)); if (key.size() != sizeof(key_)) { return false; @@ -35,7 +34,7 @@ bool Aes128GcmDecrypter::SetKey(StringPiece key) { return true; } -bool Aes128GcmDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { +bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) { DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); if (nonce_prefix.size() != kNoncePrefixSize) { return false; @@ -44,11 +43,11 @@ bool Aes128GcmDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool Aes128GcmDecrypter::Decrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece ciphertext, - unsigned char* output, - size_t* output_length) { +bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + unsigned char* output, + size_t* output_length) { if (ciphertext.length() < kAuthTagSize || nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { return false; @@ -114,7 +113,7 @@ bool Aes128GcmDecrypter::Decrypt(StringPiece nonce, return true; } -QuicData* Aes128GcmDecrypter::DecryptPacket( +QuicData* Aes128Gcm12Decrypter::DecryptPacket( QuicPacketSequenceNumber sequence_number, StringPiece associated_data, StringPiece ciphertext) { @@ -136,11 +135,11 @@ QuicData* Aes128GcmDecrypter::DecryptPacket( return new QuicData(plaintext.release(), plaintext_size, true); } -StringPiece Aes128GcmDecrypter::GetKey() const { +StringPiece Aes128Gcm12Decrypter::GetKey() const { return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); } -StringPiece Aes128GcmDecrypter::GetNoncePrefix() const { +StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const { return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); } diff --git a/net/quic/crypto/aes_128_gcm_decrypter_test.cc b/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc index 156dfb8..cea6a6e5 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter_test.cc +++ b/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/quic/crypto/aes_128_gcm_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_decrypter.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -293,7 +293,7 @@ namespace test { // DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing // in an nonce and also to allocate the buffer needed for the plaintext. -QuicData* DecryptWithNonce(Aes128GcmDecrypter* decrypter, +QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter, StringPiece nonce, StringPiece associated_data, StringPiece ciphertext) { @@ -308,8 +308,8 @@ QuicData* DecryptWithNonce(Aes128GcmDecrypter* decrypter, return new QuicData(plaintext.release(), plaintext_size, true); } -TEST(Aes128GcmDecrypterTest, Decrypt) { - if (!Aes128GcmDecrypter::IsSupported()) { +TEST(Aes128Gcm12DecrypterTest, Decrypt) { + if (!Aes128Gcm12Decrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -354,7 +354,13 @@ TEST(Aes128GcmDecrypterTest, Decrypt) { EXPECT_EQ(test_info.pt_len, pt_len * 8); } - Aes128GcmDecrypter decrypter; + // The test vectors have 16 byte authenticators but this code only uses + // the first 12. + ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize), + tag_len); + tag_len = Aes128Gcm12Decrypter::kAuthTagSize; + + Aes128Gcm12Decrypter decrypter; ASSERT_TRUE(decrypter.SetKey(StringPiece(key, key_len))); string ciphertext(ct, ct_len); ciphertext.append(tag, tag_len); diff --git a/net/quic/crypto/aes_128_gcm_encrypter.h b/net/quic/crypto/aes_128_gcm_12_encrypter.h index 3aee81e..b8380c7 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter.h +++ b/net/quic/crypto/aes_128_gcm_12_encrypter.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_QUIC_CRYPTO_AES_128_GCM_ENCRYPTER_H_ -#define NET_QUIC_CRYPTO_AES_128_GCM_ENCRYPTER_H_ +#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ #include <string> @@ -13,19 +13,24 @@ namespace net { namespace test { -class Aes128GcmEncrypterPeer; +class Aes128Gcm12EncrypterPeer; } // namespace test -// An Aes128GcmEncrypter is a QuicEncrypter that implements the -// AEAD_AES_128_GCM algorithm specified in RFC 5116. Create an instance by +// An Aes128Gcm12Encrypter is a QuicEncrypter that implements the +// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by // calling QuicEncrypter::Create(kAESG). // -// It uses an authentication tag of 16 bytes (128 bits). The fixed prefix +// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix // of the nonce is four bytes. -class NET_EXPORT_PRIVATE Aes128GcmEncrypter : public QuicEncrypter { +class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public QuicEncrypter { public: - Aes128GcmEncrypter(); - virtual ~Aes128GcmEncrypter() {} + enum { + // Authentication tags are truncated to 96 bits. + kAuthTagSize = 12, + }; + + Aes128Gcm12Encrypter(); + virtual ~Aes128Gcm12Encrypter() {} // Returns true if the underlying crypto library supports AES GCM. static bool IsSupported(); @@ -57,4 +62,4 @@ class NET_EXPORT_PRIVATE Aes128GcmEncrypter : public QuicEncrypter { } // namespace net -#endif // NET_QUIC_CRYPTO_AES_128_GCM_ENCRYPTER_H_ +#endif // NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ diff --git a/net/quic/crypto/aes_128_gcm_encrypter_nss.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc index 55b1b6f..9084b7c 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter_nss.cc +++ b/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include <nss.h> #include <pk11pub.h> @@ -106,7 +106,6 @@ base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = const size_t kKeySize = 16; const size_t kNoncePrefixSize = 4; -const size_t kAuthTagSize = 16; // Calls PK11_Encrypt if it's available. Otherwise, emulates CKM_AES_GCM using // CKM_AES_CTR and the GaloisHash class. @@ -137,7 +136,7 @@ SECStatus My_Encrypt(PK11SymKey* key, DCHECK_EQ(mechanism, static_cast<CK_MECHANISM_TYPE>(CKM_AES_GCM)); DCHECK_EQ(param->len, sizeof(CK_GCM_PARAMS)); - if (max_len < kAuthTagSize) { + if (max_len < static_cast<unsigned int>(Aes128Gcm12Encrypter::kAuthTagSize)) { DLOG(INFO) << "max_len is less than kAuthTagSize"; PORT_SetError(SEC_ERROR_OUTPUT_LEN); return SECFailure; @@ -146,7 +145,8 @@ SECStatus My_Encrypt(PK11SymKey* key, const CK_GCM_PARAMS* gcm_params = reinterpret_cast<CK_GCM_PARAMS*>(param->data); - DCHECK_EQ(gcm_params->ulTagBits, kAuthTagSize * 8); + DCHECK_EQ(gcm_params->ulTagBits, + static_cast<CK_ULONG>(Aes128Gcm12Encrypter::kAuthTagSize * 8)); if (gcm_params->ulIvLen != 12u) { DLOG(INFO) << "ulIvLen is not equal to 12"; PORT_SetError(SEC_ERROR_INPUT_LEN); @@ -228,7 +228,8 @@ SECStatus My_Encrypt(PK11SymKey* key, return SECFailure; } - if ((max_len - kAuthTagSize) < static_cast<unsigned int>(output_len)) { + if ((max_len - Aes128Gcm12Encrypter::kAuthTagSize) < + static_cast<unsigned int>(output_len)) { DLOG(INFO) << "(max_len - kAuthTagSize) is less than output_len"; PORT_SetError(SEC_ERROR_OUTPUT_LEN); return SECFailure; @@ -237,30 +238,30 @@ SECStatus My_Encrypt(PK11SymKey* key, crypto::GaloisHash ghash(ghash_key); ghash.UpdateAdditional(gcm_params->pAAD, gcm_params->ulAADLen); ghash.UpdateCiphertext(out, output_len); - ghash.Finish(out + output_len, kAuthTagSize); - for (unsigned int i = 0; i < kAuthTagSize; i++) { + ghash.Finish(out + output_len, Aes128Gcm12Encrypter::kAuthTagSize); + for (unsigned int i = 0; i < Aes128Gcm12Encrypter::kAuthTagSize; i++) { out[output_len + i] ^= tag_mask[i]; } - *out_len = output_len + kAuthTagSize; + *out_len = output_len + Aes128Gcm12Encrypter::kAuthTagSize; return SECSuccess; } } // namespace -Aes128GcmEncrypter::Aes128GcmEncrypter() { +Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() { ignore_result(g_gcm_support_checker.Get()); } // static -bool Aes128GcmEncrypter::IsSupported() { +bool Aes128Gcm12Encrypter::IsSupported() { // NSS 3.15 supports CKM_AES_GCM directly. // NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM. // Versions earlier than NSS 3.14 are not supported. return NSS_VersionCheck("3.14") != PR_FALSE; } -bool Aes128GcmEncrypter::SetKey(StringPiece key) { +bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { DCHECK_EQ(key.size(), sizeof(key_)); if (key.size() != sizeof(key_)) { return false; @@ -269,7 +270,7 @@ bool Aes128GcmEncrypter::SetKey(StringPiece key) { return true; } -bool Aes128GcmEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { +bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) { DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); if (nonce_prefix.size() != kNoncePrefixSize) { return false; @@ -278,10 +279,10 @@ bool Aes128GcmEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool Aes128GcmEncrypter::Encrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext, - unsigned char* output) { +bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { return false; } @@ -338,7 +339,7 @@ bool Aes128GcmEncrypter::Encrypt(StringPiece nonce, return true; } -QuicData* Aes128GcmEncrypter::EncryptPacket( +QuicData* Aes128Gcm12Encrypter::EncryptPacket( QuicPacketSequenceNumber sequence_number, StringPiece associated_data, StringPiece plaintext) { @@ -357,29 +358,29 @@ QuicData* Aes128GcmEncrypter::EncryptPacket( return new QuicData(ciphertext.release(), ciphertext_size, true); } -size_t Aes128GcmEncrypter::GetKeySize() const { +size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; } -size_t Aes128GcmEncrypter::GetNoncePrefixSize() const { +size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const { return kNoncePrefixSize; } -size_t Aes128GcmEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { +size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { return ciphertext_size - kAuthTagSize; } // An AEAD_AES_128_GCM ciphertext is exactly 16 bytes longer than its // corresponding plaintext. -size_t Aes128GcmEncrypter::GetCiphertextSize(size_t plaintext_size) const { +size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const { return plaintext_size + kAuthTagSize; } -StringPiece Aes128GcmEncrypter::GetKey() const { +StringPiece Aes128Gcm12Encrypter::GetKey() const { return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); } -StringPiece Aes128GcmEncrypter::GetNoncePrefix() const { +StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const { return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); } diff --git a/net/quic/crypto/aes_128_gcm_encrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc index b00dec8..7481184 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter_openssl.cc +++ b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include <openssl/evp.h> #include <string.h> @@ -18,17 +18,16 @@ namespace { const size_t kKeySize = 16; const size_t kNoncePrefixSize = 4; -const size_t kAuthTagSize = 16; } // namespace -Aes128GcmEncrypter::Aes128GcmEncrypter() { +Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() { } // static -bool Aes128GcmEncrypter::IsSupported() { return true; } +bool Aes128Gcm12Encrypter::IsSupported() { return true; } -bool Aes128GcmEncrypter::SetKey(StringPiece key) { +bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { DCHECK_EQ(key.size(), sizeof(key_)); if (key.size() != sizeof(key_)) { return false; @@ -37,7 +36,7 @@ bool Aes128GcmEncrypter::SetKey(StringPiece key) { return true; } -bool Aes128GcmEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { +bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) { DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); if (nonce_prefix.size() != kNoncePrefixSize) { return false; @@ -46,10 +45,10 @@ bool Aes128GcmEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool Aes128GcmEncrypter::Encrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext, - unsigned char* output) { +bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { // |output_len| is passed to an OpenSSL function to receive the output // length. int output_len; @@ -111,7 +110,7 @@ bool Aes128GcmEncrypter::Encrypt(StringPiece nonce, return true; } -QuicData* Aes128GcmEncrypter::EncryptPacket( +QuicData* Aes128Gcm12Encrypter::EncryptPacket( QuicPacketSequenceNumber sequence_number, StringPiece associated_data, StringPiece plaintext) { @@ -131,27 +130,27 @@ QuicData* Aes128GcmEncrypter::EncryptPacket( return new QuicData(ciphertext.release(), ciphertext_size, true); } -size_t Aes128GcmEncrypter::GetKeySize() const { return kKeySize; } +size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; } -size_t Aes128GcmEncrypter::GetNoncePrefixSize() const { +size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const { return kNoncePrefixSize; } -size_t Aes128GcmEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { +size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { return ciphertext_size - kAuthTagSize; } -// An AEAD_AES_128_GCM ciphertext is exactly 16 bytes longer than its +// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its // corresponding plaintext. -size_t Aes128GcmEncrypter::GetCiphertextSize(size_t plaintext_size) const { +size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const { return plaintext_size + kAuthTagSize; } -StringPiece Aes128GcmEncrypter::GetKey() const { +StringPiece Aes128Gcm12Encrypter::GetKey() const { return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); } -StringPiece Aes128GcmEncrypter::GetNoncePrefix() const { +StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const { return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); } diff --git a/net/quic/crypto/aes_128_gcm_encrypter_test.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc index 7e07ba9..0c9928b 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter_test.cc +++ b/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -245,7 +245,7 @@ namespace test { // EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing // in an nonce and also to allocate the buffer needed for the ciphertext. -QuicData* EncryptWithNonce(Aes128GcmEncrypter* encrypter, +QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter, StringPiece nonce, StringPiece associated_data, StringPiece plaintext) { @@ -260,8 +260,8 @@ QuicData* EncryptWithNonce(Aes128GcmEncrypter* encrypter, return new QuicData(ciphertext.release(), ciphertext_size, true); } -TEST(Aes128GcmEncrypterTest, Encrypt) { - if (!Aes128GcmEncrypter::IsSupported()) { +TEST(Aes128Gcm12EncrypterTest, Encrypt) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -304,7 +304,7 @@ TEST(Aes128GcmEncrypterTest, Encrypt) { EXPECT_EQ(test_info.pt_len, ct_len * 8); EXPECT_EQ(test_info.tag_len, tag_len * 8); - Aes128GcmEncrypter encrypter; + Aes128Gcm12Encrypter encrypter; ASSERT_TRUE(encrypter.SetKey(StringPiece(key, key_len))); scoped_ptr<QuicData> encrypted(EncryptWithNonce( &encrypter, StringPiece(iv, iv_len), @@ -313,6 +313,13 @@ TEST(Aes128GcmEncrypterTest, Encrypt) { // handle this case. StringPiece(aad_len ? aad : NULL, aad_len), StringPiece(pt, pt_len))); ASSERT_TRUE(encrypted.get()); + + // The test vectors have 16 byte authenticators but this code only uses + // the first 12. + ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize), + tag_len); + tag_len = Aes128Gcm12Encrypter::kAuthTagSize; + ASSERT_EQ(ct_len + tag_len, encrypted->length()); test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), ct_len, ct, ct_len); @@ -323,18 +330,18 @@ TEST(Aes128GcmEncrypterTest, Encrypt) { } } -TEST(Aes128GcmEncrypterTest, GetMaxPlaintextSize) { - Aes128GcmEncrypter encrypter; - EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016)); - EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116)); - EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26)); +TEST(Aes128Gcm12EncrypterTest, GetMaxPlaintextSize) { + Aes128Gcm12Encrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22)); } -TEST(Aes128GcmEncrypterTest, GetCiphertextSize) { - Aes128GcmEncrypter encrypter; - EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000)); - EXPECT_EQ(116u, encrypter.GetCiphertextSize(100)); - EXPECT_EQ(26u, encrypter.GetCiphertextSize(10)); +TEST(Aes128Gcm12EncrypterTest, GetCiphertextSize) { + Aes128Gcm12Encrypter encrypter; + EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(112u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(22u, encrypter.GetCiphertextSize(10)); } } // namespace test diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc index 4b3ce64..6307d82 100644 --- a/net/quic/crypto/crypto_framer.cc +++ b/net/quic/crypto/crypto_framer.cc @@ -160,26 +160,36 @@ bool CryptoFramer::ProcessInput(StringPiece input) { // static QuicData* CryptoFramer::ConstructHandshakeMessage( const CryptoHandshakeMessage& message) { - if (message.tag_value_map().size() > kMaxEntries) { - return NULL; + size_t num_entries = message.tag_value_map().size(); + size_t pad_length = 0; + bool need_pad_tag = false; + bool need_pad_value = false; + + size_t len = message.size(); + if (len < message.minimum_size()) { + need_pad_tag = true; + need_pad_value = true; + num_entries++; + + size_t delta = message.minimum_size() - len; + const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize; + if (delta > overhead) { + pad_length = delta - overhead; + } + len += overhead + pad_length; } - size_t len = kQuicTagSize; // message tag - len += sizeof(uint16); // number of map entries - len += sizeof(uint16); // padding. - QuicTagValueMap::const_iterator it = message.tag_value_map().begin(); - while (it != message.tag_value_map().end()) { - len += kQuicTagSize; // tag - len += kCryptoEndOffsetSize; // end offset - len += it->second.length(); // value - ++it; + + if (num_entries > kMaxEntries) { + return NULL; } + QuicDataWriter writer(len); if (!writer.WriteUInt32(message.tag())) { DCHECK(false) << "Failed to write message tag."; return NULL; } - if (!writer.WriteUInt16(message.tag_value_map().size())) { + if (!writer.WriteUInt16(num_entries)) { DCHECK(false) << "Failed to write size."; return NULL; } @@ -190,8 +200,23 @@ QuicData* CryptoFramer::ConstructHandshakeMessage( uint32 end_offset = 0; // Tags and offsets - for (it = message.tag_value_map().begin(); + for (QuicTagValueMap::const_iterator it = message.tag_value_map().begin(); it != message.tag_value_map().end(); ++it) { + if (it->first == kPAD && need_pad_tag) { + // Existing PAD tags are only checked when padding needs to be added + // because parts of the code may need to reserialize received messages + // and those messages may, legitimately include padding. + DCHECK(false) << "Message needed padding but already contained a PAD tag"; + return NULL; + } + + if (it->first > kPAD && need_pad_tag) { + need_pad_tag = false; + if (!WritePadTag(&writer, pad_length, &end_offset)) { + return NULL; + } + } + if (!writer.WriteUInt32(it->first)) { DCHECK(false) << "Failed to write tag."; return NULL; @@ -203,14 +228,36 @@ QuicData* CryptoFramer::ConstructHandshakeMessage( } } + if (need_pad_tag) { + if (!WritePadTag(&writer, pad_length, &end_offset)) { + return NULL; + } + } + // Values - for (it = message.tag_value_map().begin(); + for (QuicTagValueMap::const_iterator it = message.tag_value_map().begin(); it != message.tag_value_map().end(); ++it) { + if (it->first > kPAD && need_pad_value) { + need_pad_value = false; + if (!writer.WriteRepeatedByte('-', pad_length)) { + DCHECK(false) << "Failed to write padding."; + return NULL; + } + } + if (!writer.WriteBytes(it->second.data(), it->second.length())) { DCHECK(false) << "Failed to write value."; return NULL; } } + + if (need_pad_value) { + if (!writer.WriteRepeatedByte('-', pad_length)) { + DCHECK(false) << "Failed to write padding."; + return NULL; + } + } + return new QuicData(writer.take(), len, true); } @@ -221,4 +268,20 @@ void CryptoFramer::Clear() { state_ = STATE_READING_TAG; } +// static +bool CryptoFramer::WritePadTag(QuicDataWriter* writer, + size_t pad_length, + uint32* end_offset) { + if (!writer->WriteUInt32(kPAD)) { + DCHECK(false) << "Failed to write tag."; + return false; + } + *end_offset += pad_length; + if (!writer->WriteUInt32(*end_offset)) { + DCHECK(false) << "Failed to write end offset."; + return false; + } + return true; +} + } // namespace net diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h index 75cc405..ad1768d 100644 --- a/net/quic/crypto/crypto_framer.h +++ b/net/quic/crypto/crypto_framer.h @@ -20,8 +20,9 @@ namespace net { class CryptoFramer; -class QuicDataReader; class QuicData; +class QuicDataReader; +class QuicDataWriter; class NET_EXPORT_PRIVATE CryptoFramerVisitorInterface { public: @@ -75,6 +76,10 @@ class NET_EXPORT_PRIVATE CryptoFramer { // Clears per-message state. Does not clear the visitor. void Clear(); + static bool WritePadTag(QuicDataWriter* writer, + size_t pad_length, + uint32* end_offset); + void set_error(QuicErrorCode error) { error_ = error; } // Represents the current state of the parsing state machine. diff --git a/net/quic/crypto/crypto_framer_test.cc b/net/quic/crypto/crypto_framer_test.cc index ca4b92e..f33c6c7 100644 --- a/net/quic/crypto/crypto_framer_test.cc +++ b/net/quic/crypto/crypto_framer_test.cc @@ -171,6 +171,84 @@ TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) { EXPECT_TRUE(data.get() == NULL); } +TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSize) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x01020304, "test"); + message.set_minimum_size(64); + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 'P', 'A', 'D', 0, + // end offset 1 + 0x24, 0x00, 0x00, 0x00, + // tag 2 + 0x04, 0x03, 0x02, 0x01, + // end offset 2 + 0x28, 0x00, 0x00, 0x00, + // 36 bytes of padding. + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', + // value 2 + 't', 'e', 's', 't', + }; + + CryptoFramer framer; + scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + ASSERT_TRUE(data.get() != NULL); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + arraysize(packet)); +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSizePadLast) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + message.SetStringPiece(1, ""); + message.set_minimum_size(64); + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x01, 0x00, 0x00, 0x00, + // end offset 1 + 0x00, 0x00, 0x00, 0x00, + // tag 2 + 'P', 'A', 'D', 0, + // end offset 2 + 0x28, 0x00, 0x00, 0x00, + // 40 bytes of padding. + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', + }; + + CryptoFramer framer; + scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + ASSERT_TRUE(data.get() != NULL); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + arraysize(packet)); +} + TEST(CryptoFramerTest, ProcessInput) { test::TestCryptoVisitor visitor; CryptoFramer framer; diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 3585eb5..db66f4b 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -8,6 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" +#include "base/stringprintf.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "crypto/secure_hash.h" @@ -27,18 +28,22 @@ #include "net/quic/quic_utils.h" using base::StringPiece; +using base::StringPrintf; using std::map; using std::string; using std::vector; namespace net { -CryptoHandshakeMessage::CryptoHandshakeMessage() : tag_(0) {} +CryptoHandshakeMessage::CryptoHandshakeMessage() + : tag_(0), + minimum_size_(0) {} CryptoHandshakeMessage::CryptoHandshakeMessage( const CryptoHandshakeMessage& other) : tag_(other.tag_), - tag_value_map_(other.tag_value_map_) { + tag_value_map_(other.tag_value_map_), + minimum_size_(other.minimum_size_) { // Don't copy serialized_. scoped_ptr doesn't have a copy constructor. // The new object can reconstruct serialized_ lazily. } @@ -52,12 +57,14 @@ CryptoHandshakeMessage& CryptoHandshakeMessage::operator=( // Don't copy serialized_. scoped_ptr doesn't have an assignment operator. // However, invalidate serialized_. serialized_.reset(); + minimum_size_ = other.minimum_size_; return *this; } void CryptoHandshakeMessage::Clear() { tag_ = 0; tag_value_map_.clear(); + minimum_size_ = 0; serialized_.reset(); } @@ -196,6 +203,29 @@ QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag, return GetPOD(tag, out, sizeof(uint64)); } +size_t CryptoHandshakeMessage::size() const { + size_t ret = sizeof(QuicTag) + + sizeof(uint16) /* number of entries */ + + sizeof(uint16) /* padding */; + ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) * + tag_value_map_.size(); + for (QuicTagValueMap::const_iterator i = tag_value_map_.begin(); + i != tag_value_map_.end(); ++i) { + ret += i->second.size(); + } + + return ret; +} + +void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) { + serialized_.reset(); + minimum_size_ = min_bytes; +} + +size_t CryptoHandshakeMessage::minimum_size() const { + return minimum_size_; +} + string CryptoHandshakeMessage::DebugString() const { return DebugStringInternal(0); } @@ -269,6 +299,11 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { } } break; + case kPAD: + ret += StringPrintf("(%d bytes of padding)", + static_cast<int>(it->second.size())); + done = true; + break; } if (!done) { @@ -433,12 +468,11 @@ void QuicCryptoClientConfig::FillInchoateClientHello( QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out) const { out->set_tag(kCHLO); + out->set_minimum_size(kClientHelloMinimumSize); - // Server name indication. - // If server_hostname is not an IP address literal, it is a DNS hostname. - IPAddressNumber ip; - if (!server_hostname.empty() && - !ParseIPLiteralToNumber(server_hostname, &ip)) { + // Server name indication. We only send SNI if it's a valid domain name, as + // per the spec. + if (CryptoUtils::IsValidSNI(server_hostname)) { out->SetStringPiece(kSNI, server_hostname); } out->SetValue(kVERS, version); diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index 0774905..96a78a2 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -96,6 +96,21 @@ class NET_EXPORT_PRIVATE CryptoHandshakeMessage { QuicErrorCode GetUint32(QuicTag tag, uint32* out) const; QuicErrorCode GetUint64(QuicTag tag, uint64* out) const; + // size returns 4 (message tag) + 2 (uint16, number of entries) + + // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes. + size_t size() const; + + // set_minimum_size sets the minimum number of bytes that the message should + // consume. The CryptoFramer will add a PAD tag as needed when serializing in + // order to ensure this. Setting a value of 0 disables padding. + // + // Padding is useful in order to ensure that messages are a minimum size. A + // QUIC server can require a minimum size in order to reduce the + // amplification factor of any mirror DoS attack. + void set_minimum_size(size_t min_bytes); + + size_t minimum_size() const; + // DebugString returns a multi-line, string representation of the message // suitable for including in debug output. std::string DebugString() const; @@ -115,6 +130,8 @@ class NET_EXPORT_PRIVATE CryptoHandshakeMessage { QuicTag tag_; QuicTagValueMap tag_value_map_; + size_t minimum_size_; + // The serialized form of the handshake message. This member is constructed // lasily. mutable scoped_ptr<QuicData> serialized_; @@ -142,6 +159,8 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { CrypterPair initial_crypters; CrypterPair forward_secure_crypters; std::string server_config_id; + // Normalized SNI: converted to lower case and trailing '.' removed. + std::string sni; std::string client_nonce; std::string server_nonce; // hkdf_input_suffix contains the HKDF input following the label: the GUID, diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc index e2e45f3..b637db5 100644 --- a/net/quic/crypto/crypto_handshake_test.cc +++ b/net/quic/crypto/crypto_handshake_test.cc @@ -4,7 +4,7 @@ #include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_time.h" @@ -49,7 +49,7 @@ TEST(QuicCryptoServerConfigTest, ServerConfig) { } TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index c32884a..52f0dde 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -39,7 +39,7 @@ const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519 // AEAD algorithms const QuicTag kNULL = TAG('N', 'U', 'L', 'L'); // null algorithm -const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM +const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12 // Congestion control feedback types const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic @@ -72,6 +72,9 @@ const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry +// Universal tags +const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding + // These tags have a special form so that they appear either at the beginning // or the end of a handshake message. Since handshake messages are sorted by // tag value, the tags with 0 at the end will sort first and those with 255 at @@ -105,6 +108,12 @@ const size_t kOrbitSize = 8; // Number of bytes in an orbit value. // any cross-protocol attacks on the signature. const char kProofSignatureLabel[] = "QUIC server config signature"; +// kClientHelloMinimumSize is the minimum size of a client hello. Client hellos +// will have PAD tags added in order to ensure this minimum is met and client +// hellos smaller than this will be an error. This minimum size reduces the +// amplification factor of any mirror DoS attack. +const size_t kClientHelloMinimumSize = 512; + } // namespace net #endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc index d7eca6a..d766c20 100644 --- a/net/quic/crypto/crypto_server_config.cc +++ b/net/quic/crypto/crypto_server_config.cc @@ -4,11 +4,13 @@ #include "net/quic/crypto/crypto_server_config.h" +#include <stdlib.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/aes_128_gcm_12_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/cert_compressor.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_server_config_protobuf.h" @@ -51,8 +53,8 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( // 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), + source_address_token_encrypter_(new Aes128Gcm12Encrypter), + source_address_token_decrypter_(new Aes128Gcm12Decrypter), strike_register_max_entries_(1 << 10), strike_register_window_secs_(600), source_address_token_future_secs_(3600), @@ -295,7 +297,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( string* error_details) const { DCHECK(error_details); - CHECK(!configs_.empty()); + if (configs_.empty()) { + *error_details = "No configurations loaded"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } // FIXME(agl): we should use the client's SCID, not just the active config. map<ServerConfigID, Config*>::const_iterator it = @@ -306,6 +311,11 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( } const Config* const config(it->second); + if (client_hello.size() < kClientHelloMinimumSize) { + *error_details = "Client hello too small"; + return QUIC_CRYPTO_INVALID_VALUE_LENGTH; + } + const QuicWallTime now = clock->WallNow(); bool valid_source_address_token = false; StringPiece srct; @@ -351,7 +361,11 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( out->Clear(); StringPiece sni; - client_hello.GetStringPiece(kSNI, &sni); + if (client_hello.GetStringPiece(kSNI, &sni) && + !CryptoUtils::IsValidSNI(sni)) { + *error_details = "Invalid SNI name"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } StringPiece scid; if (!client_hello.GetStringPiece(kSCID, &scid) || @@ -458,6 +472,12 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( } params->server_config_id = scid.as_string(); + if (!sni.empty()) { + scoped_ptr<char[]> sni_tmp(new char[sni.length() + 1]); + memcpy(sni_tmp.get(), sni.data(), sni.length()); + sni_tmp[sni.length()] = 0; + params->sni = CryptoUtils::NormalizeHostname(sni_tmp.get()); + } string hkdf_suffix; const QuicData& client_hello_serialized = client_hello.GetSerialized(); diff --git a/net/quic/crypto/crypto_server_test.cc b/net/quic/crypto/crypto_server_test.cc new file mode 100644 index 0000000..7ab0efe --- /dev/null +++ b/net/quic/crypto/crypto_server_test.cc @@ -0,0 +1,148 @@ +// 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 "net/quic/crypto/quic_random.h" +#include "net/quic/test_tools/crypto_test_utils.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { + +class CryptoServerTest : public ::testing::Test { + public: + CryptoServerTest() + : rand_(QuicRandom::GetInstance()), + config_(QuicCryptoServerConfig::TESTING), + addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? + ip_ : IPAddressNumber(), 1) { + } + + virtual void SetUp() { + scoped_ptr<CryptoHandshakeMessage> msg( + config_.AddDefaultConfig(rand_, &clock_, 0)); + } + + void ShouldSucceed(const CryptoHandshakeMessage& message) { + string error_details; + QuicErrorCode error = config_.ProcessClientHello( + message, 1 /* GUID */, addr_, &clock_, + rand_, ¶ms_, &out_, &error_details); + + ASSERT_EQ(error, QUIC_NO_ERROR) + << "Message failed with error " << error_details << ": " + << message.DebugString(); + } + + void ShouldFailMentioning(const char* error_substr, + const CryptoHandshakeMessage& message) { + string error_details; + QuicErrorCode error = config_.ProcessClientHello( + message, 1 /* GUID */, addr_, &clock_, + rand_, ¶ms_, &out_, &error_details); + + ASSERT_NE(error, QUIC_NO_ERROR) + << "Message didn't fail: " << message.DebugString(); + + EXPECT_TRUE(error_details.find(error_substr) != string::npos) + << error_substr << " not in " << error_details; + } + + CryptoHandshakeMessage InchoateClientHello(const char* message_tag, ...) { + va_list ap; + va_start(ap, message_tag); + + CryptoHandshakeMessage message = + CryptoTestUtils::BuildMessage(message_tag, ap); + va_end(ap); + + message.SetStringPiece(kPAD, string(kClientHelloMinimumSize, '-')); + return message; + } + + private: + QuicRandom* const rand_; + MockClock clock_; + QuicCryptoServerConfig config_; + QuicCryptoNegotiatedParameters params_; + CryptoHandshakeMessage out_; + IPAddressNumber ip_; + IPEndPoint addr_; +}; + +TEST_F(CryptoServerTest, BadSNI) { + static const char* kBadSNIs[] = { + "", + "foo", + "#00", + "#ff00", + "127.0.0.1", + "ffee::1", + }; + + for (size_t i = 0; i < arraysize(kBadSNIs); i++) { + ShouldFailMentioning("SNI", InchoateClientHello( + "CHLO", + "SNI", kBadSNIs[i], + NULL)); + } +} + +TEST_F(CryptoServerTest, TooSmall) { + ShouldFailMentioning("too small", CryptoTestUtils::Message( + "CHLO", + NULL)); +} + +TEST_F(CryptoServerTest, BadSourceAddressToken) { + // Invalid source-address tokens should be ignored. + static const char* kBadSourceAddressTokens[] = { + "", + "foo", + "#0000", + "#0000000000000000000000000000000000000000", + }; + + for (size_t i = 0; i < arraysize(kBadSourceAddressTokens); i++) { + ShouldSucceed(InchoateClientHello( + "CHLO", + "STK", kBadSourceAddressTokens[i], + NULL)); + } +} + +TEST_F(CryptoServerTest, BadClientNonce) { + // Invalid nonces should be ignored. + static const char* kBadNonces[] = { + "", + "#0000", + "#0000000000000000000000000000000000000000", + }; + + for (size_t i = 0; i < arraysize(kBadNonces); i++) { + ShouldSucceed(InchoateClientHello( + "CHLO", + "NONC", kBadNonces[i], + NULL)); + } +} + +class CryptoServerTestNoConfig : public CryptoServerTest { + public: + virtual void SetUp() { + // Deliberately don't add a config so that we can test this situation. + } +}; + +TEST_F(CryptoServerTestNoConfig, DontCrash) { + ShouldFailMentioning("No config", InchoateClientHello( + "CHLO", + NULL)); +} + +} // namespace test +} // namespace net diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc index a95ac42..a5bed6a 100644 --- a/net/quic/crypto/crypto_utils.cc +++ b/net/quic/crypto/crypto_utils.cc @@ -5,6 +5,8 @@ #include "net/quic/crypto/crypto_utils.h" #include "crypto/hkdf.h" +#include "googleurl/src/url_canon.h" +#include "net/base/net_util.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" @@ -17,6 +19,7 @@ using std::string; namespace net { +// static void CryptoUtils::GenerateNonce(QuicWallTime now, QuicRandom* random_generator, StringPiece orbit, @@ -41,6 +44,39 @@ void CryptoUtils::GenerateNonce(QuicWallTime now, kNonceSize - bytes_written); } +// static +bool CryptoUtils::IsValidSNI(StringPiece sni) { + // TODO(rtenneti): Support RFC2396 hostname. + // NOTE: Microsoft does NOT enforce this spec, so if we throw away hostnames + // based on the above spec, we may be losing some hostnames that windows + // would consider valid. By far the most common hostname character NOT + // accepted by the above spec is '_'. + url_canon::CanonHostInfo host_info; + string canonicalized_host(CanonicalizeHost(sni.as_string(), &host_info)); + return !host_info.IsIPAddress() && + IsCanonicalizedHostCompliant(canonicalized_host, "") && + sni.find_last_of('.') != string::npos; +} + +// static +string CryptoUtils::NormalizeHostname(const char* hostname) { + url_canon::CanonHostInfo host_info; + string host(CanonicalizeHost(hostname, &host_info)); + + // Walk backwards over the string, skipping any trailing dots. + size_t host_end = host.length(); + while (host_end != 0 && host[host_end - 1] == '.') { + host_end--; + } + + // Erase the trailing dots. + if (host_end != host.length()) { + host.erase(host_end, host.length() - host_end); + } + return host; +} + +// static void CryptoUtils::DeriveKeys(StringPiece premaster_secret, QuicTag aead, StringPiece client_nonce, diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h index e516718..6dfce2a 100644 --- a/net/quic/crypto/crypto_utils.h +++ b/net/quic/crypto/crypto_utils.h @@ -13,6 +13,7 @@ #include "net/base/net_export.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" namespace net { @@ -37,6 +38,17 @@ class NET_EXPORT_PRIVATE CryptoUtils { base::StringPiece orbit, std::string* nonce); + // Returns true if the sni is valid, false otherwise. + // (1) disallow IP addresses; + // (2) check that the hostname contains valid characters only; and + // (3) contains at least one dot. + static bool IsValidSNI(base::StringPiece sni); + + // Convert hostname to lowercase and remove the trailing '.'. + // Returns |hostname|. NormalizeHostname() doesn't support IP address + // literals. IsValidSNI() should be called before calling NormalizeHostname(). + static std::string NormalizeHostname(const char* hostname); + // DeriveKeys populates |out->encrypter| and |out->decrypter| given the // contents of |premaster_secret|, |client_nonce|, |server_nonce| and // |hkdf_input|. |aead| determines which cipher will be used. |perspective| diff --git a/net/quic/crypto/crypto_utils_test.cc b/net/quic/crypto/crypto_utils_test.cc new file mode 100644 index 0000000..17eb192 --- /dev/null +++ b/net/quic/crypto/crypto_utils_test.cc @@ -0,0 +1,49 @@ +// 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_utils.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { +namespace { + +TEST(CryptoUtilsTest, IsValidSNI) { + // IP as SNI. + EXPECT_FALSE(CryptoUtils::IsValidSNI("192.168.0.1")); + // SNI without any dot. + EXPECT_FALSE(CryptoUtils::IsValidSNI("somedomain")); + // Invalid RFC2396 hostname + // TODO(rtenneti): Support RFC2396 hostname. + // EXPECT_FALSE(CryptoUtils::IsValidSNI("some_domain.com")); + // An empty string must be invalid otherwise the QUIC client will try sending + // it. + EXPECT_FALSE(CryptoUtils::IsValidSNI("")); + + // Valid SNI + EXPECT_TRUE(CryptoUtils::IsValidSNI("test.google.com")); +} + +TEST(CryptoUtilsTest, NormalizeHostname) { + struct { + const char *input, *expected; + } tests[] = { + { "www.google.com", "www.google.com", }, + { "WWW.GOOGLE.COM", "www.google.com", }, + { "www.google.com.", "www.google.com", }, + { "www.google.COM.", "www.google.com", }, + { "www.google.com..", "www.google.com", }, + { "www.google.com........", "www.google.com", }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + EXPECT_EQ(std::string(tests[i].expected), + CryptoUtils::NormalizeHostname(tests[i].input)); + } +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/crypto/quic_decrypter.cc b/net/quic/crypto/quic_decrypter.cc index 2eafdc3..fb19d4c 100644 --- a/net/quic/crypto/quic_decrypter.cc +++ b/net/quic/crypto/quic_decrypter.cc @@ -4,7 +4,7 @@ #include "net/quic/crypto/quic_decrypter.h" -#include "net/quic/crypto/aes_128_gcm_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_decrypter.h" #include "net/quic/crypto/null_decrypter.h" namespace net { @@ -13,7 +13,7 @@ namespace net { QuicDecrypter* QuicDecrypter::Create(QuicTag algorithm) { switch (algorithm) { case kAESG: - return new Aes128GcmDecrypter(); + return new Aes128Gcm12Decrypter(); case kNULL: return new NullDecrypter(); default: diff --git a/net/quic/crypto/quic_encrypter.cc b/net/quic/crypto/quic_encrypter.cc index 2e2b83d..489da8e 100644 --- a/net/quic/crypto/quic_encrypter.cc +++ b/net/quic/crypto/quic_encrypter.cc @@ -4,7 +4,7 @@ #include "net/quic/crypto/quic_encrypter.h" -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/null_encrypter.h" namespace net { @@ -13,7 +13,7 @@ namespace net { QuicEncrypter* QuicEncrypter::Create(QuicTag algorithm) { switch (algorithm) { case kAESG: - return new Aes128GcmEncrypter(); + return new Aes128Gcm12Encrypter(); case kNULL: return new NullEncrypter(); default: diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index 591da0a..4f90b3e 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -8,7 +8,7 @@ #include "net/base/capturing_net_log.h" #include "net/base/test_completion_callback.h" -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" @@ -56,7 +56,7 @@ class QuicClientSessionTest : public ::testing::Test { }; TEST_F(QuicClientSessionTest, CryptoConnect) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -65,7 +65,7 @@ TEST_F(QuicClientSessionTest, CryptoConnect) { } TEST_F(QuicClientSessionTest, MaxNumConnections) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -86,7 +86,7 @@ TEST_F(QuicClientSessionTest, MaxNumConnections) { } TEST_F(QuicClientSessionTest, GoAwayReceived) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 51319ff..e75bc1a7 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -148,11 +148,6 @@ void QuicConnection::OnError(QuicFramer* framer) { } void QuicConnection::OnPacket() { - // TODO(satyamshekhar): Validate packet before updating the time - // since it affects the timeout of the connection. - time_of_last_received_packet_ = clock_->Now(); - DVLOG(1) << "time of last received packet: " - << time_of_last_received_packet_.ToDebuggingValue(); } void QuicConnection::OnPublicResetPacket( @@ -164,15 +159,9 @@ void QuicConnection::OnPublicResetPacket( } bool QuicConnection::OnProtocolVersionMismatch(QuicTag received_version) { - if (address_migrating_) { - SendConnectionCloseWithDetails( - QUIC_ERROR_MIGRATING_ADDRESS, - "Address migration is not yet a supported feature"); - } - // TODO(satyamshekhar): Implement no server state in this mode. if (!is_server_) { - LOG(DFATAL) << "Framer called OnProtocolVersionMismatch for server. " + LOG(DFATAL) << ENDPOINT << "Framer called OnProtocolVersionMismatch. " << "Closing connection."; CloseConnection(QUIC_INTERNAL_ERROR, false); return false; @@ -221,14 +210,9 @@ bool QuicConnection::OnProtocolVersionMismatch(QuicTag received_version) { // Handles version negotiation for client connection. void QuicConnection::OnVersionNegotiationPacket( const QuicVersionNegotiationPacket& packet) { - if (address_migrating_) { - SendConnectionCloseWithDetails( - QUIC_ERROR_MIGRATING_ADDRESS, - "Address migration is not yet a supported feature"); - } if (is_server_) { - LOG(DFATAL) << "Framer parsed VersionNegotiationPacket for server." - << "Closing connection."; + LOG(DFATAL) << ENDPOINT << "Framer parsed VersionNegotiationPacket." + << " Closing connection."; CloseConnection(QUIC_INTERNAL_ERROR, false); return; } @@ -244,8 +228,8 @@ void QuicConnection::OnVersionNegotiationPacket( if (std::find(packet.versions.begin(), packet.versions.end(), quic_version_) != packet.versions.end()) { - DLOG(WARNING) << "The server already supports our version. It should have " - << "accepted our connection."; + DLOG(WARNING) << ENDPOINT << "The server already supports our version. " + << "It should have accepted our connection."; // Just drop the connection. CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, false); return; @@ -268,10 +252,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { debug_visitor_->OnPacketHeader(header); } - if (address_migrating_) { - SendConnectionCloseWithDetails( - QUIC_ERROR_MIGRATING_ADDRESS, - "Address migration is not yet a supported feature"); + if (!ProcessValidatedPacket()) { return false; } @@ -326,7 +307,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { DCHECK_EQ(NEGOTIATED_VERSION, version_negotiation_state_); --stats_.packets_dropped; - DVLOG(1) << "Received packet header: " << header; + DVLOG(1) << ENDPOINT << "Received packet header: " << header; last_header_ = header; return true; } @@ -353,7 +334,7 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { if (debug_visitor_) { debug_visitor_->OnAckFrame(incoming_ack); } - DVLOG(1) << "OnAckFrame: " << incoming_ack; + DVLOG(1) << ENDPOINT << "OnAckFrame: " << incoming_ack; if (last_header_.packet_sequence_number <= largest_seen_packet_with_ack_) { DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; @@ -506,7 +487,7 @@ void QuicConnection::UpdatePacketInformationReceivedByPeer( RetransmittableFrames* unacked = it->second; if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) { // Packet was acked, so remove it from our unacked packet list. - DVLOG(1) << "Got an ack for " << sequence_number; + DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number; acked_packets.insert(sequence_number); delete unacked; UnackedPacketMap::iterator it_tmp = it; @@ -517,7 +498,7 @@ void QuicConnection::UpdatePacketInformationReceivedByPeer( // This is a packet which we planned on retransmitting and has not been // seen at the time of this ack being sent out. See if it's our new // lowest unacked packet. - DVLOG(1) << "still missing " << sequence_number; + DVLOG(1) << ENDPOINT << "still missing packet " << sequence_number; ++it; // The peer got packets after this sequence number. This is an explicit // nack. @@ -528,7 +509,8 @@ void QuicConnection::UpdatePacketInformationReceivedByPeer( kNumberOfNacksBeforeRetransmission && retransmitted_packets < kMaxRetransmissionsPerAck) { ++retransmitted_packets; - DVLOG(1) << "Trying to retransmit packet " << sequence_number + DVLOG(1) << ENDPOINT << "Trying to retransmit packet " + << sequence_number << " as it has been nacked 3 or more times."; // TODO(satyamshekhar): save in a vector and retransmit after the // loop. @@ -562,7 +544,7 @@ void QuicConnection::UpdatePacketInformationSentByPeer( DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked); if (missed_packets || incoming_ack.sent_info.least_unacked > outgoing_ack_.received_info.largest_observed + 1) { - DVLOG(1) << "Updating entropy hashed since we missed packets"; + DVLOG(1) << ENDPOINT << "Updating entropy hashed since we missed packets"; // There were some missing packets that we won't ever get now. Recalculate // the received entropy hash. entropy_manager_.RecalculateReceivedEntropyHash( @@ -596,7 +578,7 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { if (debug_visitor_) { debug_visitor_->OnRstStreamFrame(frame); } - DLOG(INFO) << "Stream reset with error " + DLOG(INFO) << ENDPOINT << "Stream reset with error " << QuicUtils::StreamErrorToString(frame.error_code); visitor_->OnRstStream(frame); return connected_; @@ -778,6 +760,19 @@ bool QuicConnection::OnCanWrite() { return !write_blocked_; } +bool QuicConnection::ProcessValidatedPacket() { + if (address_migrating_) { + SendConnectionCloseWithDetails( + QUIC_ERROR_MIGRATING_ADDRESS, + "Address migration is not yet a supported feature"); + return false; + } + time_of_last_received_packet_ = clock_->Now(); + DVLOG(1) << ENDPOINT << "time of last received packet: " + << time_of_last_received_packet_.ToDebuggingValue(); + return true; +} + bool QuicConnection::WriteQueuedPackets() { DCHECK(!write_blocked_); @@ -821,7 +816,8 @@ void QuicConnection::RecordPacketReceived(const QuicPacketHeader& header) { header.packet_sequence_number) { // We've gotten one of the out of order packets - remove it from our // "missing packets" list. - DVLOG(1) << "Removing " << sequence_number << " from missing list"; + DVLOG(1) << ENDPOINT << "Removing " << sequence_number + << " from missing list"; outgoing_ack_.received_info.missing_packets.erase(sequence_number); } if (header.packet_sequence_number > @@ -840,7 +836,7 @@ bool QuicConnection::MaybeRetransmitPacketForRTO( ContainsKey(retransmission_map_, sequence_number)); if (!ContainsKey(unacked_packets_, sequence_number)) { - DVLOG(2) << "alarm fired for " << sequence_number + DVLOG(2) << ENDPOINT << "alarm fired for " << sequence_number << " but it has been acked or already retransmitted with" << " different sequence number."; // So no extra delay is added for this packet. @@ -903,8 +899,8 @@ void QuicConnection::RetransmitPacket( // Remove info with old sequence number. unacked_packets_.erase(unacked_it); retransmission_map_.erase(retransmission_it); - DVLOG(1) << "Retransmitting unacked packet " << sequence_number << " as " - << serialized_packet.sequence_number; + DVLOG(1) << ENDPOINT << "Retransmitting unacked packet " << sequence_number + << " as " << serialized_packet.sequence_number; DCHECK(unacked_packets_.empty() || unacked_packets_.rbegin()->first < serialized_packet.sequence_number); unacked_packets_.insert(make_pair(serialized_packet.sequence_number, @@ -950,7 +946,7 @@ void QuicConnection::MaybeSetupRetransmission( QuicPacketSequenceNumber sequence_number) { RetransmissionMap::iterator it = retransmission_map_.find(sequence_number); if (it == retransmission_map_.end()) { - DVLOG(1) << "Will not retransmit packet " << sequence_number; + DVLOG(1) << ENDPOINT << "Will not retransmit packet " << sequence_number; return; } @@ -1000,7 +996,12 @@ bool QuicConnection::WritePacket(EncryptionLevel level, << " : " << (packet->is_fec_packet() ? "FEC " : (retransmittable == HAS_RETRANSMITTABLE_DATA ? "data bearing " : " ack only ")) - << " Packet length:" << packet->length(); + << ", encryption level: " + << QuicUtils::EncryptionLevelToString(level) + << ", length:" << packet->length(); + // TODO(rtenneti): Get StringToHexASCIIDump of packet. + DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl + << packet->AsStringPiece(); DCHECK(encrypted->length() <= kMaxPacketSize) << "Packet " << sequence_number << " will not be read; too large: " @@ -1024,8 +1025,11 @@ bool QuicConnection::WritePacket(EncryptionLevel level, CloseConnection(QUIC_PACKET_WRITE_ERROR, false); return false; } - time_of_last_sent_packet_ = now; - DVLOG(1) << "time of last sent packet: " << now.ToDebuggingValue(); + if (!retransmission) { + time_of_last_sent_packet_ = now; + } + DVLOG(1) << ENDPOINT << "time of last sent packet: " + << now.ToDebuggingValue(); // Set the retransmit alarm only when we have sent the packet to the client // and not when it goes to the pending queue, otherwise we will end up adding @@ -1117,7 +1121,7 @@ void QuicConnection::UpdateOutgoingAck() { void QuicConnection::SendAck() { helper_->ClearAckAlarm(); UpdateOutgoingAck(); - DVLOG(1) << "Sending ack: " << outgoing_ack_; + DVLOG(1) << ENDPOINT << "Sending ack: " << outgoing_ack_; // TODO(rch): delay this until the CreateFeedbackFrame // method is invoked. This requires changes SetShouldSendAck @@ -1125,7 +1129,8 @@ void QuicConnection::SendAck() { bool send_feedback = false; if (congestion_manager_.GenerateCongestionFeedback( &outgoing_congestion_feedback_)) { - DVLOG(1) << "Sending feedback " << outgoing_congestion_feedback_; + DVLOG(1) << ENDPOINT << "Sending feedback " + << outgoing_congestion_feedback_; send_feedback = true; } @@ -1338,7 +1343,8 @@ bool QuicConnection::CheckForTimeout() { time_of_last_sent_packet_); QuicTime::Delta delta = now.Subtract(time_of_last_packet); - DVLOG(1) << "last packet " << time_of_last_packet.ToDebuggingValue() + DVLOG(1) << ENDPOINT << "last packet " + << time_of_last_packet.ToDebuggingValue() << " now:" << now.ToDebuggingValue() << " delta:" << delta.ToMicroseconds(); if (delta >= timeout_) { diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index dab64ee..f3096ad 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -257,6 +257,11 @@ class NET_EXPORT_PRIVATE QuicConnection // queued writes to happen. Returns false if the socket has become blocked. virtual bool OnCanWrite() OVERRIDE; + // Do any work which logically would be done in OnPacket but can not be + // safely done until the packet is validated. Returns true if the packet + // can be handled, false otherwise. + bool ProcessValidatedPacket(); + QuicTag version() const { return quic_version_; } // From QuicFramerVisitorInterface diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index b761aaf..cf28e46 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -73,7 +73,7 @@ class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm); }; -// TaggingEncrypter appends 16 bytes of |tag| to the end of each message. +// TaggingEncrypter appends kTagSize bytes of |tag| to the end of each message. class TaggingEncrypter : public QuicEncrypter { public: explicit TaggingEncrypter(uint8 tag) @@ -128,14 +128,14 @@ class TaggingEncrypter : public QuicEncrypter { private: enum { - kTagSize = 16, + kTagSize = 12, }; const uint8 tag_; }; -// TaggingDecrypter ensures that the final 16 bytes of the message all have the -// same value and then removes them. +// TaggingDecrypter ensures that the final kTagSize bytes of the message all +// have the same value and then removes them. class TaggingDecrypter : public QuicDecrypter { public: virtual ~TaggingDecrypter() {} @@ -183,7 +183,7 @@ class TaggingDecrypter : public QuicDecrypter { private: enum { - kTagSize = 16, + kTagSize = 12, }; bool CheckTag(StringPiece ciphertext) { @@ -1343,8 +1343,8 @@ TEST_F(QuicConnectionTest, RetransmitWithSameEncryptionLevel) { kDefaultRetransmissionTime); use_tagging_decrypter(); - // A TaggingEncrypter puts 16 copies of the given byte (0x01 here) at the end - // of the packet. We can test this to check which encrypter was used. + // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at + // the end of the packet. We can test this to check which encrypter was used. connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); EXPECT_EQ(0x01010101u, final_bytes_of_last_packet()); diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index 90b9122..160aaf9 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -5,7 +5,7 @@ #include "net/quic/quic_crypto_client_stream.h" #include "base/memory/scoped_ptr.h" -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_protocol.h" @@ -19,7 +19,7 @@ namespace net { namespace test { namespace { -const char kServerHostname[] = "localhost"; +const char kServerHostname[] = "example.com"; class TestQuicVisitor : public NoOpFramerVisitor { public: @@ -52,15 +52,16 @@ class QuicCryptoClientStreamTest : public ::testing::Test { : addr_(), connection_(new PacketSavingConnection(1, addr_, true)), session_(connection_, QuicConfig(), true), - stream_(kServerHostname, &session_, &crypto_config_) { - session_.SetCryptoStream(&stream_); + stream_(new QuicCryptoClientStream(kServerHostname, &session_, + &crypto_config_)) { + session_.SetCryptoStream(stream_.get()); session_.config()->SetDefaults(); crypto_config_.SetDefaults(); } void CompleteCryptoHandshake() { - EXPECT_TRUE(stream_.CryptoConnect()); - CryptoTestUtils::HandshakeWithFakeServer(connection_, &stream_); + EXPECT_TRUE(stream_->CryptoConnect()); + CryptoTestUtils::HandshakeWithFakeServer(connection_, stream_.get()); } void ConstructHandshakeMessage() { @@ -71,35 +72,35 @@ class QuicCryptoClientStreamTest : public ::testing::Test { IPEndPoint addr_; PacketSavingConnection* connection_; TestSession session_; - QuicCryptoClientStream stream_; + scoped_ptr<QuicCryptoClientStream> stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; QuicCryptoClientConfig crypto_config_; }; TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } - EXPECT_FALSE(stream_.encryption_established()); - EXPECT_FALSE(stream_.handshake_confirmed()); + EXPECT_FALSE(stream_->encryption_established()); + EXPECT_FALSE(stream_->handshake_confirmed()); } TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } CompleteCryptoHandshake(); - EXPECT_TRUE(stream_.encryption_established()); - EXPECT_TRUE(stream_.handshake_confirmed()); + EXPECT_TRUE(stream_->encryption_established()); + EXPECT_TRUE(stream_->handshake_confirmed()); } TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -110,27 +111,27 @@ TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); message_.set_tag(kCHLO); ConstructHandshakeMessage(); - stream_.ProcessData(message_data_->data(), message_data_->length()); + stream_->ProcessData(message_data_->data(), message_data_->length()); } TEST_F(QuicCryptoClientStreamTest, BadMessageType) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } - EXPECT_TRUE(stream_.CryptoConnect()); + EXPECT_TRUE(stream_->CryptoConnect()); message_.set_tag(kCHLO); ConstructHandshakeMessage(); EXPECT_CALL(*connection_, SendConnectionCloseWithDetails( QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "Expected REJ")); - stream_.ProcessData(message_data_->data(), message_data_->length()); + stream_->ProcessData(message_data_->data(), message_data_->length()); } TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -146,11 +147,26 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { EXPECT_EQ(0, config->keepalive_timeout().ToSeconds()); const QuicCryptoNegotiatedParameters& crypto_params( - stream_.crypto_negotiated_params()); + stream_->crypto_negotiated_params()); EXPECT_EQ(kAESG, crypto_params.aead); EXPECT_EQ(kC255, crypto_params.key_exchange); } +TEST_F(QuicCryptoClientStreamTest, InvalidHostname) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + stream_.reset(new QuicCryptoClientStream("invalid", &session_, + &crypto_config_)); + session_.SetCryptoStream(stream_.get()); + + CompleteCryptoHandshake(); + EXPECT_TRUE(stream_->encryption_established()); + EXPECT_TRUE(stream_->handshake_confirmed()); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index c87c6d5..6aef276 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -44,7 +44,6 @@ void QuicCryptoServerStream::OnHandshakeMessage( session()->connection()->clock(), session()->connection()->random_generator(), &crypto_negotiated_params_, &reply, &error_details); - if (error != QUIC_NO_ERROR) { CloseConnectionWithDetails(error, error_details); return; diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index 686bca7..2db0881 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -8,7 +8,7 @@ #include <vector> #include "base/memory/scoped_ptr.h" -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" @@ -103,7 +103,7 @@ class QuicCryptoServerStreamTest : public ::testing::Test { }; TEST_F(QuicCryptoServerStreamTest, NotInitiallyConected) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -113,7 +113,7 @@ TEST_F(QuicCryptoServerStreamTest, NotInitiallyConected) { } TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -132,7 +132,7 @@ TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { } TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -186,7 +186,7 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { // 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); + reinterpret_cast<MockRandom*>(client_conn->random_generator())->ChangeValue(); client_session.reset(new TestSession(client_conn, client_config, false)); server_session.reset(new TestSession(server_conn, config_, true)); client.reset(new QuicCryptoClientStream( @@ -205,7 +205,7 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { } TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -219,7 +219,7 @@ TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) { } TEST_F(QuicCryptoServerStreamTest, BadMessageType) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -232,7 +232,7 @@ TEST_F(QuicCryptoServerStreamTest, BadMessageType) { } TEST_F(QuicCryptoServerStreamTest, WithoutCertificates) { - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } diff --git a/net/quic/quic_data_writer.cc b/net/quic/quic_data_writer.cc index 4182d9a..e52cd03 100644 --- a/net/quic/quic_data_writer.cc +++ b/net/quic/quic_data_writer.cc @@ -98,6 +98,18 @@ bool QuicDataWriter::WriteBytes(const void* data, size_t data_len) { return true; } +bool QuicDataWriter::WriteRepeatedByte(uint8 byte, size_t count) { + char* dest = BeginWrite(count); + if (!dest) { + return false; + } + + memset(dest, byte, count); + + length_ += count; + return true; +} + void QuicDataWriter::WritePadding() { DCHECK_LE(length_, capacity_); if (length_ > capacity_) { diff --git a/net/quic/quic_data_writer.h b/net/quic/quic_data_writer.h index 5ecd628..f3408d1 100644 --- a/net/quic/quic_data_writer.h +++ b/net/quic/quic_data_writer.h @@ -46,6 +46,7 @@ class NET_EXPORT_PRIVATE QuicDataWriter { bool WriteUInt128(uint128 value); bool WriteStringPiece16(base::StringPiece val); bool WriteBytes(const void* data, size_t data_len); + bool WriteRepeatedByte(uint8 byte, size_t count); // Fills the remaining buffer with null characters. void WritePadding(); diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 0e56382..e900e2c 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -105,6 +105,8 @@ size_t QuicFramer::GetMinGoAwayFrameSize() { // static // TODO(satyamshekhar): 16 - Crypto hash for integrity. Not a static value. Use // QuicEncrypter::GetMaxPlaintextSize. +// 16 is a conservative estimate in the case of AEAD_AES_128_GCM_12, which uses +// 12-byte tags. size_t QuicFramer::GetMaxUnackedPackets(bool include_version) { return (kMaxPacketSize - GetPacketHeaderSize(include_version) - GetMinAckFrameSize() - 16) / kSequenceNumberSize; @@ -115,7 +117,7 @@ bool QuicFramer::IsSupportedVersion(QuicTag version) { } size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { - return kQuicGuidSize + kPublicFlagsSize + + return kPublicFlagsSize + kQuicGuidSize + number_versions * kQuicVersionSize; } @@ -274,12 +276,12 @@ QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket( size_t len = GetPublicResetPacketSize(); QuicDataWriter writer(len); - if (!writer.WriteUInt64(packet.public_header.guid)) { + uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST); + if (!writer.WriteUInt8(flags)) { return NULL; } - uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST); - if (!writer.WriteUInt8(flags)) { + if (!writer.WriteUInt64(packet.public_header.guid)) { return NULL; } @@ -302,12 +304,12 @@ QuicEncryptedPacket* QuicFramer::ConstructVersionNegotiationPacket( size_t len = GetVersionNegotiationPacketSize(supported_versions.size()); QuicDataWriter writer(len); - if (!writer.WriteUInt64(header.guid)) { + uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_VERSION); + if (!writer.WriteUInt8(flags)) { return NULL; } - uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_VERSION); - if (!writer.WriteUInt8(flags)) { + if (!writer.WriteUInt64(header.guid)) { return NULL; } @@ -463,10 +465,6 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, QuicDataWriter* writer) { - if (!writer->WriteUInt64(header.public_header.guid)) { - return false; - } - uint8 flags = 0; if (header.public_header.reset_flag) { flags |= PACKET_PUBLIC_FLAGS_RST; @@ -478,6 +476,10 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, return false; } + if (!writer->WriteUInt64(header.public_header.guid)) { + return false; + } + if (header.public_header.version_flag) { DCHECK(!is_server_); writer->WriteUInt32(quic_version_); @@ -538,11 +540,6 @@ QuicPacketSequenceNumber QuicFramer::CalculatePacketSequenceNumberFromWire( } bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { - if (!reader_->ReadUInt64(&public_header->guid)) { - set_detailed_error("Unable to read GUID."); - return false; - } - uint8 public_flags; if (!reader_->ReadBytes(&public_flags, 1)) { set_detailed_error("Unable to read public flags."); @@ -563,6 +560,11 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { return false; } + if (!reader_->ReadUInt64(&public_header->guid)) { + set_detailed_error("Unable to read GUID."); + return false; + } + if (public_header->version_flag && is_server_) { QuicTag version; if (!reader_->ReadUInt32(&version)) { @@ -579,7 +581,13 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { // static bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, QuicGuid* guid) { + // TODO(ianswett): In the next CL, the flags will be used for guid length. QuicDataReader reader(packet.data(), packet.length()); + uint8 public_flags; + if (!reader.ReadBytes(&public_flags, 1)) { + return false; + } + return reader.ReadUInt64(guid); } @@ -1115,9 +1123,9 @@ QuicEncryptedPacket* QuicFramer::EncryptPacket( size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) { // In order to keep the code simple, we don't have the current encryption - // level to hand. At the moment, all AEADs have a tag-length of 16 bytes so - // that doesn't matter but we take the minimum plaintext length just to be - // safe. + // level to hand. At the moment, the NullEncrypter has a tag length of 16 + // bytes and AES-GCM has a tag length of 12. We take the minimum plaintext + // length just to be safe. size_t min_plaintext_size = ciphertext_size; for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) { diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 701e2bb..d75d9a6 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -36,16 +36,16 @@ namespace test { const QuicPacketSequenceNumber kEpoch = GG_UINT64_C(1) << 48; const QuicPacketSequenceNumber kMask = kEpoch - 1; -// Index into the guid offset in the header. -const size_t kGuidOffset = 0; // Index into the flags offset in the header. -const size_t kPublicFlagsOffset = kQuicGuidSize; +const size_t kPublicFlagsOffset = 0; +// Index into the guid offset in the header. +const size_t kGuidOffset = kPublicFlagsSize; // Index into the version string in the header. (if present). -const size_t kVersionOffset = kPublicFlagsOffset + kPublicFlagsSize; +const size_t kVersionOffset = kGuidOffset + kQuicGuidSize; // Index into the sequence number offset in the header. size_t GetSequenceNumberOffset(bool include_version) { - return kPublicFlagsOffset + kPublicFlagsSize + + return kGuidOffset + kQuicGuidSize + (include_version ? kQuicVersionSize : 0); } @@ -60,8 +60,7 @@ size_t GetFecGroupOffset(bool include_version) { } // Index into the nonce proof of the public reset packet. -const size_t kPublicResetPacketNonceProofOffset = - kPublicFlagsOffset + kPublicFlagsSize; +const size_t kPublicResetPacketNonceProofOffset = kGuidOffset + kQuicGuidSize; // Index into the rejected sequence number of the public reset packet. const size_t kPublicResetPacketRejectedSequenceNumberOffset = kPublicResetPacketNonceProofOffset + kPublicResetNonceSize; @@ -486,11 +485,11 @@ TEST_F(QuicFramerTest, EmptyPacket) { TEST_F(QuicFramerTest, LargePacket) { unsigned char packet[kMaxPacketSize + 1] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -516,11 +515,11 @@ TEST_F(QuicFramerTest, LargePacket) { TEST_F(QuicFramerTest, PacketHeader) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -549,10 +548,10 @@ TEST_F(QuicFramerTest, PacketHeader) { // Now test framing boundaries for (size_t i = 0; i < GetPacketHeaderSize(!kIncludeVersion); ++i) { string expected_error; - if (i < kPublicFlagsOffset) { - expected_error = "Unable to read GUID."; - } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { + if (i < kGuidOffset) { expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { + expected_error = "Unable to read GUID."; } else if (i < GetPrivateFlagsOffset(!kIncludeVersion)) { expected_error = "Unable to read sequence number."; } else if (i < GetFecGroupOffset(!kIncludeVersion)) { @@ -566,13 +565,13 @@ TEST_F(QuicFramerTest, PacketHeader) { TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { unsigned char packet[] = { + // public flags (version) + 0x01, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags (version) - 0x01, // version tag - 'Q', '0', '0', '2', + 'Q', '0', '0', '3', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -602,10 +601,10 @@ TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { // Now test framing boundaries for (size_t i = 0; i < GetPacketHeaderSize(kIncludeVersion); ++i) { string expected_error; - if (i < kPublicFlagsOffset) { - expected_error = "Unable to read GUID."; - } else if (i < kVersionOffset) { + if (i < kGuidOffset) { expected_error = "Unable to read public flags."; + } else if (i < kVersionOffset) { + expected_error = "Unable to read GUID."; } else if (i < GetSequenceNumberOffset(kIncludeVersion)) { expected_error = "Unable to read protocol version."; } else if (i < GetPrivateFlagsOffset(kIncludeVersion)) { @@ -622,11 +621,11 @@ TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { TEST_F(QuicFramerTest, InvalidPublicFlag) { unsigned char packet[] = { + // public flags + 0x07, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x07, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -661,11 +660,11 @@ TEST_F(QuicFramerTest, InvalidPublicFlag) { TEST_F(QuicFramerTest, InvalidPrivateFlag) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -700,11 +699,11 @@ TEST_F(QuicFramerTest, InvalidPrivateFlag) { TEST_F(QuicFramerTest, PaddingFrame) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -742,11 +741,11 @@ TEST_F(QuicFramerTest, PaddingFrame) { TEST_F(QuicFramerTest, StreamFrame) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -811,13 +810,13 @@ TEST_F(QuicFramerTest, StreamFrame) { TEST_F(QuicFramerTest, StreamFrameWithVersion) { unsigned char packet[] = { + // public flags (version) + 0x01, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags (version) - 0x01, // version tag - 'Q', '0', '0', '2', + 'Q', '0', '0', '3', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -886,11 +885,11 @@ TEST_F(QuicFramerTest, RejectPacket) { visitor_.accept_packet_ = false; unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -988,11 +987,11 @@ TEST_F(QuicFramerTest, RevivedStreamFrame) { TEST_F(QuicFramerTest, StreamFrameInFecGroup) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x12, 0x34, @@ -1042,11 +1041,11 @@ TEST_F(QuicFramerTest, StreamFrameInFecGroup) { TEST_F(QuicFramerTest, AckFrame) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1136,11 +1135,11 @@ TEST_F(QuicFramerTest, AckFrame) { TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1194,11 +1193,11 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1291,11 +1290,11 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1345,11 +1344,11 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { TEST_F(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1372,11 +1371,11 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { TEST_F(QuicFramerTest, RstStreamFrame) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1430,11 +1429,11 @@ TEST_F(QuicFramerTest, RstStreamFrame) { TEST_F(QuicFramerTest, ConnectionCloseFrame) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1516,11 +1515,11 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { TEST_F(QuicFramerTest, GoAwayFrame) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1576,11 +1575,11 @@ TEST_F(QuicFramerTest, GoAwayFrame) { TEST_F(QuicFramerTest, PublicResetPacket) { unsigned char packet[] = { + // public flags (public reset) + 0x02, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags (public reset) - 0x02, // nonce proof 0x89, 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, @@ -1606,12 +1605,12 @@ TEST_F(QuicFramerTest, PublicResetPacket) { for (size_t i = 0; i < GetPublicResetPacketSize(); ++i) { string expected_error; DLOG(INFO) << "iteration: " << i; - if (i < kPublicFlagsOffset) { - expected_error = "Unable to read GUID."; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); } else if (i < kPublicResetPacketNonceProofOffset) { - expected_error = "Unable to read public flags."; + expected_error = "Unable to read GUID."; CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); } else if (i < kPublicResetPacketRejectedSequenceNumberOffset) { @@ -1628,12 +1627,13 @@ TEST_F(QuicFramerTest, PublicResetPacket) { TEST_F(QuicFramerTest, VersionNegotiationPacket) { unsigned char packet[] = { - 0x10, 0x32, 0x54, 0x76, - 0x98, 0xBA, 0xDC, 0xFE, // public flags (version) 0x01, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '2', + 'Q', '0', '0', '3', 'Q', '2', '.', '0', }; @@ -1647,13 +1647,13 @@ TEST_F(QuicFramerTest, VersionNegotiationPacket) { EXPECT_EQ(kQuicVersion1, visitor_.version_negotiation_packet_->versions[0]); - for (size_t i = 0; i <= kQuicGuidSize + kPublicFlagsSize; ++i) { + for (size_t i = 0; i <= kPublicFlagsSize + kQuicGuidSize; ++i) { string expected_error; QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER; - if (i < kPublicFlagsOffset) { - expected_error = "Unable to read GUID."; - } else if (i < kVersionOffset) { + if (i < kGuidOffset) { expected_error = "Unable to read public flags."; + } else if (i < kVersionOffset) { + expected_error = "Unable to read GUID."; } else { expected_error = "Unable to read supported version in negotiation."; error_code = QUIC_INVALID_VERSION_NEGOTIATION_PACKET; @@ -1664,11 +1664,11 @@ TEST_F(QuicFramerTest, VersionNegotiationPacket) { TEST_F(QuicFramerTest, FecPacket) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1716,11 +1716,11 @@ TEST_F(QuicFramerTest, ConstructPaddingFramePacket) { frames.push_back(QuicFrame(&padding_frame)); unsigned char packet[kMaxPacketSize] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1766,11 +1766,11 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacket) { frames.push_back(QuicFrame(&stream_frame)); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1826,13 +1826,13 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { frames.push_back(QuicFrame(&stream_frame)); unsigned char packet[] = { + // public flags (version) + 0x01, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags (version) - 0x01, // version tag - 'Q', '0', '0', '2', + 'Q', '0', '0', '3', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1875,12 +1875,13 @@ TEST_F(QuicFramerTest, ConstructVersionNegotiationPacket) { header.version_flag = true; unsigned char packet[] = { - 0x10, 0x32, 0x54, 0x76, - 0x98, 0xBA, 0xDC, 0xFE, // public flags (version) 0x01, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '2', + 'Q', '0', '0', '3', 'Q', '2', '.', '0', }; @@ -1920,11 +1921,11 @@ TEST_F(QuicFramerTest, ConstructAckFramePacket) { frames.push_back(QuicFrame(&ack_frame)); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1983,11 +1984,11 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { frames.push_back(QuicFrame(&congestion_feedback_frame)); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2045,11 +2046,11 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { frames.push_back(QuicFrame(&frame)); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2111,11 +2112,11 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { frames.push_back(QuicFrame(&congestion_feedback_frame)); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2181,11 +2182,11 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) { rst_frame.error_details = "because I can"; unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2247,11 +2248,11 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) { frames.push_back(QuicFrame(&close_frame)); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2321,11 +2322,11 @@ TEST_F(QuicFramerTest, ConstructGoAwayPacket) { frames.push_back(QuicFrame(&goaway_frame)); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2367,11 +2368,11 @@ TEST_F(QuicFramerTest, ConstructPublicResetPacket) { reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789); unsigned char packet[] = { + // public flags + 0x02, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x02, // nonce proof 0x89, 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, @@ -2405,11 +2406,11 @@ TEST_F(QuicFramerTest, ConstructFecPacket) { fec_data.redundancy = "abcdefghijklmnop"; unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2437,11 +2438,11 @@ TEST_F(QuicFramerTest, ConstructFecPacket) { TEST_F(QuicFramerTest, EncryptPacket) { QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC); unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2470,11 +2471,11 @@ TEST_F(QuicFramerTest, EncryptPacket) { TEST_F(QuicFramerTest, EncryptPacketWithVersionFlag) { QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC); unsigned char packet[] = { + // public flags (version) + 0x01, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags (version) - 0x01, // version tag 'Q', '.', '1', '0', // packet sequence number @@ -2664,11 +2665,11 @@ TEST_F(QuicFramerTest, CleanTruncation) { TEST_F(QuicFramerTest, EntropyFlagTest) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2705,11 +2706,11 @@ TEST_F(QuicFramerTest, EntropyFlagTest) { TEST_F(QuicFramerTest, FecEntropyFlagTest) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2747,11 +2748,11 @@ TEST_F(QuicFramerTest, FecEntropyFlagTest) { TEST_F(QuicFramerTest, StopPacketProcessing) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -2810,11 +2811,11 @@ TEST_F(QuicFramerTest, StopPacketProcessing) { TEST_F(QuicFramerTest, ConnectionCloseWithInvalidAck) { unsigned char packet[] = { + // public flags + 0x00, // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, - // public flags - 0x00, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index bd4f332..4208b46 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -173,7 +173,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, "", 0u, true, &frame); EXPECT_EQ(0u, consumed); - CheckStreamFrame(frame, 1u, std::string(), 0u, true); + CheckStreamFrame(frame, 1u, string(), 0u, true); delete frame.stream_frame; } diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index 27f980f..4c0c88c 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -14,13 +14,13 @@ using std::string; namespace net { size_t GetPacketHeaderSize(bool include_version) { - return kQuicGuidSize + kPublicFlagsSize + + return kPublicFlagsSize + kQuicGuidSize + (include_version ? kQuicVersionSize : 0) + kSequenceNumberSize + kPrivateFlagsSize + kFecGroupSize; } size_t GetPublicResetPacketSize() { - return kQuicGuidSize + kPublicFlagsSize + kPublicResetNonceSize + + return kPublicFlagsSize + kQuicGuidSize + kPublicResetNonceSize + kSequenceNumberSize; } diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index f4c5e95..45676f5 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -48,10 +48,10 @@ const QuicByteCount kMaxPacketSize = 1200; // Maximum number of open streams per connection. const size_t kDefaultMaxStreamsPerConnection = 100; -// Number of bytes reserved for guid in the packet header. -const size_t kQuicGuidSize = 8; // Number of bytes reserved for public flags in the packet header. const size_t kPublicFlagsSize = 1; +// Number of bytes reserved for guid in the packet header. +const size_t kQuicGuidSize = 8; // Number of bytes reserved for version number in the packet header. const size_t kQuicVersionSize = 4; // Number of bytes reserved for sequence number in the packet header. @@ -271,7 +271,7 @@ const QuicTag kUnsupportedVersion = -1; // Each time the wire format changes, this need needs to be incremented. // At some point, we will actually freeze the wire format and make an official // version number, but this works for now. -const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '2'); +const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '3'); #undef TAG // MakeQuicTag returns a value given the four bytes. For example: diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index 00003d4..10ede55 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -104,6 +104,8 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Servers will simply call it once with HANDSHAKE_CONFIRMED. virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event); + // Returns mutable config for this session. Returned config is owned + // by QuicSession. virtual QuicConfig* config(); // Returns true if the stream existed previously and has been closed. diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index a42993c..ae5f4b5 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -197,6 +197,17 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { } // static +const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) { + switch (level) { + RETURN_STRING_LITERAL(ENCRYPTION_NONE); + RETURN_STRING_LITERAL(ENCRYPTION_INITIAL); + RETURN_STRING_LITERAL(ENCRYPTION_FORWARD_SECURE); + RETURN_STRING_LITERAL(NUM_ENCRYPTION_LEVELS); + } + return "INVALID_ENCRYPTION_LEVEL"; +} + +// static string QuicUtils::TagToString(QuicTag tag) { char chars[4]; bool ascii = true; diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h index e866a07..e277504 100644 --- a/net/quic/quic_utils.h +++ b/net/quic/quic_utils.h @@ -55,6 +55,9 @@ class NET_EXPORT_PRIVATE QuicUtils { // Returns the name of the QuicErrorCode as a char* static const char* ErrorToString(QuicErrorCode error); + // Returns the level of encryption as a char* + static const char* EncryptionLevelToString(EncryptionLevel level); + // TagToString is a utility function for pretty-printing handshake messages // that converts a tag to a string. It will try to maintain the human friendly // name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 4e285ad..40a61a4 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -100,6 +100,24 @@ void MovePackets(PacketSavingConnection* source_conn, } } +// HexChar parses |c| as a hex character. If valid, it sets |*value| to the +// value of the hex character and returns true. Otherwise it returns false. +bool HexChar(char c, uint8* value) { + if (c >= '0' && c <= '9') { + *value = c - '0'; + return true; + } + if (c >= 'a' && c <= 'f') { + *value = c - 'a'; + return true; + } + if (c >= 'A' && c <= 'F') { + *value = c - 'A'; + return true; + } + return false; +} + } // anonymous namespace CryptoTestUtils::FakeClientOptions::FakeClientOptions() @@ -107,29 +125,6 @@ CryptoTestUtils::FakeClientOptions::FakeClientOptions() } // static -void CryptoTestUtils::CommunicateHandshakeMessages( - PacketSavingConnection* a_conn, - QuicCryptoStream* a, - PacketSavingConnection* b_conn, - QuicCryptoStream* b) { - size_t a_i = 0, b_i = 0; - while (!a->handshake_confirmed()) { - 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, b_conn); - - 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, a_conn); - } -} - -// static int CryptoTestUtils::HandshakeWithFakeServer( PacketSavingConnection* client_conn, QuicCryptoClientStream* client) { @@ -207,6 +202,29 @@ void CryptoTestUtils::SetupCryptoServerConfigForTest( } // static +void CryptoTestUtils::CommunicateHandshakeMessages( + PacketSavingConnection* a_conn, + QuicCryptoStream* a, + PacketSavingConnection* b_conn, + QuicCryptoStream* b) { + size_t a_i = 0, b_i = 0; + while (!a->handshake_confirmed()) { + 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, b_conn); + + 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, a_conn); + } +} + +// static string CryptoTestUtils::GetValueForTag(const CryptoHandshakeMessage& message, QuicTag tag) { QuicTagValueMap::const_iterator it = message.tag_value_map().find(tag); @@ -364,5 +382,91 @@ void CryptoTestUtils::CompareClientAndServerKeys( client_forward_secure_decrypter_iv.data(), client_forward_secure_decrypter_iv.length()); } + +// static +QuicTag CryptoTestUtils::ParseTag(const char* tagstr) { + const size_t len = strlen(tagstr); + CHECK_NE(0u, len); + + QuicTag tag = 0; + + if (tagstr[0] == '#') { + CHECK_EQ(static_cast<size_t>(1 + 2*4), len); + tagstr++; + + for (size_t i = 0; i < 8; i++) { + tag <<= 4; + + uint8 v = 0; + CHECK(HexChar(tagstr[i], &v)); + tag |= v; + } + + return tag; + } + + CHECK_LE(len, 4u); + for (size_t i = 0; i < 4; i++) { + tag >>= 8; + if (i < len) { + tag |= static_cast<uint32>(tagstr[i]) << 24; + } + } + + return tag; +} + +// static +CryptoHandshakeMessage CryptoTestUtils::Message(const char* message_tag, ...) { + va_list ap; + va_start(ap, message_tag); + + CryptoHandshakeMessage message = BuildMessage(message_tag, ap); + va_end(ap); + return message; +} + +// static +CryptoHandshakeMessage CryptoTestUtils::BuildMessage(const char* message_tag, + va_list ap) { + CryptoHandshakeMessage msg; + msg.set_tag(ParseTag(message_tag)); + + for (;;) { + const char* tagstr = va_arg(ap, const char*); + if (tagstr == NULL) { + break; + } + + const QuicTag tag = ParseTag(tagstr); + const char* valuestr = va_arg(ap, const char*); + + size_t len = strlen(valuestr); + if (len > 0 && valuestr[0] == '#') { + valuestr++; + len--; + + CHECK(len % 2 == 0); + scoped_ptr<uint8[]> buf(new uint8[len/2]); + + for (size_t i = 0; i < len/2; i++) { + uint8 v = 0; + CHECK(HexChar(valuestr[i*2], &v)); + buf[i] = v << 4; + CHECK(HexChar(valuestr[i*2 + 1], &v)); + buf[i] |= v; + } + + msg.SetStringPiece( + tag, StringPiece(reinterpret_cast<char*>(buf.get()), len/2)); + continue; + } + + msg.SetStringPiece(tag, valuestr); + } + + return msg; +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h index 3a4fdd4..b811d9f 100644 --- a/net/quic/test_tools/crypto_test_utils.h +++ b/net/quic/test_tools/crypto_test_utils.h @@ -5,6 +5,8 @@ #ifndef NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_ #define NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_ +#include <stdarg.h> + #include <vector> #include "base/logging.h" @@ -82,6 +84,32 @@ class CryptoTestUtils { uint64 hash, uint32 index); + // ParseTag returns a QuicTag from parsing |tagstr|. |tagstr| may either be + // in the format "EXMP" (i.e. ASCII format), or "#11223344" (an explicit hex + // format). It CHECK fails if there's a parse error. + static QuicTag ParseTag(const char* tagstr); + + // Message constructs a handshake message from a variable number of + // arguments. |message_tag| is passed to |ParseTag| and used as the tag of + // the resulting message. The arguments are taken in pairs and NULL + // terminated. The first of each pair is the tag of a tag/value and is given + // as an argument to |ParseTag|. The second is the value of the tag/value + // pair and is either a hex dump, preceeded by a '#', or a raw value. + // + // Message( + // "CHLO", + // "NOCE", "#11223344", + // "SNI", "www.example.com", + // NULL); + static CryptoHandshakeMessage Message(const char* message_tag, ...); + + // BuildMessage is the same as |Message|, but takes the variable arguments + // explicitly. TODO(rtenneti): Investigate whether it'd be better for + // Message() and BuildMessage() to return a CryptoHandshakeMessage* pointer + // instead, to avoid copying the return value. + static CryptoHandshakeMessage BuildMessage(const char* message_tag, + va_list ap); + 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 a8b8956..19a2832 100644 --- a/net/quic/test_tools/mock_random.cc +++ b/net/quic/test_tools/mock_random.cc @@ -23,6 +23,9 @@ bool MockRandom::RandBool() { } void MockRandom::Reseed(const void* additional_entropy, size_t entropy_len) { +} + +void MockRandom::ChangeValue() { increment_++; } diff --git a/net/quic/test_tools/mock_random.h b/net/quic/test_tools/mock_random.h index 1278297..544f5ce 100644 --- a/net/quic/test_tools/mock_random.h +++ b/net/quic/test_tools/mock_random.h @@ -21,12 +21,14 @@ class MockRandom : public QuicRandom { virtual uint64 RandUint64() OVERRIDE; // Returns false. virtual bool RandBool() OVERRIDE; - // Reseed advances |increment_| which causes the value returned by - // |RandUint64| to increment and the byte that |RandBytes| fills with, to - // advance. + // Does nothing. virtual void Reseed(const void* additional_entropy, size_t entropy_len) OVERRIDE; + // ChangeValue increments |increment_|. This causes the value returned by + // |RandUint64| and the byte that |RandBytes| fills with, to change. + void ChangeValue(); + private: uint8 increment_; }; diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index 01e9181..281dc4c 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -12,7 +12,7 @@ #include "base/threading/simple_thread.h" #include "net/base/ip_endpoint.h" // TODO(rtenneti): Delete this when NSS is supported. -#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/null_encrypter.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" @@ -62,10 +62,11 @@ void GenerateBody(string* body, int length) { // Simple wrapper class to run server in a thread. class ServerThread : public base::SimpleThread { public: - explicit ServerThread(IPEndPoint address) + explicit ServerThread(IPEndPoint address, const QuicConfig& config) : SimpleThread("server_thread"), listening_(true, false), quit_(true, false), + server_(config), address_(address), port_(0) { } @@ -110,12 +111,13 @@ class ServerThread : public base::SimpleThread { class EndToEndTest : public ::testing::Test { protected: EndToEndTest() - : server_hostname_("localhost"), + : server_hostname_("example.com"), server_started_(false) { net::IPAddressNumber ip; CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip)); server_address_ = IPEndPoint(ip, 0); - config_.SetDefaults(); + client_config_.SetDefaults(); + server_config_.SetDefaults(); AddToCache("GET", kLargeRequest, "HTTP/1.1", "200", "OK", kFooResponseBody); AddToCache("GET", "https://www.google.com/foo", @@ -131,7 +133,7 @@ class EndToEndTest : public ::testing::Test { virtual QuicTestClient* CreateQuicClient() { QuicTestClient* client = new QuicTestClient(server_address_, server_hostname_, - config_); + client_config_); client->Connect(); return client; } @@ -149,7 +151,7 @@ class EndToEndTest : public ::testing::Test { } void StartServer() { - server_thread_.reset(new ServerThread(server_address_)); + server_thread_.reset(new ServerThread(server_address_, server_config_)); server_thread_->Start(); server_thread_->listening()->Wait(); server_address_ = IPEndPoint(server_address_.address(), @@ -202,12 +204,13 @@ class EndToEndTest : public ::testing::Test { scoped_ptr<ServerThread> server_thread_; scoped_ptr<QuicTestClient> client_; bool server_started_; - QuicConfig config_; + QuicConfig client_config_; + QuicConfig server_config_; }; TEST_F(EndToEndTest, SimpleRequestResponse) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -220,7 +223,7 @@ TEST_F(EndToEndTest, SimpleRequestResponse) { TEST_F(EndToEndTest, SimpleRequestResponsev6) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -236,7 +239,7 @@ TEST_F(EndToEndTest, SimpleRequestResponsev6) { TEST_F(EndToEndTest, SeparateFinPacket) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -266,7 +269,7 @@ TEST_F(EndToEndTest, SeparateFinPacket) { TEST_F(EndToEndTest, MultipleRequestResponse) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -281,7 +284,7 @@ TEST_F(EndToEndTest, MultipleRequestResponse) { TEST_F(EndToEndTest, MultipleClients) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -310,7 +313,7 @@ TEST_F(EndToEndTest, MultipleClients) { TEST_F(EndToEndTest, RequestOverMultiplePackets) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -340,7 +343,7 @@ TEST_F(EndToEndTest, RequestOverMultiplePackets) { TEST_F(EndToEndTest, MultipleFramesRandomOrder) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -371,7 +374,7 @@ TEST_F(EndToEndTest, MultipleFramesRandomOrder) { TEST_F(EndToEndTest, PostMissingBytes) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -393,7 +396,7 @@ TEST_F(EndToEndTest, PostMissingBytes) { TEST_F(EndToEndTest, LargePost) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -413,7 +416,7 @@ TEST_F(EndToEndTest, LargePost) { TEST_F(EndToEndTest, LargePostFEC) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -451,7 +454,7 @@ TEST_F(EndToEndTest, LargePostFEC) { TEST_F(EndToEndTest, InvalidStream) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -475,7 +478,7 @@ TEST_F(EndToEndTest, InvalidStream) { TEST_F(EndToEndTest, MultipleTermination) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -509,8 +512,8 @@ TEST_F(EndToEndTest, MultipleTermination) { "Check failed: !fin_buffered_"); } -/*TEST_F(EndToEndTest, Timeout) { - config_.set_idle_connection_state_lifetime( +TEST_F(EndToEndTest, Timeout) { + client_config_.set_idle_connection_state_lifetime( QuicTime::Delta::FromMicroseconds(500), QuicTime::Delta::FromMicroseconds(500)); // Note: we do NOT ASSERT_TRUE: we may time out during initial handshake: @@ -519,11 +522,22 @@ TEST_F(EndToEndTest, MultipleTermination) { while (client_->client()->connected()) { client_->client()->WaitForEvents(); } -}*/ +} + +TEST_F(EndToEndTest, LimitMaxOpenStreams) { + // Server limits the number of max streams to 2. + server_config_.set_max_streams_per_connection(2, 2); + // Client tries to negotiate for 10. + client_config_.set_max_streams_per_connection(10, 5); + + ASSERT_TRUE(Initialize()); + QuicConfig* client_negotiated_config = client_->client()->session()->config(); + EXPECT_EQ(2u, client_negotiated_config->max_streams_per_connection()); +} TEST_F(EndToEndTest, ResetConnection) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } @@ -561,7 +575,7 @@ class WrongAddressWriter : public QuicPacketWriter { TEST_F(EndToEndTest, ConnectionMigration) { // TODO(rtenneti): Delete this when NSS is supported. - if (!Aes128GcmEncrypter::IsSupported()) { + if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; } diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index 9ca70a3..e3edac5 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc @@ -57,8 +57,8 @@ QuicClient::QuicClient(IPEndPoint server_address, QuicClient::~QuicClient() { if (connected()) { - session()->connection() - ->SendConnectionClosePacket(QUIC_PEER_GOING_AWAY, std::string()); + session()->connection()->SendConnectionClosePacket( + QUIC_PEER_GOING_AWAY, string()); } } @@ -67,8 +67,8 @@ bool QuicClient::Initialize() { epoll_server_.set_timeout_in_us(50 * 1000); crypto_config_.SetDefaults(); - int address_family = server_address_.GetSockAddrFamily(); + int address_family = server_address_.GetSockAddrFamily(); fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); if (fd_ < 0) { LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc index 0b9603c..a1dbc16 100644 --- a/net/tools/quic/quic_client_session_test.cc +++ b/net/tools/quic/quic_client_session_test.cc @@ -7,6 +7,7 @@ #include <vector> #include "net/base/ip_endpoint.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/quic_reliable_client_stream.h" @@ -48,10 +49,18 @@ class QuicClientSessionTest : public ::testing::Test { }; TEST_F(QuicClientSessionTest, CryptoConnect) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } CompleteCryptoHandshake(); } TEST_F(QuicClientSessionTest, DISABLED_MaxNumConnections) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } // FLAGS_max_streams_per_connection = 1; // Initialize crypto before the client session will create a stream. CompleteCryptoHandshake(); diff --git a/net/tools/quic/quic_reliable_client_stream_test.cc b/net/tools/quic/quic_reliable_client_stream_test.cc index 1f1eb54..af0ffd5 100644 --- a/net/tools/quic/quic_reliable_client_stream_test.cc +++ b/net/tools/quic/quic_reliable_client_stream_test.cc @@ -25,7 +25,7 @@ namespace { class QuicClientStreamTest : public ::testing::Test { public: QuicClientStreamTest() - : session_("localhost", QuicConfig(), + : session_("example.com", QuicConfig(), new MockConnection(1, IPEndPoint(), 0, &eps_, false), &crypto_config_), body_("hello world") { diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc index a92c4ba..adc2df1 100644 --- a/net/tools/quic/quic_server.cc +++ b/net/tools/quic/quic_server.cc @@ -40,12 +40,29 @@ QuicServer::QuicServer() overflow_supported_(false), use_recvmmsg_(false), crypto_config_(kSourceAddressTokenSecret) { + // Use hardcoded crypto parameters for now. + config_.SetDefaults(); + Initialize(); +} + +QuicServer::QuicServer(const QuicConfig& config) + : port_(0), + packets_dropped_(0), + overflow_supported_(false), + use_recvmmsg_(false), + config_(config), + crypto_config_(kSourceAddressTokenSecret) { + Initialize(); +} + +void QuicServer::Initialize() { +#if MMSG_MORE + use_recvmmsg_ = true; +#endif epoll_server_.set_timeout_in_us(50 * 1000); // Initialize the in memory cache now. QuicInMemoryCache::GetInstance(); - // Use hardcoded crypto parameters for now. - config_.SetDefaults(); QuicEpollClock clock(&epoll_server_); scoped_ptr<CryptoHandshakeMessage> scfg( @@ -123,7 +140,6 @@ bool QuicServer::Listen(const IPEndPoint& address) { } epoll_server_.RegisterFD(fd_, this, kEpollFlags); - dispatcher_.reset(new QuicDispatcher(config_, crypto_config_, fd_, &epoll_server_)); @@ -187,6 +203,11 @@ bool QuicServer::ReadAndDispatchSinglePacket(int fd, QuicEncryptedPacket packet(buf, bytes_read, false); QuicGuid guid; QuicDataReader reader(packet.data(), packet.length()); + uint8 public_flags; + if (!reader.ReadBytes(&public_flags, 1)) { + LOG(DFATAL) << "Unable to read public flags."; + return false; + } if (!reader.ReadUInt64(&guid)) { return true; // We read, we just didn't like the results. } diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h index 99a9d7f..6399566 100644 --- a/net/tools/quic/quic_server.h +++ b/net/tools/quic/quic_server.h @@ -27,6 +27,8 @@ class QuicDispatcher; class QuicServer : public EpollCallbackInterface { public: QuicServer(); + explicit QuicServer(const QuicConfig& config); + virtual ~QuicServer(); // Start listening on the specified address. @@ -64,6 +66,9 @@ class QuicServer : public EpollCallbackInterface { int port() { return port_; } private: + // Initialize the internal state of the server. + void Initialize(); + // Accepts data from the framer and demuxes clients to sessions. scoped_ptr<QuicDispatcher> dispatcher_; // Frames incoming packets and hands them to the dispatcher. |