diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-03 13:11:48 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-03 13:11:48 +0000 |
commit | 8ba812191740bbfde777360953618d80dd3467a5 (patch) | |
tree | 8463dee728e41899878d4e0ae7af5547aff429c9 /net | |
parent | a5f906063cff4e0362d364345ba0c727df86dc09 (diff) | |
download | chromium_src-8ba812191740bbfde777360953618d80dd3467a5.zip chromium_src-8ba812191740bbfde777360953618d80dd3467a5.tar.gz chromium_src-8ba812191740bbfde777360953618d80dd3467a5.tar.bz2 |
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
Diffstat (limited to 'net')
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 97eb896..4bed7ed 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 b0ff9ea..cbc1079 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<size_t>(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<pair<CryptoTag, size_t> >::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 07ffa39..7861284 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 <map> +#include <utility> +#include <vector> #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<CryptoTag, size_t> tag_length_map_; + // tags_and_lengths_ contains the tags that are currently being parsed and + // their lengths. + std::vector<std::pair<CryptoTag, size_t> > 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 1335e8b..0cdbd40 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 3e22e7b..baceeae 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 b1bcd549..c3b8d3a 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 2bf6e78..fbbbecf 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 6d26541..8b7bddb 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<QuicPacket> p( framer.ConstructFrameDataPacket(header, frames).packet); - scoped_ptr<QuicEncryptedPacket> packet(framer.EncryptPacket(1, *p)); + scoped_ptr<QuicEncryptedPacket> 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 766274b..4851d47 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<QuicEncryptedPacket> 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 2845dbec..5e916f8 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<QuicConnectionHelperInterface> 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 1bddba2..3576981 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<QuicPacket> 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<QuicPacket> 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 e4b4400..e81e011 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<char*>(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<char*>(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<MockSendAlgorithm>), 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<QuicPacket> packet(serialized_packet.packet); scoped_ptr<QuicEncryptedPacket> 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<QuicPacket> packet(ConstructDataPacket(number, fec_group, entropy_flag)); - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(number, - *packet)); + scoped_ptr<QuicEncryptedPacket> 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<QuicPacket> packet(ConstructClosePacket(number, fec_group)); - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(number, - *packet)); + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + ENCRYPTION_NONE, number, *packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } @@ -409,7 +557,7 @@ class QuicConnectionTest : public ::testing::Test { scoped_ptr<QuicPacket> fec_packet( framer_.ConstructFecPacket(header_, fec_data).packet); scoped_ptr<QuicEncryptedPacket> 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<QuicPacket> packet( framer_.ConstructFrameDataPacket(header, frames).packet); - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(12, *packet)); + scoped_ptr<QuicEncryptedPacket> 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 97b2b78..03b5c28 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 3381c8a..740c892 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 a840c3c..96547e42 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 5fa33e1..1dab72f 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 50fc2ca..2cc2746 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 9167e6f..df6c610 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 b43cb38..db83503 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 8f8028b..6e465ab 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 fafd2e3..70084a5 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<QuicData> out(encrypter_->EncryptPacket(packet_sequence_number, - packet.AssociatedData(), - packet.Plaintext())); + DCHECK(encrypter_[level].get() != NULL); + + scoped_ptr<QuicData> 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 ff5e4f8..1802fed 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<QuicDecrypter> decrypter_; - // Backup decrypter used to decrypt packets during parsing. May be NULL. - scoped_ptr<QuicDecrypter> backup_decrypter_; - // Encrypter used to encrypt packets via EncryptPacket(). - scoped_ptr<QuicEncrypter> encrypter_; + // Alternative decrypter that can also be used to decrypt packets. + scoped_ptr<QuicDecrypter> 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<QuicEncrypter> 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 ebb8cd0..6e2556c 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<QuicEncryptedPacket> 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<QuicEncryptedPacket> 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<QuicEncryptedPacket> 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<QuicEncryptedPacket> 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<QuicEncryptedPacket> 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<QuicEncryptedPacket> 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 e5bcdcc..3068d5f 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<bool> { 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<bool> { "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<bool> { frames.push_back(frame); scoped_ptr<QuicPacket> 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 240728b..3f7cf12 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<QuicPacket> packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr<QuicEncryptedPacket>(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<QuicEncryptedPacket> 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<QuicPacket> packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr<QuicEncryptedPacket>(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<QuicEncryptedPacket> data( + ConstructDataPacket(1, true, true, 0, GetRequestString("GET", "/"))); + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); + + MockWrite quic_writes[] = { + MockWrite(SYNCHRONOUS, data->data(), data->length()), + MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + }; + + scoped_ptr<QuicEncryptedPacket> 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<HttpNetworkTransaction> 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 60d48d0..4fe4f18 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<bool> { 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<bool> { void ProcessPacket(QuicPacket* packet) { scoped_ptr<QuicEncryptedPacket> 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 4e84d0b..edf67e9 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 faa5c78..27f980f 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 d4f23de..efb2459 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<std::string*> stream_data_; diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 8e9e411..35f764f 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 963a53a..9d2d2b7 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 bd18ec8..f993625 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<QuicStreamId> 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 c9c9177..0ebeb79 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<QuicPacket> packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr<QuicEncryptedPacket>(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<QuicEncryptedPacket> 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<QuicPacket> packet( framer.ConstructFrameDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>( - framer.EncryptPacket(header.packet_sequence_number, *packet)); + return scoped_ptr<QuicEncryptedPacket>(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 6de120d..8a9d01b 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 9504949..4fec83d 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 c63d0f0..8ac7c12 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 2d9fdc3..f244ca9 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 f774328..20926ce 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 445e766..01d0c75 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 6777810..07d1f20 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<QuicData> 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 cec4b31..7d34031 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 7bd1ff1..546ae99 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<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(0, packet)); + scoped_ptr<QuicEncryptedPacket> 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 ff8502f..5054eb9 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 fa3e014..2d19e6c 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 6fdf11e..e3394d3 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 daaa59e..91fafa0 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<MockSendAlgorithm>), 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 81e199b..dd792ea 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)), |