diff options
Diffstat (limited to 'net/quic')
42 files changed, 906 insertions, 429 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); } |