From 8ba812191740bbfde777360953618d80dd3467a5 Mon Sep 17 00:00:00 2001 From: "rtenneti@chromium.org" Date: Fri, 3 May 2013 13:11:48 +0000 Subject: [PATCH] Land Recent QUIC Changes Alter the serialisation format of the crypto messages. This changes the format of the crypto messages so that: * We can cope with > 65K values in order to be robust to post-quantum algorithms in the future. * Rather than encoding lengths, we encode the offset one byte past the end of the value. This allows an implementation to binary search the header without having to do all the allocation and copying the we currently do. Merge internal change: 44699015 Automated rollback of changelist 44685914. Rollback: Bugfix infinite wait Merge internal change: 44693957 QUIC: retransmit packets with the correct encryption. This change does four things: * Splits the concept of a completed handshake in two: when encryption is established and when the server has confirmed the handshake. In order to do 0-RTT, we have to start sending after the first of those events. * Retransmits packets using the same encryption level as they were sent with. Without this, the loss of a client hello message is fatal to the connection because it will be retransmitted under encryption and the server will never be able to process it. * Makes decryption failures an ignored error. This is needed because, if a client hello message is lost, the subsequent packets will be encrypted and the server won't have the decrypter to process them. * Changes how decrypters are handled by the framer. A server now replaces its decrypter completely - thus removing the NullDecrypter. The client now has latching alternative decrypters which replace the primary decrypter when used. This doesn't completely close the hole: the connection still needs to worry about plaintext packets injected into the client. This change does not implement the correct fallback for the server rejecting a full client hello. It also doesn't implement a limit for the number of packets that we'll send without the server confirming the handshake. I'm hoping that rch can do that much more easily than I can! Merge internal change: 44690884 R=rch@chromium.org Review URL: https://chromiumcodereview.appspot.com/14718011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@198099 0039d316-1c4b-4281-b951-d872f2087c98 --- .../congestion_control/tcp_cubic_sender.cc | 1 - net/quic/crypto/crypto_framer.cc | 111 ++++---- net/quic/crypto/crypto_framer.h | 14 +- net/quic/crypto/crypto_framer_test.cc | 134 ++++++---- net/quic/crypto/crypto_protocol.h | 2 +- net/quic/quic_client_session.cc | 26 +- net/quic/quic_client_session.h | 5 +- net/quic/quic_client_session_test.cc | 21 +- net/quic/quic_connection.cc | 73 ++++-- net/quic/quic_connection.h | 82 +++--- net/quic/quic_connection_helper_test.cc | 15 +- net/quic/quic_connection_test.cc | 245 +++++++++++++++--- net/quic/quic_crypto_client_stream.cc | 44 ++-- net/quic/quic_crypto_client_stream.h | 6 - net/quic/quic_crypto_client_stream_test.cc | 6 +- net/quic/quic_crypto_server_stream.cc | 19 +- net/quic/quic_crypto_server_stream_test.cc | 6 +- net/quic/quic_crypto_stream.cc | 12 +- net/quic/quic_crypto_stream.h | 7 +- net/quic/quic_crypto_stream_test.cc | 5 +- net/quic/quic_framer.cc | 88 +++++-- net/quic/quic_framer.h | 48 +++- net/quic/quic_framer_test.cc | 20 +- net/quic/quic_http_stream_test.cc | 11 +- net/quic/quic_network_transaction_unittest.cc | 84 +++++- net/quic/quic_packet_creator_test.cc | 15 +- net/quic/quic_packet_generator_test.cc | 6 +- net/quic/quic_protocol.cc | 8 +- net/quic/quic_protocol.h | 20 ++ net/quic/quic_session.cc | 11 +- net/quic/quic_session.h | 41 ++- net/quic/quic_session_test.cc | 10 +- net/quic/quic_stream_factory_test.cc | 20 +- net/quic/quic_utils.cc | 1 + net/quic/test_tools/crypto_test_utils.cc | 41 +-- .../test_tools/mock_crypto_client_stream.cc | 18 +- .../test_tools/mock_crypto_client_stream.h | 24 +- .../mock_crypto_client_stream_factory.cc | 7 +- .../mock_crypto_client_stream_factory.h | 11 + net/quic/test_tools/quic_test_utils.cc | 7 +- net/quic/test_tools/quic_test_utils.h | 1 + net/quic/test_tools/simple_quic_framer.cc | 9 +- net/tools/quic/quic_client.cc | 6 +- net/tools/quic/quic_client.h | 8 +- net/tools/quic/quic_client_session.cc | 4 +- .../quic/quic_epoll_connection_helper_test.cc | 14 +- net/tools/quic/quic_time_wait_list_manager.cc | 4 +- 47 files changed, 919 insertions(+), 452 deletions(-) diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 97eb896b15ef38..4bed7edcdc8293 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -227,7 +227,6 @@ void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { } hybrid_slow_start_.Update(rtt, delay_min_); if (hybrid_slow_start_.Exit()) { - DLOG(INFO) << "Set slowstart threshold:" << congestion_window_; slowstart_threshold_ = congestion_window_; } } diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc index b0ff9ea178ae2d..cbc10797488544 100644 --- a/net/quic/crypto/crypto_framer.cc +++ b/net/quic/crypto/crypto_framer.cc @@ -9,12 +9,16 @@ #include "net/quic/quic_data_writer.h" using base::StringPiece; +using std::make_pair; +using std::pair; +using std::vector; namespace net { namespace { const size_t kCryptoTagSize = sizeof(uint32); +const size_t kCryptoEndOffsetSize = sizeof(uint32); const size_t kNumEntriesSize = sizeof(uint16); const size_t kValueLenSize = sizeof(uint16); @@ -90,7 +94,7 @@ bool CryptoFramer::ProcessInput(StringPiece input) { message_.set_tag(message_tag); state_ = STATE_READING_NUM_ENTRIES; case STATE_READING_NUM_ENTRIES: - if (reader.BytesRemaining() < kNumEntriesSize) { + if (reader.BytesRemaining() < kNumEntriesSize + sizeof(uint16)) { break; } reader.ReadUInt16(&num_entries_); @@ -98,56 +102,55 @@ bool CryptoFramer::ProcessInput(StringPiece input) { error_ = QUIC_CRYPTO_TOO_MANY_ENTRIES; return false; } - state_ = STATE_READING_KEY_TAGS; - case STATE_READING_KEY_TAGS: - if (reader.BytesRemaining() < num_entries_ * kCryptoTagSize) { + uint16 padding; + reader.ReadUInt16(&padding); + + tags_and_lengths_.reserve(num_entries_); + state_ = STATE_READING_TAGS_AND_LENGTHS; + values_len_ = 0; + case STATE_READING_TAGS_AND_LENGTHS: { + if (reader.BytesRemaining() < num_entries_ * (kCryptoTagSize + + kCryptoEndOffsetSize)) { break; } - for (int i = 0; i < num_entries_; ++i) { + + uint32 last_end_offset = 0; + for (unsigned i = 0; i < num_entries_; ++i) { CryptoTag tag; reader.ReadUInt32(&tag); - if (i > 0 && tag <= tags_.back()) { - error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER; + if (i > 0 && tag <= tags_and_lengths_[i-1].first) { + if (tag == tags_and_lengths_[i-1].first) { + error_ = QUIC_CRYPTO_DUPLICATE_TAG; + } else { + error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER; + } return false; } - tags_.push_back(tag); - } - state_ = STATE_READING_LENGTHS; - case STATE_READING_LENGTHS: { - size_t expected_bytes = num_entries_ * kValueLenSize; - bool has_padding = (num_entries_ % 2 == 1); - if (has_padding) { - expected_bytes += kValueLenSize; - } - if (reader.BytesRemaining() < expected_bytes) { - break; - } - values_len_ = 0; - for (int i = 0; i < num_entries_; ++i) { - uint16 len; - reader.ReadUInt16(&len); - tag_length_map_[tags_[i]] = len; - values_len_ += len; - } - // Possible padding - if (has_padding) { - uint16 len; - reader.ReadUInt16(&len); - if (len != 0) { - error_ = QUIC_CRYPTO_INVALID_VALUE_LENGTH; + + uint32 end_offset; + reader.ReadUInt32(&end_offset); + + if (end_offset < last_end_offset) { + error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER; return false; } + tags_and_lengths_.push_back( + make_pair(tag, static_cast(end_offset - last_end_offset))); + last_end_offset = end_offset; } + values_len_ = last_end_offset; state_ = STATE_READING_VALUES; } case STATE_READING_VALUES: if (reader.BytesRemaining() < values_len_) { break; } - for (int i = 0; i < num_entries_; ++i) { + for (vector >::const_iterator + it = tags_and_lengths_.begin(); it != tags_and_lengths_.end(); + it++) { StringPiece value; - reader.ReadStringPiece(&value, tag_length_map_[tags_[i]]); - message_.SetStringPiece(tags_[i], value); + reader.ReadStringPiece(&value, it->second); + message_.SetStringPiece(it->first, value); } visitor_->OnHandshakeMessage(message_); Clear(); @@ -165,18 +168,16 @@ QuicData* CryptoFramer::ConstructHandshakeMessage( if (message.tag_value_map().size() > kMaxEntries) { return NULL; } - size_t len = sizeof(uint32); // message tag + size_t len = kCryptoTagSize; // message tag len += sizeof(uint16); // number of map entries + len += sizeof(uint16); // padding. CryptoTagValueMap::const_iterator it = message.tag_value_map().begin(); while (it != message.tag_value_map().end()) { - len += sizeof(uint32); // tag - len += sizeof(uint16); // value len + len += kCryptoTagSize; // tag + len += kCryptoEndOffsetSize; // end offset len += it->second.length(); // value ++it; } - if (message.tag_value_map().size() % 2 == 1) { - len += sizeof(uint16); // padding - } QuicDataWriter writer(len); if (!writer.WriteUInt32(message.tag())) { @@ -187,29 +188,26 @@ QuicData* CryptoFramer::ConstructHandshakeMessage( DCHECK(false) << "Failed to write size."; return NULL; } - // Tags + if (!writer.WriteUInt16(0)) { + DCHECK(false) << "Failed to write padding."; + return NULL; + } + + uint32 end_offset = 0; + // Tags and offsets for (it = message.tag_value_map().begin(); it != message.tag_value_map().end(); ++it) { if (!writer.WriteUInt32(it->first)) { DCHECK(false) << "Failed to write tag."; return NULL; } - } - // Lengths - for (it = message.tag_value_map().begin(); - it != message.tag_value_map().end(); ++it) { - if (!writer.WriteUInt16(it->second.length())) { - DCHECK(false) << "Failed to write length."; - return NULL; - } - } - // Possible padding - if (message.tag_value_map().size() % 2 == 1) { - if (!writer.WriteUInt16(0)) { - DCHECK(false) << "Failed to write padding."; + end_offset += it->second.length(); + if (!writer.WriteUInt32(end_offset)) { + DCHECK(false) << "Failed to write end offset."; return NULL; } } + // Values for (it = message.tag_value_map().begin(); it != message.tag_value_map().end(); ++it) { @@ -223,8 +221,7 @@ QuicData* CryptoFramer::ConstructHandshakeMessage( void CryptoFramer::Clear() { message_.Clear(); - tag_length_map_.clear(); - tags_.clear(); + tags_and_lengths_.clear(); error_ = QUIC_NO_ERROR; state_ = STATE_READING_TAG; } diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h index 07ffa3976ade05..786128453f70bb 100644 --- a/net/quic/crypto/crypto_framer.h +++ b/net/quic/crypto/crypto_framer.h @@ -5,7 +5,8 @@ #ifndef NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ #define NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ -#include +#include +#include #include "base/basictypes.h" #include "base/logging.h" @@ -86,8 +87,7 @@ class NET_EXPORT_PRIVATE CryptoFramer { enum CryptoFramerState { STATE_READING_TAG, STATE_READING_NUM_ENTRIES, - STATE_READING_KEY_TAGS, - STATE_READING_LENGTHS, + STATE_READING_TAGS_AND_LENGTHS, STATE_READING_VALUES }; @@ -103,11 +103,9 @@ class NET_EXPORT_PRIVATE CryptoFramer { CryptoHandshakeMessage message_; // Number of entires in the message currently being parsed. uint16 num_entries_; - // Vector of tags in the message currently being parsed. - CryptoTagVector tags_; - // Length of the data associated with each tag in the message currently - // being parsed. - std::map tag_length_map_; + // tags_and_lengths_ contains the tags that are currently being parsed and + // their lengths. + std::vector > tags_and_lengths_; // Cumulative length of all values in the message currently being parsed. size_t values_len_; }; diff --git a/net/quic/crypto/crypto_framer_test.cc b/net/quic/crypto/crypto_framer_test.cc index 1335e8b0028089..0cdbd40a4a7283 100644 --- a/net/quic/crypto/crypto_framer_test.cc +++ b/net/quic/crypto/crypto_framer_test.cc @@ -71,20 +71,20 @@ TEST(CryptoFramerTest, ConstructHandshakeMessage) { 0x33, 0x77, 0xAA, 0xFF, // num entries 0x03, 0x00, + // padding + 0x00, 0x00, // tag 1 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, // tag 2 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, // tag 3 0x7A, 0x56, 0x34, 0x12, - // len 1 - 0x06, 0x00, - // len 2 - 0x05, 0x00, - // len 3 - 0x07, 0x00, - // padding - 0x00, 0x00, + // end offset 3 + 0x12, 0x00, 0x00, 0x00, // value 1 'a', 'b', 'c', 'd', 'e', 'f', @@ -115,14 +115,16 @@ TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) { 0x33, 0x77, 0xAA, 0xFF, // num entries 0x02, 0x00, + // padding + 0x00, 0x00, // tag 1 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, // tag 2 0x79, 0x56, 0x34, 0x12, - // len 1 - 0x06, 0x00, - // len 2 - 0x05, 0x00, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, // value 1 'a', 'b', 'c', 'd', 'e', 'f', @@ -150,12 +152,12 @@ TEST(CryptoFramerTest, ConstructHandshakeMessageZeroLength) { 0x33, 0x77, 0xAA, 0xFF, // num entries 0x01, 0x00, - // tag 1 - 0x78, 0x56, 0x34, 0x12, - // len 1 - 0x00, 0x00, // padding 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x00, 0x00, 0x00, 0x00, }; CryptoFramer framer; @@ -189,14 +191,16 @@ TEST(CryptoFramerTest, ProcessInput) { 0x33, 0x77, 0xAA, 0xFF, // num entries 0x02, 0x00, + // padding + 0x00, 0x00, // tag 1 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, // tag 2 0x79, 0x56, 0x34, 0x12, - // len 1 - 0x06, 0x00, - // len 2 - 0x05, 0x00, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, // value 1 'a', 'b', 'c', 'd', 'e', 'f', @@ -226,20 +230,20 @@ TEST(CryptoFramerTest, ProcessInputWithThreeKeys) { 0x33, 0x77, 0xAA, 0xFF, // num entries 0x03, 0x00, + // padding + 0x00, 0x00, // tag 1 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, // tag 2 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, // tag 3 0x7A, 0x56, 0x34, 0x12, - // len 1 - 0x06, 0x00, - // len 2 - 0x05, 0x00, - // len 3 - 0x07, 0x00, - // padding - 0x00, 0x00, + // end offset 3 + 0x12, 0x00, 0x00, 0x00, // value 1 'a', 'b', 'c', 'd', 'e', 'f', @@ -273,14 +277,16 @@ TEST(CryptoFramerTest, ProcessInputIncrementally) { 0x33, 0x77, 0xAA, 0xFF, // num entries 0x02, 0x00, + // padding + 0x00, 0x00, // tag 1 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, // tag 2 0x79, 0x56, 0x34, 0x12, - // len 1 - 0x06, 0x00, - // len 2 - 0x05, 0x00, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, // value 1 'a', 'b', 'c', 'd', 'e', 'f', @@ -311,10 +317,16 @@ TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) { 0x33, 0x77, 0xAA, 0xFF, // num entries 0x02, 0x00, + // padding + 0x00, 0x00, // tag 1 0x78, 0x56, 0x34, 0x13, + // end offset 1 + 0x01, 0x00, 0x00, 0x00, // tag 2 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x02, 0x00, 0x00, 0x00, }; EXPECT_FALSE( @@ -322,7 +334,7 @@ TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) { EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error()); } -TEST(CryptoFramerTest, ProcessInputTooManyEntries) { +TEST(CryptoFramerTest, ProcessEndOffsetsOutOfOrder) { test::TestCryptoVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor); @@ -331,15 +343,25 @@ TEST(CryptoFramerTest, ProcessInputTooManyEntries) { // tag 0x33, 0x77, 0xAA, 0xFF, // num entries - 0xA0, 0x00, + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x79, 0x56, 0x34, 0x12, + // end offset 1 + 0x01, 0x00, 0x00, 0x00, + // tag 2 + 0x78, 0x56, 0x34, 0x13, + // end offset 2 + 0x00, 0x00, 0x00, 0x00, }; - EXPECT_FALSE( - framer.ProcessInput(StringPiece(AsChars(input), arraysize(input)))); - EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error()); + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error()); } -TEST(CryptoFramerTest, ProcessInputZeroLength) { +TEST(CryptoFramerTest, ProcessInputTooManyEntries) { test::TestCryptoVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor); @@ -348,22 +370,17 @@ TEST(CryptoFramerTest, ProcessInputZeroLength) { // tag 0x33, 0x77, 0xAA, 0xFF, // num entries - 0x02, 0x00, - // tag 1 - 0x78, 0x56, 0x34, 0x12, - // tag 2 - 0x79, 0x56, 0x34, 0x12, - // len 1 + 0xA0, 0x00, + // padding 0x00, 0x00, - // len 2 - 0x05, 0x00, }; - EXPECT_TRUE( - framer.ProcessInput(StringPiece(AsChars(input), arraysize(input)))); + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error()); } -TEST(CryptoFramerTest, ProcessInputInvalidLengthPadding) { +TEST(CryptoFramerTest, ProcessInputZeroLength) { test::TestCryptoVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor); @@ -372,18 +389,21 @@ TEST(CryptoFramerTest, ProcessInputInvalidLengthPadding) { // tag 0x33, 0x77, 0xAA, 0xFF, // num entries - 0x01, 0x00, + 0x02, 0x00, + // padding + 0x00, 0x00, // tag 1 0x78, 0x56, 0x34, 0x12, - // len 1 - 0x05, 0x00, - // padding - 0x05, 0x00, + // end offset 1 + 0x00, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x05, 0x00, 0x00, 0x00, }; - EXPECT_FALSE( - framer.ProcessInput(StringPiece(AsChars(input), arraysize(input)))); - EXPECT_EQ(QUIC_CRYPTO_INVALID_VALUE_LENGTH, framer.error()); + EXPECT_TRUE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); } } // namespace test diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 3e22e7b5816e3d..baceeae8fc4fb9 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -75,7 +75,7 @@ const CryptoTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature). #undef TAG -const size_t kMaxEntries = 16; // Max number of entries in a message. +const size_t kMaxEntries = 128; // Max number of entries in a message. const size_t kNonceSize = 32; // Size in bytes of the connection nonce. diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc index b1bcd549fd7481..c3b8d3a1765f82 100644 --- a/net/quic/quic_client_session.cc +++ b/net/quic/quic_client_session.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_client_session.h" +#include "base/callback_helpers.h" #include "base/message_loop.h" #include "base/stl_util.h" #include "base/string_number_conversions.h" @@ -50,13 +51,14 @@ QuicClientSession::QuicClientSession( } QuicClientSession::~QuicClientSession() { + DCHECK(callback_.is_null()); connection()->set_debug_visitor(NULL); net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION); } QuicReliableClientStream* QuicClientSession::CreateOutgoingReliableStream() { - if (!crypto_stream_->handshake_complete()) { - DLOG(INFO) << "Crypto handshake not complete, no outgoing stream created."; + if (!crypto_stream_->encryption_established()) { + DLOG(INFO) << "Encryption not active so no outgoing stream created."; return NULL; } if (GetNumOpenStreams() >= get_max_open_streams()) { @@ -87,7 +89,7 @@ int QuicClientSession::CryptoConnect(const CompletionCallback& callback) { return ERR_CONNECTION_FAILED; } - if (IsCryptoHandshakeComplete()) { + if (IsEncryptionEstablished()) { return OK; } @@ -109,12 +111,23 @@ void QuicClientSession::CloseStream(QuicStreamId stream_id) { } } -void QuicClientSession::OnCryptoHandshakeComplete(QuicErrorCode error) { +void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { if (!callback_.is_null()) { - callback_.Run(error == QUIC_NO_ERROR ? OK : ERR_UNEXPECTED); + // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_ + // could be called because there are no error events in CryptoHandshakeEvent + // enum. If error events are added to CryptoHandshakeEvent, then the + // following code needs to changed. + base::ResetAndReturn(&callback_).Run(OK); } } +void QuicClientSession::ConnectionClose(QuicErrorCode error, bool from_peer) { + if (!callback_.is_null()) { + base::ResetAndReturn(&callback_).Run(error); + } + QuicSession::ConnectionClose(error, from_peer); +} + void QuicClientSession::StartReading() { if (read_pending_) { return; @@ -137,6 +150,9 @@ void QuicClientSession::StartReading() { } void QuicClientSession::CloseSessionOnError(int error) { + if (!callback_.is_null()) { + base::ResetAndReturn(&callback_).Run(error); + } while (!streams()->empty()) { ReliableQuicStream* stream = streams()->begin()->second; QuicStreamId id = stream->id(); diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h index 2bf6e7856e59e0..fbbbecfb79df21 100644 --- a/net/quic/quic_client_session.h +++ b/net/quic/quic_client_session.h @@ -45,7 +45,10 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { virtual QuicReliableClientStream* CreateOutgoingReliableStream() OVERRIDE; virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE; virtual void CloseStream(QuicStreamId stream_id) OVERRIDE; - virtual void OnCryptoHandshakeComplete(QuicErrorCode error) OVERRIDE; + virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) OVERRIDE; + + // QuicConnectionVisitorInterface methods: + virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE; // Performs a crypto handshake with the server. int CryptoConnect(const CompletionCallback& callback); diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index 6d26541627e1da..8b7bddb051c874 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -86,12 +86,12 @@ TEST_F(QuicClientSessionTest, MaxNumConnections) { } TEST_F(QuicClientSessionTest, GoAwayReceived) { - // Initialize crypto before the client session will create a stream. - ASSERT_TRUE(session_.CryptoConnect(callback_.callback())); - // Simulate the server crypto handshake. - CryptoHandshakeMessage server_message; - server_message.set_tag(kSHLO); - session_.GetCryptoStream()->OnHandshakeMessage(server_message); + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); // After receiving a GoAway, I should no longer be able to create outgoing // streams. @@ -109,11 +109,7 @@ TEST_F(QuicClientSessionTest, Logging) { // TODO(rch): Add some helper methods to simplify packet creation in tests. // Receive a packet, and verify that it was logged. - QuicFramer framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false); + QuicFramer framer(kQuicVersion1, QuicTime::Zero(), false); QuicRstStreamFrame frame; frame.stream_id = 2; frame.error_code = QUIC_STREAM_CONNECTION_ERROR; @@ -132,7 +128,8 @@ TEST_F(QuicClientSessionTest, Logging) { header.fec_group = 0; scoped_ptr p( framer.ConstructFrameDataPacket(header, frames).packet); - scoped_ptr packet(framer.EncryptPacket(1, *p)); + scoped_ptr packet(framer.EncryptPacket( + ENCRYPTION_NONE, 1, *p)); IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); IPEndPoint peer_addr = IPEndPoint(ip, 443); diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 766274b9bdb4ba..4851d47019a6ab 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -79,10 +79,9 @@ QuicConnection::QuicConnection(QuicGuid guid, bool is_server) : helper_(helper), framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), helper->GetClock()->ApproximateNow(), is_server), + encryption_level_(ENCRYPTION_NONE), clock_(helper->GetClock()), random_generator_(helper->GetRandomGenerator()), guid_(guid), @@ -151,7 +150,9 @@ bool QuicConnection::SelectMutualVersion( } void QuicConnection::OnError(QuicFramer* framer) { - if (!connected_) { + // Packets that we cannot decrypt are dropped. + // TODO(rch): add stats to measure this. + if (!connected_ || framer->error() == QUIC_DECRYPTION_FAILURE) { return; } SendConnectionClose(framer->error()); @@ -727,7 +728,9 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, stats_.bytes_received += packet.length(); ++stats_.packets_received; - framer_.ProcessPacket(packet); + if (!framer_.ProcessPacket(packet)) { + return; + } MaybeProcessRevivedPacket(); } @@ -770,7 +773,8 @@ bool QuicConnection::WriteQueuedPackets() { // TODO(rch): clean up and close the connection if we really hit this. DCHECK_LT(queued_packets_.size(), num_queued_packets); num_queued_packets = queued_packets_.size(); - if (WritePacket(packet_iterator->sequence_number, + if (WritePacket(packet_iterator->encryption_level, + packet_iterator->sequence_number, packet_iterator->packet, packet_iterator->retransmittable, NO_FORCE)) { @@ -883,7 +887,8 @@ void QuicConnection::RetransmitPacket( unacked_packets_.rbegin()->first < serialized_packet.sequence_number); unacked_packets_.insert(make_pair(serialized_packet.sequence_number, unacked)); - SendOrQueuePacket(serialized_packet.sequence_number, + SendOrQueuePacket(unacked->encryption_level(), + serialized_packet.sequence_number, serialized_packet.packet, serialized_packet.entropy_hash, HAS_RETRANSMITTABLE_DATA); @@ -901,7 +906,8 @@ bool QuicConnection::CanWrite(Retransmission retransmission, QuicTime::Delta delay = congestion_manager_.TimeUntilSend( now, retransmission, retransmittable); if (delay.IsInfinite()) { - return false; + // TODO(pwestin): should be false but trigger other bugs see b/8350327. + return true; } // If the scheduler requires a delay, then we can not send this packet now. @@ -946,7 +952,8 @@ void QuicConnection::MaybeSetupRetransmission( // SendStreamData(). } -bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, +bool QuicConnection::WritePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, QuicPacket* packet, HasRetransmittableData retransmittable, Force forced) { @@ -967,7 +974,7 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, } scoped_ptr encrypted( - framer_.EncryptPacket(sequence_number, *packet)); + framer_.EncryptPacket(level, sequence_number, *packet)); DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number << " : " << (packet->is_fec_packet() ? "FEC " : (retransmittable == HAS_RETRANSMITTABLE_DATA @@ -1022,6 +1029,10 @@ bool QuicConnection::OnSerializedPacket( DCHECK(unacked_packets_.empty() || unacked_packets_.rbegin()->first < serialized_packet.sequence_number); + // Retransmitted frames will be sent with the same encryption level as the + // original. + serialized_packet.retransmittable_frames->set_encryption_level( + encryption_level_); unacked_packets_.insert( make_pair(serialized_packet.sequence_number, serialized_packet.retransmittable_frames)); @@ -1030,7 +1041,8 @@ bool QuicConnection::OnSerializedPacket( make_pair(serialized_packet.sequence_number, RetransmissionInfo(serialized_packet.sequence_number))); } - return SendOrQueuePacket(serialized_packet.sequence_number, + return SendOrQueuePacket(encryption_level_, + serialized_packet.sequence_number, serialized_packet.packet, serialized_packet.entropy_hash, serialized_packet.retransmittable_frames != NULL ? @@ -1038,13 +1050,14 @@ bool QuicConnection::OnSerializedPacket( NO_RETRANSMITTABLE_DATA); } -bool QuicConnection::SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, +bool QuicConnection::SendOrQueuePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, QuicPacket* packet, QuicPacketEntropyHash entropy_hash, HasRetransmittableData retransmittable) { entropy_manager_.RecordSentPacketEntropyHash(sequence_number, entropy_hash); - if (!WritePacket(sequence_number, packet, retransmittable, NO_FORCE)) { - queued_packets_.push_back(QueuedPacket(sequence_number, packet, + if (!WritePacket(level, sequence_number, packet, retransmittable, NO_FORCE)) { + queued_packets_.push_back(QueuedPacket(sequence_number, packet, level, retransmittable)); return false; } @@ -1134,16 +1147,35 @@ QuicTime QuicConnection::OnRetransmissionTimeout() { return retransmission_timeouts_.top().scheduled_time; } -void QuicConnection::ChangeEncrypter(QuicEncrypter* encrypter) { - framer_.set_encrypter(encrypter); +void QuicConnection::SetEncrypter(EncryptionLevel level, + QuicEncrypter* encrypter) { + framer_.SetEncrypter(level, encrypter); +} + +const QuicEncrypter* QuicConnection::encrypter(EncryptionLevel level) const { + return framer_.encrypter(level); +} + +void QuicConnection::SetDefaultEncryptionLevel( + EncryptionLevel level) { + encryption_level_ = level; +} + +void QuicConnection::SetDecrypter(QuicDecrypter* decrypter) { + framer_.SetDecrypter(decrypter); +} + +void QuicConnection::SetAlternativeDecrypter(QuicDecrypter* decrypter, + bool latch_once_used) { + framer_.SetAlternativeDecrypter(decrypter, latch_once_used); } -void QuicConnection::PushDecrypter(QuicDecrypter* decrypter) { - framer_.push_decrypter(decrypter); +const QuicDecrypter* QuicConnection::decrypter() const { + return framer_.decrypter(); } -void QuicConnection::PopDecrypter() { - framer_.pop_decrypter(); +const QuicDecrypter* QuicConnection::alternative_decrypter() const { + return framer_.alternative_decrypter(); } void QuicConnection::MaybeProcessRevivedPacket() { @@ -1208,7 +1240,8 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, serialized_packet.sequence_number, serialized_packet.entropy_hash); - WritePacket(serialized_packet.sequence_number, + WritePacket(encryption_level_, + serialized_packet.sequence_number, serialized_packet.packet, serialized_packet.retransmittable_frames != NULL ? HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA, diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index 2845dbecd8a040..5e916f83e7630d 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -345,26 +345,32 @@ class NET_EXPORT_PRIVATE QuicConnection // should next fire, or 0 if no retransmission alarm should be set. QuicTime OnRetransmissionTimeout(); - // Changes the encrypter used by |framer_| to |encrypter|. The function + // Changes the encrypter used for level |level| to |encrypter|. The function // takes ownership of |encrypter|. - void ChangeEncrypter(QuicEncrypter* encrypter); - - // Sets the primary decrypter used by |framer_| to |decrypter|. The current - // primary decrypter becomes the backup decrypter. The function takes - // ownership of |decrypter|. - // - // After the function is called, |framer_| starts to decrypt packets using - // |decrypter|. If the decryption fails, |framer_| falls back on the backup - // decrypter. Eventually |framer_| determines that the backup decrypter is - // no longer needed and deletes it. - void PushDecrypter(QuicDecrypter* decrypter); - - // Deletes the current primary decrypter and promotes the backup decrypter - // to be the primary decrypter. - void PopDecrypter(); - - QuicDecrypter* decrypter() const { return framer_.decrypter(); } - QuicEncrypter* encrypter() const { return framer_.encrypter(); } + void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter); + const QuicEncrypter* encrypter(EncryptionLevel level) const; + + // SetDefaultEncryptionLevel sets the encryption level that will be applied + // to new packets. + void SetDefaultEncryptionLevel(EncryptionLevel level); + + // SetDecrypter sets the primary decrypter, replacing any that already exists, + // and takes ownership. If an alternative decrypter is in place then the + // function DCHECKs. This is intended for cases where one knows that future + // packets will be using the new decrypter and the previous decrypter is now + // obsolete. + void SetDecrypter(QuicDecrypter* decrypter); + + // SetAlternativeDecrypter sets a decrypter that may be used to decrypt + // future packets and takes ownership of it. If |latch_once_used| is true, + // then the first time that the decrypter is successful it will replace the + // primary decrypter. Otherwise both decrypters will remain active and the + // primary decrypter will be the one last used. + void SetAlternativeDecrypter(QuicDecrypter* decrypter, + bool latch_once_used); + + const QuicDecrypter* decrypter() const; + const QuicDecrypter* alternative_decrypter() const; protected: // Deletes all missing packets before least unacked. The connection won't @@ -373,27 +379,29 @@ class NET_EXPORT_PRIVATE QuicConnection // |least_unacked| unacked, false otherwise. bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked); - // Send a packet to the peer. If |sequence_number| is present in the - // |retransmission_map_|, then contents of this packet will be retransmitted - // with a new sequence number if it's not acked by the peer. Deletes - // |packet| via WritePacket call or transfers ownership to QueuedPacket, - // ultimately deleted via WritePacket. Also, it updates the entropy map - // corresponding to |sequence_number| using |entropy_hash|. + // Send a packet to the peer using encryption |level|. If |sequence_number| + // is present in the |retransmission_map_|, then contents of this packet will + // be retransmitted with a new sequence number if it's not acked by the peer. + // Deletes |packet| via WritePacket call or transfers ownership to + // QueuedPacket, ultimately deleted via WritePacket. Also, it updates the + // entropy map corresponding to |sequence_number| using |entropy_hash|. // TODO(wtc): none of the callers check the return value. - virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, + virtual bool SendOrQueuePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, QuicPacket* packet, QuicPacketEntropyHash entropy_hash, HasRetransmittableData retransmittable); - // Writes the given packet to socket with the help of helper. Returns true on - // successful write, false otherwise. However, behavior is undefined if - // connection is not established or broken. In any circumstances, a return - // value of true implies that |packet| has been deleted and should not be - // accessed. If |sequence_number| is present in |retransmission_map_| it also - // sets up retransmission of the given packet in case of successful write. If - // |force| is FORCE, then the packet will be sent immediately and the send - // scheduler will not be consulted. - bool WritePacket(QuicPacketSequenceNumber sequence_number, + // Writes the given packet to socket, encrypted with |level|, with the help + // of helper. Returns true on successful write, false otherwise. However, + // behavior is undefined if connection is not established or broken. In any + // circumstances, a return value of true implies that |packet| has been + // deleted and should not be accessed. If |sequence_number| is present in + // |retransmission_map_| it also sets up retransmission of the given packet + // in case of successful write. If |force| is FORCE, then the packet will be + // sent immediately and the send scheduler will not be consulted. + bool WritePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, QuicPacket* packet, HasRetransmittableData retransmittable, Force force); @@ -420,14 +428,17 @@ class NET_EXPORT_PRIVATE QuicConnection struct QueuedPacket { QueuedPacket(QuicPacketSequenceNumber sequence_number, QuicPacket* packet, + EncryptionLevel level, HasRetransmittableData retransmittable) : sequence_number(sequence_number), packet(packet), + encryption_level(level), retransmittable(retransmittable) { } QuicPacketSequenceNumber sequence_number; QuicPacket* packet; + const EncryptionLevel encryption_level; HasRetransmittableData retransmittable; }; @@ -498,6 +509,7 @@ class NET_EXPORT_PRIVATE QuicConnection scoped_ptr helper_; QuicFramer framer_; + EncryptionLevel encryption_level_; const QuicClock* clock_; QuicRandom* random_generator_; diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index 1bddba221bb5d3..3576981cbb97ec 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -59,11 +59,7 @@ class QuicConnectionHelperTest : public ::testing::Test { QuicConnectionHelperTest() : guid_(2), - framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false), + framer_(kQuicVersion1, QuicTime::Zero(), false), net_log_(BoundNetLog()), frame_(1, false, 0, kData) { Initialize(); @@ -152,7 +148,8 @@ class QuicConnectionHelperTest : public ::testing::Test { frames.push_back(QuicFrame(&feedback)); scoped_ptr packet( framer_.ConstructFrameDataPacket(header_, frames).packet); - return framer_.EncryptPacket(header_.packet_sequence_number, *packet); + return framer_.EncryptPacket( + ENCRYPTION_NONE, header_.packet_sequence_number, *packet); } // Returns a newly created packet to send a connection close frame. @@ -200,7 +197,8 @@ class QuicConnectionHelperTest : public ::testing::Test { frames.push_back(frame); scoped_ptr packet( framer_.ConstructFrameDataPacket(header_, frames).packet); - return framer_.EncryptPacket(header_.packet_sequence_number, *packet); + return framer_.EncryptPacket( + ENCRYPTION_NONE, header_.packet_sequence_number, *packet); } QuicGuid guid_; @@ -413,7 +411,8 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) { testing::Return(QuicTime::Delta::FromMicroseconds(1))); QuicPacket* packet = ConstructRawDataPacket(1); - connection_->SendOrQueuePacket(1, packet, 0, HAS_RETRANSMITTABLE_DATA); + connection_->SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, 0, HAS_RETRANSMITTABLE_DATA); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); EXPECT_EQ(1u, connection_->NumQueuedPackets()); diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index e4b44009bd6641..e81e011c02fb04 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_connection.h" +#include "base/basictypes.h" #include "base/bind.h" #include "net/base/net_errors.h" #include "net/quic/congestion_control/receive_algorithm_interface.h" @@ -72,6 +73,131 @@ class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm); }; +// TaggingEncrypter appends 16 bytes of |tag| to the end of each message. +class TaggingEncrypter : public QuicEncrypter { + public: + explicit TaggingEncrypter(uint8 tag) + : tag_(tag) { + } + + virtual ~TaggingEncrypter() {} + + // QuicEncrypter interface. + virtual bool SetKey(StringPiece key) OVERRIDE { return true; } + virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE { + return true; + } + + virtual bool Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) OVERRIDE { + memcpy(output, plaintext.data(), plaintext.size()); + output += plaintext.size(); + memset(output, tag_, kTagSize); + return true; + } + + virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) OVERRIDE { + const size_t len = plaintext.size() + kTagSize; + uint8* buffer = new uint8[len]; + Encrypt(StringPiece(), associated_data, plaintext, buffer); + return new QuicData(reinterpret_cast(buffer), len, true); + } + + virtual size_t GetKeySize() const OVERRIDE { return 0; } + virtual size_t GetNoncePrefixSize() const OVERRIDE { return 0; } + + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE { + return ciphertext_size - kTagSize; + } + + virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE { + return plaintext_size + kTagSize; + } + + virtual StringPiece GetKey() const OVERRIDE { + return StringPiece(); + } + + virtual StringPiece GetNoncePrefix() const OVERRIDE { + return StringPiece(); + } + + private: + enum { + kTagSize = 16, + }; + + const uint8 tag_; +}; + +// TaggingDecrypter ensures that the final 16 bytes of the message all have the +// same value and then removes them. +class TaggingDecrypter : public QuicDecrypter { + public: + virtual ~TaggingDecrypter() {} + + // QuicDecrypter interface + virtual bool SetKey(StringPiece key) OVERRIDE { return true; } + virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE { + return true; + } + + virtual bool Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + unsigned char* output, + size_t* output_length) OVERRIDE { + if (ciphertext.size() < kTagSize) { + return false; + } + if (!CheckTag(ciphertext)) { + return false; + } + *output_length = ciphertext.size() - kTagSize; + memcpy(output, ciphertext.data(), *output_length); + return true; + } + + virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) OVERRIDE { + if (ciphertext.size() < kTagSize) { + return NULL; + } + if (!CheckTag(ciphertext)) { + return NULL; + } + const size_t len = ciphertext.size() - kTagSize; + uint8* buf = new uint8[len]; + memcpy(buf, ciphertext.data(), len); + return new QuicData(reinterpret_cast(buf), len, + true /* owns buffer */); + } + + virtual StringPiece GetKey() const OVERRIDE { return StringPiece(); } + virtual StringPiece GetNoncePrefix() const OVERRIDE { return StringPiece(); } + + private: + enum { + kTagSize = 16, + }; + + bool CheckTag(StringPiece ciphertext) { + uint8 tag = ciphertext.data()[ciphertext.size()-1]; + for (size_t i = ciphertext.size() - kTagSize; i < ciphertext.size(); i++) { + if (ciphertext.data()[i] != tag) { + return false; + } + } + + return true; + } +}; + class TestConnectionHelper : public QuicConnectionHelperInterface { public: TestConnectionHelper(MockClock* clock, MockRandom* random_generator) @@ -82,7 +208,8 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { QuicTime::Delta::FromMilliseconds(1))), timeout_alarm_(QuicTime::Zero()), blocked_(false), - is_server_(true) { + is_server_(true), + use_tagging_decrypter_(false) { } // QuicConnectionHelperInterface @@ -98,11 +225,15 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { virtual int WritePacketToWire(const QuicEncryptedPacket& packet, int* error) OVERRIDE { - QuicFramer framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - is_server_); + if (packet.length() >= sizeof(final_bytes_of_last_packet_)) { + memcpy(&final_bytes_of_last_packet_, packet.data() + packet.length() - 4, + sizeof(final_bytes_of_last_packet_)); + } + + QuicFramer framer(kQuicVersion1, QuicTime::Zero(), is_server_); + if (use_tagging_decrypter_) { + framer.SetDecrypter(new TaggingDecrypter); + } FramerVisitorCapturingFrames visitor; framer.set_visitor(&visitor); EXPECT_TRUE(framer.ProcessPacket(packet)); @@ -192,6 +323,16 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { void set_is_server(bool is_server) { is_server_ = is_server; } + // final_bytes_of_last_packet_ returns the last four bytes of the previous + // packet as a little-endian, uint32. This is intended to be used with a + // TaggingEncrypter so that tests can determine which encrypter was used for + // a given packet. + uint32 final_bytes_of_last_packet() { return final_bytes_of_last_packet_; } + + void use_tagging_decrypter() { + use_tagging_decrypter_ = true; + } + private: MockClock* clock_; MockRandom* random_generator_; @@ -207,6 +348,8 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { size_t last_packet_size_; bool blocked_; bool is_server_; + uint32 final_bytes_of_last_packet_; + bool use_tagging_decrypter_; DISALLOW_COPY_AND_ASSIGN(TestConnectionHelper); }; @@ -266,11 +409,7 @@ class QuicConnectionTest : public ::testing::Test { protected: QuicConnectionTest() : guid_(42), - framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false), + framer_(kQuicVersion1, QuicTime::Zero(), false), creator_(guid_, &framer_, QuicRandom::GetInstance(), false), send_algorithm_(new StrictMock), helper_(new TestConnectionHelper(&clock_, &random_generator_)), @@ -309,6 +448,14 @@ class QuicConnectionTest : public ::testing::Test { return helper_->last_packet_size(); } + uint32 final_bytes_of_last_packet() { + return helper_->final_bytes_of_last_packet(); + } + + void use_tagging_decrypter() { + helper_->use_tagging_decrypter(); + } + void ProcessPacket(QuicPacketSequenceNumber number) { EXPECT_CALL(visitor_, OnPacket(_, _, _, _)) .WillOnce(Return(accept_packet_)); @@ -323,7 +470,8 @@ class QuicConnectionTest : public ::testing::Test { SerializedPacket serialized_packet = creator_.SerializeAllFrames(frames); scoped_ptr packet(serialized_packet.packet); scoped_ptr encrypted( - framer_.EncryptPacket(serialized_packet.sequence_number, *packet)); + framer_.EncryptPacket(ENCRYPTION_NONE, + serialized_packet.sequence_number, *packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); return serialized_packet.entropy_hash; } @@ -345,8 +493,8 @@ class QuicConnectionTest : public ::testing::Test { bool entropy_flag) { scoped_ptr packet(ConstructDataPacket(number, fec_group, entropy_flag)); - scoped_ptr encrypted(framer_.EncryptPacket(number, - *packet)); + scoped_ptr encrypted(framer_.EncryptPacket( + ENCRYPTION_NONE, number, *packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); return encrypted->length(); } @@ -354,8 +502,8 @@ class QuicConnectionTest : public ::testing::Test { void ProcessClosePacket(QuicPacketSequenceNumber number, QuicFecGroupNumber fec_group) { scoped_ptr packet(ConstructClosePacket(number, fec_group)); - scoped_ptr encrypted(framer_.EncryptPacket(number, - *packet)); + scoped_ptr encrypted(framer_.EncryptPacket( + ENCRYPTION_NONE, number, *packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } @@ -409,7 +557,7 @@ class QuicConnectionTest : public ::testing::Test { scoped_ptr fec_packet( framer_.ConstructFecPacket(header_, fec_data).packet); scoped_ptr encrypted( - framer_.EncryptPacket(number, *fec_packet)); + framer_.EncryptPacket(ENCRYPTION_NONE, number, *fec_packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); return encrypted->length(); @@ -1150,6 +1298,41 @@ TEST_F(QuicConnectionTest, TestRetransmit) { EXPECT_EQ(2u, outgoing_ack()->sent_info.least_unacked); } +TEST_F(QuicConnectionTest, RetransmitWithSameEncryptionLevel) { + const QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + + QuicTime default_retransmission_time = clock_.ApproximateNow().Add( + 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. + connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + EXPECT_EQ(0x01010101u, final_bytes_of_last_packet()); + + connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + EXPECT_EQ(0x02020202u, final_bytes_of_last_packet()); + + EXPECT_EQ(default_retransmission_time, helper_->retransmission_alarm()); + // Simulate the retransimission alarm firing + clock_.AdvanceTime(kDefaultRetransmissionTime); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + connection_.RetransmitPacket(1); + // Packet should have been sent with ENCRYPTION_NONE. + EXPECT_EQ(0x01010101u, final_bytes_of_last_packet()); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + connection_.RetransmitPacket(2); + // Packet should have been sent with ENCRYPTION_INITIAL. + EXPECT_EQ(0x02020202u, final_bytes_of_last_packet()); +} + TEST_F(QuicConnectionTest, TestRetransmitOrder) { QuicByteCount first_packet_size; EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce( @@ -1355,7 +1538,7 @@ TEST_F(QuicConnectionTest, SendScheduler) { testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } @@ -1367,7 +1550,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelay) { testing::Return(QuicTime::Delta::FromMicroseconds(1))); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } @@ -1378,7 +1561,7 @@ TEST_F(QuicConnectionTest, SendSchedulerForce) { TimeUntilSend(_, IS_RETRANSMISSION, _)).Times(0); EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); // XXX: fixme. was: connection_.SendOrQueuePacket(1, packet, kForce); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } @@ -1391,7 +1574,7 @@ TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } @@ -1402,7 +1585,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler @@ -1457,13 +1640,13 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Attempt to send another packet and make sure that it gets queued. packet = ConstructDataPacket(2, 0, !kEntropyFlag); connection_.SendOrQueuePacket( - 2, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 2, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(2u, connection_.NumQueuedPackets()); } @@ -1473,7 +1656,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Now send non-retransmitting information, that we're not going to @@ -1498,7 +1681,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Now send non-retransmitting information, that we're not going to @@ -1518,7 +1701,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // OnCanWrite should not send the packet (because of the delay) @@ -1570,7 +1753,7 @@ TEST_F(QuicConnectionTest, SendWhenDisconnected) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); connection_.SendOrQueuePacket( - 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); } TEST_F(QuicConnectionTest, PublicReset) { @@ -1689,7 +1872,8 @@ TEST_F(QuicConnectionTest, CheckSentEntropyHash) { } QuicPacket* packet = ConstructDataPacket(i, 0, entropy_flag); connection_.SendOrQueuePacket( - i, packet, packet_entropy_hash, HAS_RETRANSMITTABLE_DATA); + ENCRYPTION_NONE, i, packet, packet_entropy_hash, + HAS_RETRANSMITTABLE_DATA); if (is_missing) { missing_packets.insert(i); @@ -1723,7 +1907,8 @@ TEST_F(QuicConnectionTest, SendVersionNegotiationPacket) { frames.push_back(frame); scoped_ptr packet( framer_.ConstructFrameDataPacket(header, frames).packet); - scoped_ptr encrypted(framer_.EncryptPacket(12, *packet)); + scoped_ptr encrypted( + framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); QuicFramerPeer::SetVersion(&framer_, kQuicVersion1); connection_.set_is_server(true); diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index 97b2b78b24abb9..03b5c2882f294e 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -6,6 +6,7 @@ #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/null_encrypter.h" #include "net/quic/crypto/proof_verifier.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" @@ -22,7 +23,6 @@ QuicCryptoClientStream::QuicCryptoClientStream( num_client_hellos_(0), config_(config), crypto_config_(crypto_config), - decrypter_pushed_(false), server_hostname_(server_hostname) { } @@ -118,9 +118,24 @@ void QuicCryptoClientStream::DoHandshakeLoop( DLOG(INFO) << "Client Sending: " << out.DebugString(); SendHandshakeMessage(out); // Be prepared to decrypt with the new server write key. - session()->connection()->PushDecrypter( - crypto_negotiated_params_.decrypter.release()); - decrypter_pushed_ = true; + session()->connection()->SetAlternativeDecrypter( + crypto_negotiated_params_.decrypter.release(), + true /* latch once used */); + // Send subsequent packets under encryption on the assumption that the + // server will accept the handshake. + session()->connection()->SetEncrypter( + ENCRYPTION_INITIAL, + crypto_negotiated_params_.encrypter.release()); + session()->connection()->SetDefaultEncryptionLevel( + ENCRYPTION_INITIAL); + if (!encryption_established_) { + session()->OnCryptoHandshakeEvent( + QuicSession::ENCRYPTION_FIRST_ESTABLISHED); + encryption_established_ = true; + } else { + session()->OnCryptoHandshakeEvent( + QuicSession::ENCRYPTION_REESTABLISHED); + } return; } case STATE_RECV_REJ: @@ -160,11 +175,9 @@ void QuicCryptoClientStream::DoHandshakeLoop( cached->SetProofValid(); } } - // Clear any new server write key that we may have set before. - if (decrypter_pushed_) { - session()->connection()->PopDecrypter(); - decrypter_pushed_ = false; - } + // Send the subsequent client hello in plaintext. + session()->connection()->SetDefaultEncryptionLevel( + ENCRYPTION_NONE); next_state_ = STATE_SEND_CHLO; break; case STATE_RECV_SHLO: @@ -179,15 +192,10 @@ void QuicCryptoClientStream::DoHandshakeLoop( "Expected SHLO or REJ"); return; } - // Receiving SHLO implies the server must have processed our full - // CHLO and is ready to decrypt with the new client write key. We - // can start to encrypt with the new client write key. - // TODO(wtc): when we support 0-RTT, we will need to change the - // encrypter when we send a full CHLO because we will be sending - // application data immediately after. - session()->connection()->ChangeEncrypter( - crypto_negotiated_params_.encrypter.release()); - SetHandshakeComplete(QUIC_NO_ERROR); + // TODO(agl): enable this once the tests are corrected to permit it. + // DCHECK(session()->connection()->alternative_decrypter() == NULL); + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); return; case STATE_IDLE: // This means that the peer sent us a message that we weren't expecting. diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 3381c8af07b5d2..740c8921936022 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -70,12 +70,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { QuicNegotiatedParameters negotiated_params_; QuicCryptoNegotiatedParameters crypto_negotiated_params_; - // decrypter_pushed_ is true if we have installed a QuicDecrypter in the - // connection. We need to track this because, in the event of a handshake - // failure, we have to remove any previous decrypters as they will have the - // wrong keys. - bool decrypter_pushed_; - // Client's connection nonce (4-byte timestamp + 28 random bytes) std::string nonce_; // Server's hostname diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index a840c3c31cf2c1..96547e42fc6b92 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -98,7 +98,8 @@ TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { return; } - EXPECT_FALSE(stream_.handshake_complete()); + EXPECT_FALSE(stream_.encryption_established()); + EXPECT_FALSE(stream_.handshake_confirmed()); } TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { @@ -108,7 +109,8 @@ TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { } CompleteCryptoHandshake(); - EXPECT_TRUE(stream_.handshake_complete()); + EXPECT_TRUE(stream_.encryption_established()); + EXPECT_TRUE(stream_.handshake_confirmed()); } TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 5fa33e198d8aa0..1dab72f6be44e9 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -27,8 +27,8 @@ QuicCryptoServerStream::~QuicCryptoServerStream() { void QuicCryptoServerStream::OnHandshakeMessage( const CryptoHandshakeMessage& message) { - // Do not process handshake messages after the handshake is complete. - if (handshake_complete()) { + // Do not process handshake messages after the handshake is confirmed. + if (handshake_confirmed_) { CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); return; } @@ -62,13 +62,18 @@ void QuicCryptoServerStream::OnHandshakeMessage( // write key. // // NOTE: the SHLO will be encrypted with the new server write key. - session()->connection()->ChangeEncrypter( + session()->connection()->SetEncrypter( + ENCRYPTION_INITIAL, crypto_negotiated_params_.encrypter.release()); - // Be prepared to decrypt with the new client write key, as the client - // will start to use it upon receiving the SHLO. - session()->connection()->PushDecrypter( + session()->connection()->SetDefaultEncryptionLevel( + ENCRYPTION_INITIAL); + // Set the decrypter immediately so that we no longer accept unencrypted + // packets. + session()->connection()->SetDecrypter( crypto_negotiated_params_.decrypter.release()); - SetHandshakeComplete(QUIC_NO_ERROR); + encryption_established_ = true; + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); } SendHandshakeMessage(reply); diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index 50fc2cae879e0a..2cc2746b6e1eb3 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -115,7 +115,8 @@ TEST_F(QuicCryptoServerStreamTest, NotInitiallyConected) { return; } - EXPECT_FALSE(stream_.handshake_complete()); + EXPECT_FALSE(stream_.encryption_established()); + EXPECT_FALSE(stream_.handshake_confirmed()); } TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { @@ -133,7 +134,8 @@ TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { // have sent the following client hello. // * One to get the server's certificates EXPECT_EQ(2, CompleteCryptoHandshake()); - EXPECT_TRUE(stream_.handshake_complete()); + EXPECT_TRUE(stream_.encryption_established()); + EXPECT_TRUE(stream_.handshake_confirmed()); } TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc index 9167e6f78ca099..df6c610813ba1a 100644 --- a/net/quic/quic_crypto_stream.cc +++ b/net/quic/quic_crypto_stream.cc @@ -18,7 +18,8 @@ namespace net { QuicCryptoStream::QuicCryptoStream(QuicSession* session) : ReliableQuicStream(kCryptoStreamId, session), - handshake_complete_(false) { + encryption_established_(false), + handshake_confirmed_(false) { crypto_framer_.set_visitor(this); } @@ -28,8 +29,8 @@ void QuicCryptoStream::OnError(CryptoFramer* framer) { uint32 QuicCryptoStream::ProcessData(const char* data, uint32 data_len) { - // Do not process handshake messages after the handshake is complete. - if (handshake_complete()) { + // Do not process handshake messages after the handshake is confirmed. + if (handshake_confirmed()) { CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); return 0; } @@ -49,11 +50,6 @@ void QuicCryptoStream::CloseConnectionWithDetails(QuicErrorCode error, session()->connection()->SendConnectionCloseWithDetails(error, details); } -void QuicCryptoStream::SetHandshakeComplete(QuicErrorCode error) { - handshake_complete_ = true; - session()->OnCryptoHandshakeComplete(error); -} - void QuicCryptoStream::SendHandshakeMessage( const CryptoHandshakeMessage& message) { const QuicData& data = message.GetSerialized(); diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h index b43cb38f2ea9c3..db8350330cb397 100644 --- a/net/quic/quic_crypto_stream.h +++ b/net/quic/quic_crypto_stream.h @@ -42,18 +42,19 @@ class NET_EXPORT_PRIVATE QuicCryptoStream // TODO(wtc): return a success/failure status. void SendHandshakeMessage(const CryptoHandshakeMessage& message); - bool handshake_complete() { return handshake_complete_; } + bool encryption_established() { return encryption_established_; } + bool handshake_confirmed() { return handshake_confirmed_; } protected: // Closes the connection void CloseConnection(QuicErrorCode error); void CloseConnectionWithDetails(QuicErrorCode error, const string& details); - void SetHandshakeComplete(QuicErrorCode error); + bool encryption_established_; + bool handshake_confirmed_; private: CryptoFramer crypto_framer_; - bool handshake_complete_; DISALLOW_COPY_AND_ASSIGN(QuicCryptoStream); }; diff --git a/net/quic/quic_crypto_stream_test.cc b/net/quic/quic_crypto_stream_test.cc index 8f8028b5d94b2e..6e465ab396acdf 100644 --- a/net/quic/quic_crypto_stream_test.cc +++ b/net/quic/quic_crypto_stream_test.cc @@ -74,7 +74,8 @@ class QuicCryptoStreamTest : public ::testing::Test { }; TEST_F(QuicCryptoStreamTest, NotInitiallyConected) { - EXPECT_FALSE(stream_.handshake_complete()); + EXPECT_FALSE(stream_.encryption_established()); + EXPECT_FALSE(stream_.handshake_confirmed()); } TEST_F(QuicCryptoStreamTest, OnErrorClosesConnection) { @@ -97,7 +98,7 @@ TEST_F(QuicCryptoStreamTest, ProcessData) { TEST_F(QuicCryptoStreamTest, ProcessBadData) { string bad(message_data_->data(), message_data_->length()); - bad[6] = 0x7F; // out of order tag + bad[8] = 0x7F; // out of order tag EXPECT_CALL(*connection_, SendConnectionClose(QUIC_CRYPTO_TAGS_OUT_OF_ORDER)); diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index fafd2e33be5367..70084a5bb354c2 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -46,8 +46,6 @@ QuicPacketSequenceNumber ClosestTo(QuicPacketSequenceNumber target, } // namespace QuicFramer::QuicFramer(QuicVersionTag version, - QuicDecrypter* decrypter, - QuicEncrypter* encrypter, QuicTime creation_time, bool is_server) : visitor_(NULL), @@ -55,11 +53,11 @@ QuicFramer::QuicFramer(QuicVersionTag version, error_(QUIC_NO_ERROR), last_sequence_number_(0), quic_version_(version), - decrypter_(decrypter), - encrypter_(encrypter), + decrypter_(QuicDecrypter::Create(kNULL)), is_server_(is_server), creation_time_(creation_time) { DCHECK(IsSupportedVersion(version)); + encrypter_[ENCRYPTION_NONE].reset(QuicEncrypter::Create(kNULL)); } QuicFramer::~QuicFramer() {} @@ -1037,27 +1035,47 @@ StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( kStartOfHashData); } -void QuicFramer::push_decrypter(QuicDecrypter* decrypter) { - DCHECK(backup_decrypter_.get() == NULL); - backup_decrypter_.reset(decrypter_.release()); +void QuicFramer::SetDecrypter(QuicDecrypter* decrypter) { + DCHECK(alternative_decrypter_.get() == NULL); decrypter_.reset(decrypter); } -void QuicFramer::pop_decrypter() { - DCHECK(backup_decrypter_.get() != NULL); - decrypter_.reset(backup_decrypter_.release()); +void QuicFramer::SetAlternativeDecrypter(QuicDecrypter* decrypter, + bool latch_once_used) { + alternative_decrypter_.reset(decrypter); + alternative_decrypter_latch_ = latch_once_used; } -void QuicFramer::set_encrypter(QuicEncrypter* encrypter) { - encrypter_.reset(encrypter); +const QuicDecrypter* QuicFramer::decrypter() const { + return decrypter_.get(); +} + +const QuicDecrypter* QuicFramer::alternative_decrypter() const { + return alternative_decrypter_.get(); +} + +void QuicFramer::SetEncrypter(EncryptionLevel level, + QuicEncrypter* encrypter) { + DCHECK_GE(level, 0); + DCHECK_LT(level, NUM_ENCRYPTION_LEVELS); + encrypter_[level].reset(encrypter); +} + +const QuicEncrypter* QuicFramer::encrypter(EncryptionLevel level) const { + DCHECK_GE(level, 0); + DCHECK_LT(level, NUM_ENCRYPTION_LEVELS); + DCHECK(encrypter_[level].get() != NULL); + return encrypter_[level].get(); } QuicEncryptedPacket* QuicFramer::EncryptPacket( + EncryptionLevel level, QuicPacketSequenceNumber packet_sequence_number, const QuicPacket& packet) { - scoped_ptr out(encrypter_->EncryptPacket(packet_sequence_number, - packet.AssociatedData(), - packet.Plaintext())); + DCHECK(encrypter_[level].get() != NULL); + + scoped_ptr out(encrypter_[level]->EncryptPacket( + packet_sequence_number, packet.AssociatedData(), packet.Plaintext())); if (out.get() == NULL) { RaiseError(QUIC_ENCRYPTION_FAILURE); return NULL; @@ -1072,7 +1090,22 @@ QuicEncryptedPacket* QuicFramer::EncryptPacket( } size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) { - return encrypter_->GetMaxPlaintextSize(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. + size_t min_plaintext_size = ciphertext_size; + + for (int i = ENCRYPTION_NONE; i <= ENCRYPTION_FORWARD_SECURE; i++) { + if (encrypter_[i].get() != NULL) { + size_t size = encrypter_[i]->GetMaxPlaintextSize(ciphertext_size); + if (size < min_plaintext_size) { + min_plaintext_size = size; + } + } + } + + return min_plaintext_size; } bool QuicFramer::DecryptPayload(QuicPacketSequenceNumber sequence_number, @@ -1083,19 +1116,32 @@ bool QuicFramer::DecryptPayload(QuicPacketSequenceNumber sequence_number, return false; } DCHECK(decrypter_.get() != NULL); + LOG(INFO) << "Decrypting packet"; decrypted_.reset(decrypter_->DecryptPacket( sequence_number, GetAssociatedDataFromEncryptedPacket(packet, version_flag), encrypted)); - if (decrypted_.get() == NULL && backup_decrypter_.get() != NULL) { - decrypted_.reset(backup_decrypter_->DecryptPacket( + if (decrypted_.get() == NULL && alternative_decrypter_.get() != NULL) { + LOG(INFO) << "Trying alternative"; + decrypted_.reset(alternative_decrypter_->DecryptPacket( sequence_number, GetAssociatedDataFromEncryptedPacket(packet, version_flag), encrypted)); + if (decrypted_.get() != NULL) { + LOG(INFO) << "alternative ok"; + if (alternative_decrypter_latch_) { + LOG(INFO) << " latching"; + // Switch to the alternative decrypter and latch so that we cannot + // switch back. + decrypter_.reset(alternative_decrypter_.release()); + } else { + LOG(INFO) << " swapping"; + // Switch the alternative decrypter so that we use it first next time. + decrypter_.swap(alternative_decrypter_); + } + } } - // TODO(wtc): tell the caller or visitor which decrypter was used, so that - // they can verify a packet that should be encrypted is encrypted. - // TODO(wtc): figure out when it is safe to delete backup_decrypter_. + if (decrypted_.get() == NULL) { return false; } diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index ff5e4f84af1e87..1802fed17bd71e 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -151,10 +151,9 @@ class NET_EXPORT_PRIVATE QuicReceivedEntropyHashCalculatorInterface { // in order to generate FEC data for subsequently building FEC packets. class NET_EXPORT_PRIVATE QuicFramer { public: - // Constructs a new framer that will own |decrypter| and |encrypter|. + // Constructs a new framer that installs a kNULL QuicEncrypter and + // QuicDecrypter for level ENCRYPTION_NONE. QuicFramer(QuicVersionTag quic_version, - QuicDecrypter* decrypter, - QuicEncrypter* encrypter, QuicTime creation_time, bool is_server); @@ -274,15 +273,32 @@ class NET_EXPORT_PRIVATE QuicFramer { const QuicPacketPublicHeader& header, const QuicVersionTagList& supported_versions); - QuicDecrypter* decrypter() const { return decrypter_.get(); } - void push_decrypter(QuicDecrypter* decrypter); - void pop_decrypter(); - - QuicEncrypter* encrypter() const { return encrypter_.get(); } - void set_encrypter(QuicEncrypter* encrypter); + // SetDecrypter sets the primary decrypter, replacing any that already exists, + // and takes ownership. If an alternative decrypter is in place then the + // function DCHECKs. This is intended for cases where one knows that future + // packets will be using the new decrypter and the previous decrypter is not + // obsolete. + void SetDecrypter(QuicDecrypter* decrypter); + + // SetAlternativeDecrypter sets a decrypter that may be used to decrypt + // future packets and takes ownership of it. If |latch_once_used| is true, + // then the first time that the decrypter is successful it will replace the + // primary decrypter. Otherwise both decrypters will remain active and the + // primary decrypter will be the one last used. + void SetAlternativeDecrypter(QuicDecrypter* decrypter, + bool latch_once_used); + + const QuicDecrypter* decrypter() const; + const QuicDecrypter* alternative_decrypter() const; + + // Changes the encrypter used for level |level| to |encrypter|. The function + // takes ownership of |encrypter|. + void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter); + const QuicEncrypter* encrypter(EncryptionLevel level) const; // Returns a new encrypted packet, owned by the caller. - QuicEncryptedPacket* EncryptPacket(QuicPacketSequenceNumber sequence_number, + QuicEncryptedPacket* EncryptPacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, const QuicPacket& packet); // Returns the maximum length of plaintext that can be encrypted @@ -383,10 +399,14 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicVersionTag quic_version_; // Primary decrypter used to decrypt packets during parsing. scoped_ptr decrypter_; - // Backup decrypter used to decrypt packets during parsing. May be NULL. - scoped_ptr backup_decrypter_; - // Encrypter used to encrypt packets via EncryptPacket(). - scoped_ptr encrypter_; + // Alternative decrypter that can also be used to decrypt packets. + scoped_ptr alternative_decrypter_; + // alternative_decrypter_latch_is true if, when |alternative_decrypter_| + // successfully decrypts a packet, we should install it as the only + // decrypter. + bool alternative_decrypter_latch_; + // Encrypters used to encrypt packets via EncryptPacket(). + scoped_ptr encrypter_[NUM_ENCRYPTION_LEVELS]; // Tracks if the framer is being used by the entity that received the // connection or the entity that initiated it. bool is_server_; diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index ebb8cd057386aa..6e2556cd947a85 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -279,7 +279,9 @@ class QuicFramerTest : public ::testing::Test { : encrypter_(new test::TestEncrypter()), decrypter_(new test::TestDecrypter()), start_(QuicTime::Zero().Add(QuicTime::Delta::FromMicroseconds(0x10))), - framer_(kQuicVersion1, decrypter_, encrypter_, start_, true) { + framer_(kQuicVersion1, start_, true) { + framer_.SetDecrypter(decrypter_); + framer_.SetEncrypter(ENCRYPTION_NONE, encrypter_); framer_.set_visitor(&visitor_); framer_.set_entropy_calculator(&entropy_calculator_); } @@ -2453,7 +2455,7 @@ TEST_F(QuicFramerTest, EncryptPacket) { QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false, !kIncludeVersion)); scoped_ptr encrypted( - framer_.EncryptPacket(sequence_number, *raw)); + framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); ASSERT_TRUE(encrypted.get() != NULL); EXPECT_TRUE(CheckEncryption(sequence_number, raw.get())); @@ -2488,7 +2490,7 @@ TEST_F(QuicFramerTest, EncryptPacketWithVersionFlag) { QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false, kIncludeVersion)); scoped_ptr encrypted( - framer_.EncryptPacket(sequence_number, *raw)); + framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); ASSERT_TRUE(encrypted.get() != NULL); EXPECT_TRUE(CheckEncryption(sequence_number, raw.get())); @@ -2549,7 +2551,8 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) { ASSERT_TRUE(raw_ack_packet != NULL); scoped_ptr ack_packet( - framer_.EncryptPacket(header.packet_sequence_number, *raw_ack_packet)); + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet)); // Create a packet with just connection close. frames.clear(); @@ -2562,7 +2565,8 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) { ASSERT_TRUE(raw_close_packet != NULL); scoped_ptr close_packet( - framer_.EncryptPacket(header.packet_sequence_number, *raw_close_packet)); + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_close_packet)); // Now make sure we can turn our ack packet back into an ack frame ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); @@ -2604,7 +2608,8 @@ TEST_F(QuicFramerTest, CleanTruncation) { ASSERT_TRUE(raw_ack_packet != NULL); scoped_ptr ack_packet( - framer_.EncryptPacket(header.packet_sequence_number, *raw_ack_packet)); + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet)); // Create a packet with just connection close. frames.clear(); @@ -2617,7 +2622,8 @@ TEST_F(QuicFramerTest, CleanTruncation) { ASSERT_TRUE(raw_close_packet != NULL); scoped_ptr close_packet( - framer_.EncryptPacket(header.packet_sequence_number, *raw_close_packet)); + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_close_packet)); // Now make sure we can turn our ack packet back into an ack frame ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index e5bcdcccf10e8e..3068d5fcd48080 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -118,11 +118,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam { use_closing_stream_(false), read_buffer_(new IOBufferWithSize(4096)), guid_(2), - framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false), + framer_(kQuicVersion1, QuicTime::Zero(), false), creator_(guid_, &framer_, &random_, false) { IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); @@ -186,7 +182,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam { "www.google.com", &crypto_config_, NULL)); session_->GetCryptoStream()->CryptoConnect(); - EXPECT_TRUE(session_->IsCryptoHandshakeComplete()); + EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed()); QuicReliableClientStream* stream = session_->CreateOutgoingReliableStream(); stream_.reset(use_closing_stream_ ? @@ -298,7 +294,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam { frames.push_back(frame); scoped_ptr packet( framer_.ConstructFrameDataPacket(header_, frames).packet); - return framer_.EncryptPacket(header.packet_sequence_number, *packet); + return framer_.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet); } const QuicGuid guid_; diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index 240728bf530e93..3f7cf12c827548 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc @@ -121,18 +121,14 @@ class QuicNetworkTransactionTest : public PlatformTest { feedback.tcp.accumulated_number_of_lost_packets = 0; feedback.tcp.receive_window = 256000; - QuicFramer framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false); + QuicFramer framer(kQuicVersion1, QuicTime::Zero(), false); QuicFrames frames; frames.push_back(QuicFrame(&ack)); frames.push_back(QuicFrame(&feedback)); scoped_ptr packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } std::string GetRequestString(const std::string& method, @@ -178,17 +174,13 @@ class QuicNetworkTransactionTest : public PlatformTest { scoped_ptr ConstructPacket( const QuicPacketHeader& header, const QuicFrame& frame) { - QuicFramer framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false); + QuicFramer framer(kQuicVersion1, QuicTime::Zero(), false); QuicFrames frames; frames.push_back(frame); scoped_ptr packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } void InitializeHeader(QuicPacketSequenceNumber sequence_number, @@ -496,5 +488,69 @@ TEST_F(QuicNetworkTransactionTest, DontUseAlternateProtocolForQuicHttps) { EXPECT_EQ("hello!", response_data); } +TEST_F(QuicNetworkTransactionTest, ZeroRTT) { + HttpStreamFactory::set_use_alternate_protocols(true); + HttpStreamFactory::SetNextProtos(QuicNextProtos()); + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + scoped_ptr data( + ConstructDataPacket(1, true, true, 0, GetRequestString("GET", "/"))); + scoped_ptr ack(ConstructAckPacket(1, 0)); + + MockWrite quic_writes[] = { + MockWrite(SYNCHRONOUS, data->data(), data->length()), + MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + }; + + scoped_ptr resp( + ConstructDataPacket( + 1, false, true, 0, GetResponseString("200 OK", "hello!"))); + MockRead quic_reads[] = { + MockRead(SYNCHRONOUS, resp->data(), resp->length()), + MockRead(ASYNC, OK), // EOF + }; + + DelayedSocketData quic_data( + 1, // wait for one write to finish before reading. + quic_reads, arraysize(quic_reads), + quic_writes, arraysize(quic_writes)); + + socket_factory_.AddSocketDataProvider(&quic_data); + + TestCompletionCallback callback; + + CreateSession(); + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::ZERO_RTT); + + HttpServerProperties* http_server_properties = + session_->http_server_properties(); + http_server_properties->SetAlternateProtocol( + HostPortPair("www.google.com", 80), 80, QUIC); + + scoped_ptr trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session_)); + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_TRUE(response->was_npn_negotiated); + EXPECT_EQ("quic/1+spdy/3", response->npn_negotiated_protocol); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello!", response_data); +} + } // namespace test } // namespace net diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index 60d48d04a8559a..4fe4f183b1b99c 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -30,16 +30,8 @@ namespace { class QuicPacketCreatorTest : public ::testing::TestWithParam { protected: QuicPacketCreatorTest() - : server_framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - true), - client_framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false), + : server_framer_(kQuicVersion1, QuicTime::Zero(), true), + client_framer_(kQuicVersion1, QuicTime::Zero(), false), id_(1), sequence_number_(0), guid_(2), @@ -53,7 +45,8 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam { void ProcessPacket(QuicPacket* packet) { scoped_ptr encrypted( - server_framer_.EncryptPacket(sequence_number_, *packet)); + server_framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number_, + *packet)); server_framer_.ProcessPacket(*encrypted); } diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc index 4e84d0bbf86235..edf67e9e5995ad 100644 --- a/net/quic/quic_packet_generator_test.cc +++ b/net/quic/quic_packet_generator_test.cc @@ -76,11 +76,7 @@ struct PacketContents { class QuicPacketGeneratorTest : public ::testing::Test { protected: QuicPacketGeneratorTest() - : framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false), + : framer_(kQuicVersion1, QuicTime::Zero(), false), creator_(42, &framer_, &random_, false), generator_(&delegate_, &creator_), packet_(0, NULL, 0, NULL), diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index faa5c787569031..27f980fa8be371 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -274,7 +274,9 @@ StringPiece QuicPacket::Plaintext() const { length() - start_of_encrypted_data); } -RetransmittableFrames::RetransmittableFrames() {} +RetransmittableFrames::RetransmittableFrames() + : encryption_level_(NUM_ENCRYPTION_LEVELS) { +} RetransmittableFrames::~RetransmittableFrames() { for (QuicFrames::iterator it = frames_.begin(); it != frames_.end(); ++it) { @@ -326,6 +328,10 @@ const QuicFrame& RetransmittableFrames::AddNonStreamFrame( return frames_.back(); } +void RetransmittableFrames::set_encryption_level(EncryptionLevel level) { + encryption_level_ = level; +} + ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) { os << s.length() << "-byte data"; return os; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index d4f23ded364300..efb2459e3a98f0 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -224,6 +224,8 @@ enum QuicErrorCode { QUIC_CRYPTO_TOO_MANY_REJECTS, // The client rejected the server's certificate chain or signature. QUIC_PROOF_INVALID, + // A crypto message was received with a duplicate tag. + QUIC_CRYPTO_DUPLICATE_TAG, // No error. Used as bound while iterating. QUIC_LAST_ERROR, @@ -461,6 +463,18 @@ struct NET_EXPORT_PRIVATE QuicGoAwayFrame { std::string reason_phrase; }; +// EncryptionLevel enumerates the stages of encryption that a QUIC connection +// progresses through. When retransmitting a packet, the encryption level needs +// to be specified so that it is retransmitted at a level which the peer can +// understand. +enum EncryptionLevel { + ENCRYPTION_NONE = 0, + ENCRYPTION_INITIAL = 1, + ENCRYPTION_FORWARD_SECURE = 2, + + NUM_ENCRYPTION_LEVELS, +}; + struct NET_EXPORT_PRIVATE QuicFrame { QuicFrame() {} explicit QuicFrame(QuicPaddingFrame* padding_frame) @@ -628,8 +642,14 @@ class NET_EXPORT_PRIVATE RetransmittableFrames { const QuicFrame& AddNonStreamFrame(const QuicFrame& frame); const QuicFrames& frames() const { return frames_; } + void set_encryption_level(EncryptionLevel level); + EncryptionLevel encryption_level() const { + return encryption_level_; + } + private: QuicFrames frames_; + EncryptionLevel encryption_level_; // Data referenced by the StringPiece of a QuicStreamFrame. std::vector stream_data_; diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 8e9e4117a9ba50..35f764fd1bbc9d 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -190,12 +190,15 @@ void QuicSession::CloseStream(QuicStreamId stream_id) { stream->OnClose(); } -bool QuicSession::IsCryptoHandshakeComplete() { - return GetCryptoStream()->handshake_complete(); +bool QuicSession::IsEncryptionEstablished() { + return GetCryptoStream()->encryption_established(); } -void QuicSession::OnCryptoHandshakeComplete(QuicErrorCode error) { - // TODO(rch): tear down the connection if error != QUIC_NO_ERROR. +bool QuicSession::IsCryptoHandshakeConfirmed() { + return GetCryptoStream()->handshake_confirmed(); +} + +void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { } void QuicSession::ActivateStream(ReliableQuicStream* stream) { diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index 963a53a37fed96..9d2d2b7cf5ed8b 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -31,6 +31,23 @@ class QuicSessionPeer; class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { public: + // CryptoHandshakeEvent enumerates the events generated by a QuicCryptoStream. + enum CryptoHandshakeEvent { + // ENCRYPTION_FIRST_ESTABLISHED indicates that a full client hello has been + // sent by a client and that subsequent packets will be encrypted. (Client + // only.) + ENCRYPTION_FIRST_ESTABLISHED, + // ENCRYPTION_REESTABLISHED indicates that a client hello was rejected by + // the server and thus the encryption key has been updated. Therefore the + // connection should resend any packets that were sent under + // ENCRYPTION_INITIAL. (Client only.) + ENCRYPTION_REESTABLISHED, + // HANDSHAKE_CONFIRMED, in a client, indicates the the server has accepted + // our handshake. In a server it indicates that a full, valid client hello + // has been received. (Client and server.) + HANDSHAKE_CONFIRMED, + }; + QuicSession(QuicConnection* connection, bool is_server); virtual ~QuicSession(); @@ -65,13 +82,23 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Removes the stream associated with 'stream_id' from the active stream map. virtual void CloseStream(QuicStreamId stream_id); - // Returns true once the crypto handshake is complete. - virtual bool IsCryptoHandshakeComplete(); - - // Called by the QuicCryptoStream when the handshake completes. - // If |error| is QUIC_NO_ERROR then the handshake was succesful, - // otherwise it failed. - virtual void OnCryptoHandshakeComplete(QuicErrorCode error); + // Returns true if outgoing packets will be encrypted, even if the server + // hasn't confirmed the handshake yet. + virtual bool IsEncryptionEstablished(); + + // For a client, returns true if the server has confirmed our handshake. For + // a server, returns true if a full, valid client hello has been received. + virtual bool IsCryptoHandshakeConfirmed(); + + // Called by the QuicCryptoStream when the handshake enters a new state. + // + // Clients will call this function in the order: + // ENCRYPTION_FIRST_ESTABLISHED + // zero or more ENCRYPTION_REESTABLISHED + // HANDSHAKE_CONFIRMED + // + // Servers will simply call it once with HANDSHAKE_CONFIRMED. + virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event); // Returns true if the stream existed previously and has been closed. // Returns false if the stream is still active or if the stream has diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index bd18ec8a4155e2..f9936258d696d4 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -30,7 +30,9 @@ class TestCryptoStream : public QuicCryptoStream { virtual void OnHandshakeMessage( const CryptoHandshakeMessage& message) OVERRIDE { - SetHandshakeComplete(QUIC_NO_ERROR); + encryption_established_ = true; + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); } }; @@ -115,11 +117,11 @@ class QuicSessionTest : public ::testing::Test { set closed_streams_; }; -TEST_F(QuicSessionTest, IsCryptoHandshakeComplete) { - EXPECT_FALSE(session_.IsCryptoHandshakeComplete()); +TEST_F(QuicSessionTest, IsCryptoHandshakeConfirmed) { + EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); CryptoHandshakeMessage message; session_.crypto_stream_.OnHandshakeMessage(message); - EXPECT_TRUE(session_.IsCryptoHandshakeComplete()); + EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed()); } TEST_F(QuicSessionTest, IsClosedStreamDefault) { diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index c9c917733e9487..0ebeb79d75d7d3 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc @@ -71,18 +71,14 @@ class QuicStreamFactoryTest : public ::testing::Test { feedback.tcp.accumulated_number_of_lost_packets = 0; feedback.tcp.receive_window = 16000; - QuicFramer framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false); + QuicFramer framer(kQuicVersion1, QuicTime::Zero(), false); QuicFrames frames; frames.push_back(QuicFrame(&ack)); frames.push_back(QuicFrame(&feedback)); scoped_ptr packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } // Returns a newly created packet to send congestion feedback data. @@ -110,17 +106,13 @@ class QuicStreamFactoryTest : public ::testing::Test { scoped_ptr ConstructPacket( const QuicPacketHeader& header, const QuicFrame& frame) { - QuicFramer framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false); + QuicFramer framer(kQuicVersion1, QuicTime::Zero(), false); QuicFrames frames; frames.push_back(frame); scoped_ptr packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } MockHostResolver host_resolver_; diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 6de120d2657f6e..8a9d01b7432d26 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -90,6 +90,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT); RETURN_STRING_LITERAL(QUIC_PROOF_INVALID); + RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG); RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 95049492572925..4fec83d5dab22c 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -110,7 +110,7 @@ void CryptoTestUtils::CommunicateHandshakeMessages( PacketSavingConnection* b_conn, QuicCryptoStream* b) { size_t a_i = 0, b_i = 0; - while (!a->handshake_complete()) { + while (!a->handshake_confirmed()) { ASSERT_GT(a_conn->packets_.size(), a_i); LOG(INFO) << "Processing " << a_conn->packets_.size() - a_i << " packets a->b"; @@ -218,22 +218,29 @@ string CryptoTestUtils::GetValueForTag(const CryptoHandshakeMessage& message, void CryptoTestUtils::CompareClientAndServerKeys( QuicCryptoClientStream* client, QuicCryptoServerStream* server) { - StringPiece client_encrypter_key = - client->session()->connection()->encrypter()->GetKey(); - StringPiece client_encrypter_iv = - client->session()->connection()->encrypter()->GetNoncePrefix(); - StringPiece client_decrypter_key = - client->session()->connection()->decrypter()->GetKey(); - StringPiece client_decrypter_iv = - client->session()->connection()->decrypter()->GetNoncePrefix(); - StringPiece server_encrypter_key = - server->session()->connection()->encrypter()->GetKey(); - StringPiece server_encrypter_iv = - server->session()->connection()->encrypter()->GetNoncePrefix(); - StringPiece server_decrypter_key = - server->session()->connection()->decrypter()->GetKey(); - StringPiece server_decrypter_iv = - server->session()->connection()->decrypter()->GetNoncePrefix(); + const QuicEncrypter* client_encrypter( + client->session()->connection()->encrypter(ENCRYPTION_INITIAL)); + // Normally we would expect the client's INITIAL decrypter to have latched + // from the receipt of the server hello. However, when using a + // PacketSavingConnection (at the tests do) we don't actually encrypt with + // the correct encrypter. + // TODO(agl): make the tests more realistic. + const QuicDecrypter* client_decrypter( + client->session()->connection()->alternative_decrypter()); + const QuicEncrypter* server_encrypter( + server->session()->connection()->encrypter(ENCRYPTION_INITIAL)); + const QuicDecrypter* server_decrypter( + server->session()->connection()->decrypter()); + + StringPiece client_encrypter_key = client_encrypter->GetKey(); + StringPiece client_encrypter_iv = client_encrypter->GetNoncePrefix(); + StringPiece client_decrypter_key = client_decrypter->GetKey(); + StringPiece client_decrypter_iv = client_decrypter->GetNoncePrefix(); + StringPiece server_encrypter_key = server_encrypter->GetKey(); + StringPiece server_encrypter_iv = server_encrypter->GetNoncePrefix(); + StringPiece server_decrypter_key = server_decrypter->GetKey(); + StringPiece server_decrypter_iv = server_decrypter->GetNoncePrefix(); + CompareCharArraysWithHexError("client write key", client_encrypter_key.data(), client_encrypter_key.length(), diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc index c63d0f01ede890..8ac7c126413667 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.cc +++ b/net/quic/test_tools/mock_crypto_client_stream.cc @@ -10,8 +10,10 @@ MockCryptoClientStream::MockCryptoClientStream( const string& server_hostname, const QuicConfig& config, QuicSession* session, - QuicCryptoClientConfig* crypto_config) - : QuicCryptoClientStream(server_hostname, config, session, crypto_config) { + QuicCryptoClientConfig* crypto_config, + HandshakeMode handshake_mode) + : QuicCryptoClientStream(server_hostname, config, session, crypto_config), + handshake_mode_(handshake_mode) { } MockCryptoClientStream::~MockCryptoClientStream() { @@ -23,7 +25,17 @@ void MockCryptoClientStream::OnHandshakeMessage( } bool MockCryptoClientStream::CryptoConnect() { - SetHandshakeComplete(QUIC_NO_ERROR); + if (handshake_mode_ == ZERO_RTT) { + encryption_established_ = true; + handshake_confirmed_ = false; + session()->OnCryptoHandshakeEvent( + QuicSession::ENCRYPTION_FIRST_ESTABLISHED); + return true; + } + + encryption_established_ = true; + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); return true; } diff --git a/net/quic/test_tools/mock_crypto_client_stream.h b/net/quic/test_tools/mock_crypto_client_stream.h index 2d9fdc322104e2..f244ca9595869d 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.h +++ b/net/quic/test_tools/mock_crypto_client_stream.h @@ -15,10 +15,24 @@ namespace net { class MockCryptoClientStream : public QuicCryptoClientStream { public: - MockCryptoClientStream(const string& server_hostname, - const QuicConfig& config, - QuicSession* session, - QuicCryptoClientConfig* crypto_config); + // HandshakeMode enumerates the handshake mode MockCryptoClientStream should + // mock in CryptoConnect. + enum HandshakeMode { + // CONFIRM_HANDSHAKE indicates that CryptoConnect will immediately confirm + // the handshake and establish encryption. + CONFIRM_HANDSHAKE, + + // ZERO_RTT indicates that CryptoConnect will establish encryption but will + // not confirm the handshake. + ZERO_RTT, + }; + + MockCryptoClientStream( + const string& server_hostname, + const QuicConfig& config, + QuicSession* session, + QuicCryptoClientConfig* crypto_config, + HandshakeMode handshake_mode); virtual ~MockCryptoClientStream(); // CryptoFramerVisitorInterface implementation. @@ -27,6 +41,8 @@ class MockCryptoClientStream : public QuicCryptoClientStream { // QuicCryptoClientStream implementation. virtual bool CryptoConnect() OVERRIDE; + + HandshakeMode handshake_mode_; }; } // namespace net diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/net/quic/test_tools/mock_crypto_client_stream_factory.cc index f774328f2cc428..20926ce1add1a4 100644 --- a/net/quic/test_tools/mock_crypto_client_stream_factory.cc +++ b/net/quic/test_tools/mock_crypto_client_stream_factory.cc @@ -6,12 +6,15 @@ #include "base/lazy_instance.h" #include "net/quic/quic_crypto_client_stream.h" -#include "net/quic/test_tools/mock_crypto_client_stream.h" using std::string; namespace net { +MockCryptoClientStreamFactory::MockCryptoClientStreamFactory() + : handshake_mode_(MockCryptoClientStream::CONFIRM_HANDSHAKE) { +} + QuicCryptoClientStream* MockCryptoClientStreamFactory::CreateQuicCryptoClientStream( const string& server_hostname, @@ -19,7 +22,7 @@ MockCryptoClientStreamFactory::CreateQuicCryptoClientStream( QuicSession* session, QuicCryptoClientConfig* crypto_config) { return new MockCryptoClientStream(server_hostname, config, session, - crypto_config); + crypto_config, handshake_mode_); } } // namespace net diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.h b/net/quic/test_tools/mock_crypto_client_stream_factory.h index 445e7667cc6f82..01d0c75bbbc091 100644 --- a/net/quic/test_tools/mock_crypto_client_stream_factory.h +++ b/net/quic/test_tools/mock_crypto_client_stream_factory.h @@ -10,11 +10,14 @@ #include "net/quic/quic_crypto_client_stream.h" #include "net/quic/quic_crypto_client_stream_factory.h" #include "net/quic/quic_session.h" +#include "net/quic/test_tools/mock_crypto_client_stream.h" namespace net { class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { public: + MockCryptoClientStreamFactory(); + virtual ~MockCryptoClientStreamFactory() {} virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( @@ -22,6 +25,14 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config) OVERRIDE; + + void set_handshake_mode( + MockCryptoClientStream::HandshakeMode handshake_mode) { + handshake_mode_ = handshake_mode; + } + + private: + MockCryptoClientStream::HandshakeMode handshake_mode_; }; } // namespace net diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index 6777810af85006..07d1f20a7fc9bc 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -218,6 +218,7 @@ PacketSavingConnection::~PacketSavingConnection() { } bool PacketSavingConnection::SendOrQueuePacket( + EncryptionLevel level, QuicPacketSequenceNumber sequence_number, QuicPacket* packet, QuicPacketEntropyHash entropy_hash, @@ -329,11 +330,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( bool should_include_version) { CryptoFramer crypto_framer; scoped_ptr data(crypto_framer.ConstructHandshakeMessage(message)); - QuicFramer quic_framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false); + QuicFramer quic_framer(kQuicVersion1, QuicTime::Zero(), false); QuicPacketHeader header; header.public_header.guid = guid; diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index cec4b319830f39..7d340313d0f807 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -250,6 +250,7 @@ class PacketSavingConnection : public MockConnection { virtual ~PacketSavingConnection(); virtual bool SendOrQueuePacket( + EncryptionLevel level, QuicPacketSequenceNumber sequence_number, QuicPacket* packet, QuicPacketEntropyHash entropy_hash, diff --git a/net/quic/test_tools/simple_quic_framer.cc b/net/quic/test_tools/simple_quic_framer.cc index 7bd1ff10f57957..546ae9922ca69c 100644 --- a/net/quic/test_tools/simple_quic_framer.cc +++ b/net/quic/test_tools/simple_quic_framer.cc @@ -128,11 +128,7 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { }; SimpleQuicFramer::SimpleQuicFramer() - : framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - true), + : framer_(kQuicVersion1, QuicTime::Zero(), true), visitor_(NULL) { } @@ -140,7 +136,8 @@ SimpleQuicFramer::~SimpleQuicFramer() { } bool SimpleQuicFramer::ProcessPacket(const QuicPacket& packet) { - scoped_ptr encrypted(framer_.EncryptPacket(0, packet)); + scoped_ptr encrypted(framer_.EncryptPacket( + ENCRYPTION_NONE, 0, packet)); return ProcessPacket(*encrypted); } diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index ff8502f012d6fb..5054eb91dd35ae 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc @@ -121,7 +121,7 @@ bool QuicClient::Connect() { if (!StartConnect()) { return false; } - while (CryptoHandshakeInProgress()) { + while (EncryptionBeingEstablished()) { WaitForEvents(); } return session_->connection()->connected(); @@ -141,8 +141,8 @@ bool QuicClient::StartConnect() { return session_->CryptoConnect(); } -bool QuicClient::CryptoHandshakeInProgress() { - return !session_->IsCryptoHandshakeComplete() && +bool QuicClient::EncryptionBeingEstablished() { + return !session_->IsEncryptionEstablished() && session_->connection()->connected(); } diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h index fa3e014af39724..2d19e6c7d48585 100644 --- a/net/tools/quic/quic_client.h +++ b/net/tools/quic/quic_client.h @@ -43,10 +43,10 @@ class QuicClient : public EpollCallbackInterface { // completes. bool StartConnect(); - // Returns true if the crypto handshake is in progress. - // Returns false if the handshake is complete or the connection has been - // closed. - bool CryptoHandshakeInProgress(); + // Returns true if the crypto handshake has yet to establish encryption. + // Returns false if encryption is active (even if the server hasn't confirmed + // the handshake) or if the connection has been closed. + bool EncryptionBeingEstablished(); // Disconnects from the QUIC server. void Disconnect(); diff --git a/net/tools/quic/quic_client_session.cc b/net/tools/quic/quic_client_session.cc index 6fdf11ec12a5a4..e3394d31d2faf5 100644 --- a/net/tools/quic/quic_client_session.cc +++ b/net/tools/quic/quic_client_session.cc @@ -27,8 +27,8 @@ QuicClientSession::~QuicClientSession() { } QuicReliableClientStream* QuicClientSession::CreateOutgoingReliableStream() { - if (!crypto_stream_.handshake_complete()) { - DLOG(INFO) << "Crypto handshake not complete, no outgoing stream created."; + if (!crypto_stream_.encryption_established()) { + DLOG(INFO) << "Encryption not active so no outgoing stream created."; return NULL; } if (GetNumOpenStreams() >= get_max_open_streams()) { diff --git a/net/tools/quic/quic_epoll_connection_helper_test.cc b/net/tools/quic/quic_epoll_connection_helper_test.cc index daaa59e0c3ec71..91fafa0d27a753 100644 --- a/net/tools/quic/quic_epoll_connection_helper_test.cc +++ b/net/tools/quic/quic_epoll_connection_helper_test.cc @@ -39,11 +39,7 @@ class TestConnectionHelper : public QuicEpollConnectionHelper { virtual int WritePacketToWire(const QuicEncryptedPacket& packet, int* error) OVERRIDE { - QuicFramer framer(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - true); + QuicFramer framer(kQuicVersion1, QuicTime::Zero(), true); FramerVisitorCapturingFrames visitor; framer.set_visitor(&visitor); EXPECT_TRUE(framer.ProcessPacket(packet)); @@ -81,11 +77,7 @@ class QuicConnectionHelperTest : public ::testing::Test { protected: QuicConnectionHelperTest() : guid_(42), - framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), - false), + framer_(kQuicVersion1, QuicTime::Zero(), false), send_algorithm_(new testing::StrictMock), helper_(new TestConnectionHelper(0, &epoll_server_)), connection_(guid_, IPEndPoint(), helper_), @@ -186,7 +178,7 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) { EXPECT_CALL( *send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); - connection_.SendOrQueuePacket(1, packet, 0, + connection_.SendOrQueuePacket(ENCRYPTION_NONE, 1, packet, 0, HAS_RETRANSMITTABLE_DATA); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); EXPECT_EQ(1u, connection_.NumQueuedPackets()); diff --git a/net/tools/quic/quic_time_wait_list_manager.cc b/net/tools/quic/quic_time_wait_list_manager.cc index 81e199ba2ab9f7..dd792eacffa168 100644 --- a/net/tools/quic/quic_time_wait_list_manager.cc +++ b/net/tools/quic/quic_time_wait_list_manager.cc @@ -92,9 +92,7 @@ QuicTimeWaitListManager::QuicTimeWaitListManager( QuicPacketWriter* writer, EpollServer* epoll_server) : framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL), - QuicTime::Zero(), + QuicTime::Zero(), // unused true), epoll_server_(epoll_server), kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),