diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-31 02:49:11 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-31 02:49:11 +0000 |
commit | 74bda14a9f7d490da4c473e0f8307bfe780d67cd (patch) | |
tree | acccae63a7efccbc7247c2493c292c9febfe954d /net | |
parent | 5e9e96aae1a78860b984124519f720163fc52a33 (diff) | |
download | chromium_src-74bda14a9f7d490da4c473e0f8307bfe780d67cd.zip chromium_src-74bda14a9f7d490da4c473e0f8307bfe780d67cd.tar.gz chromium_src-74bda14a9f7d490da4c473e0f8307bfe780d67cd.tar.bz2 |
Add support for P-256 key exchange in crypto handshake.
Merge internal change: 44173744
Add default return to avoid crashing when we get an unknown
error code from the peer.
Merge internal change: 44160057
Fix incorrect DCHECK while serializing version negotiation
packet.
Merge internal change: 44156166
Reorder the addends in GetPacketHeaderSize to match the order
of the public header fields.
Merge internal change: 44153020
Changing retransmission and retransmittable data boolean flags
to enums.
Merge internal change: 44071662
Remove methods from QuicTime for converting to/from
microseconds and milliseconds since the epoch for QuicTime is
unspecified. (It wraps TimeTicks in Chromium).
Merge internal change: 44069965
Change InterArrival feedback message to traffic in delta since
the "start" of the connection instead of a delta since the epoch.
One step closer to being able to remove QuicTime::To/FromMicroseconds
since those methods don't "do the right thing".
Merge internal change: 44037996
Changing kForce into an enum.
Merge internal change: 44024887
Cleanups from landing P-256 key exchange in Chromium.
Merge internal change: 44023801
Fix for std::vector in QuicPacketPublicHeader's memory
corruption by memset.
Merge internal change: 44022862
Merging cleanup changes from chromium.
Merge internal change: 44009665
Plug in the new decrypter and encrypter after the new keys have
been derived.
This is a first cut, as some details on changing the encryption
keys still need to be worked out. Our interim solution is
permissive trial decryption, which allows the peer to encrypt
with the wrong key, either using the new key too early or using
the null key for too long. The latter will leak confidential
information, so we err on the side of using the new key too early.
WARNING: the interim solution protects against eavesdroppers, but
is vulberable to active attackers.
Merge internal change: 44006658
Start tracking server and client stream resets and export them
via varz.
Merge internal change: 43971847
Pull out RstStreamFrame error code from QuicErrorCode so that
they don't appear in the tracked ConnectionClose error map.
This will also help in tracking RstStream error codes separately.
Merge internal change: 43968620
Adding Client/Server logging to all LOGS/DLOGs Not bothering
with VLOGs/DVLOGS unless it's requested.
Merge internal change: 43948596
crypto: step 5.
This change implements source-address tokens at the server and has the client
echo them. Source address tokens are opaque (to the client) bytestrings that
prove ownership of an IP address. In order to prevent amplification attacks,
the server demands that the client have a valid source address token for the IP
address that it's claiming to come from and that the token is reasonably
recent.
Since we already have it implemented, this code uses AES-GCM to encrypt and
authenticate the tokens with a fixed, dummy secret (for now). In the future,
the secret will be derived from the primary, private key in the same way that
SessionTicket keys used to be.
The QuicEncrypter/Decrypter code was written to be quite specific to the task
of encrypting and decrypting packets and, as part of this, it exposed only 64
bits of the AEAD nonce.
Since all GFEs will share the same token secret, and they'll all create tokens
with random nonces, that runs an unacceptably high risk of an attacker
obtaining two tokens with the same nonce.
Thus this change also reworks the QuicEncrypter/Decrypter so that the full
nonce is exposed and thus we can use 96-bit nonces. That's still not completely
wonderful but, at 10Mpps an attacker would still take a year to obtain a pair
of nonces, so it's good enough for a while at least.
Merge internal change: 43893806
R=rch@chromium.org
Review URL: https://chromiumcodereview.appspot.com/13282004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@191569 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
79 files changed, 1662 insertions, 736 deletions
diff --git a/net/net.gyp b/net/net.gyp index 9b94caa..743507c 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -1602,6 +1602,7 @@ 'quic/quic_stream_factory_test.cc', 'quic/quic_stream_sequencer_test.cc', 'quic/quic_time_test.cc', + 'quic/quic_utils_test.cc', 'quic/reliable_quic_stream_test.cc', 'socket/buffered_write_stream_socket_unittest.cc', 'socket/client_socket_pool_base_unittest.cc', diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc index 19549a47..2ccbc8b 100644 --- a/net/quic/congestion_control/fix_rate_sender.cc +++ b/net/quic/congestion_control/fix_rate_sender.cc @@ -58,7 +58,7 @@ void FixRateSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) { void FixRateSender::SentPacket(QuicTime sent_time, QuicPacketSequenceNumber /*sequence_number*/, QuicByteCount bytes, - bool is_retransmission) { + Retransmission is_retransmission) { fix_rate_leaky_bucket_.Add(sent_time, bytes); paced_sender_.SentPacket(sent_time, bytes); if (!is_retransmission) { @@ -73,8 +73,8 @@ void FixRateSender::AbandoningPacket( QuicTime::Delta FixRateSender::TimeUntilSend( QuicTime now, - bool /*is_retransmission*/, - bool /*has_retransmittable_data*/) { + Retransmission /*is_retransmission*/, + HasRetransmittableData /*has_retransmittable_data*/) { if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending(now)) { if (CongestionWindow() <= data_in_flight_) { // We need an ack before we send more. diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h index e81981a..42915e1 100644 --- a/net/quic/congestion_control/fix_rate_sender.h +++ b/net/quic/congestion_control/fix_rate_sender.h @@ -36,12 +36,13 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { virtual void SentPacket(QuicTime sent_time, QuicPacketSequenceNumber equence_number, QuicByteCount bytes, - bool is_retransmission) OVERRIDE; + Retransmission is_retransmission) OVERRIDE; virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, QuicByteCount abandoned_bytes) OVERRIDE; - virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission, - bool has_retransmittable_data) OVERRIDE; + virtual QuicTime::Delta TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data) OVERRIDE; virtual QuicBandwidth BandwidthEstimate() OVERRIDE; virtual QuicTime::Delta SmoothedRtt() OVERRIDE; // End implementation of SendAlgorithmInterface. diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc index bbbb3cd..4969966 100644 --- a/net/quic/congestion_control/fix_rate_test.cc +++ b/net/quic/congestion_control/fix_rate_test.cc @@ -32,7 +32,8 @@ class FixRateTest : public ::testing::Test { : rtt_(QuicTime::Delta::FromMilliseconds(30)), unused_bandwidth_(QuicBandwidth::Zero()), sender_(new FixRateSender(&clock_)), - receiver_(new FixRateReceiverPeer()) { + receiver_(new FixRateReceiverPeer()), + start_(clock_.Now()) { // Make sure clock does not start at 0. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); } @@ -42,6 +43,7 @@ class FixRateTest : public ::testing::Test { SendAlgorithmInterface::SentPacketsMap unused_packet_map_; scoped_ptr<FixRateSender> sender_; scoped_ptr<FixRateReceiverPeer> receiver_; + const QuicTime start_; }; TEST_F(FixRateTest, ReceiverAPI) { @@ -61,21 +63,24 @@ TEST_F(FixRateTest, SenderAPI) { sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), unused_bandwidth_, unused_packet_map_); EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); - sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, false); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); - sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, false); - sender_->SentPacket(clock_.Now(), 3, 600, false); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), - sender_->TimeUntilSend(clock_.Now(), false, true)); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); + sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, NOT_RETRANSMISSION); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); + sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, NOT_RETRANSMISSION); + sender_->SentPacket(clock_.Now(), 3, 600, NOT_RETRANSMISSION); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); - EXPECT_EQ(QuicTime::Delta::Infinite(), - sender_->TimeUntilSend(clock_.Now(), false, true)); + EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); sender_->OnIncomingAck(1, kMaxPacketSize, rtt_); sender_->OnIncomingAck(2, kMaxPacketSize, rtt_); sender_->OnIncomingAck(3, 600, rtt_); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); } TEST_F(FixRateTest, FixRatePacing) { @@ -90,19 +95,24 @@ TEST_F(FixRateTest, FixRatePacing) { QuicTime acc_advance_time(QuicTime::Zero()); QuicPacketSequenceNumber sequence_number = 0; for (int i = 0; i < num_packets; i += 2) { - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); - sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); - sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false); - QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(), false, - true); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); + sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, + NOT_RETRANSMISSION); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); + sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, + NOT_RETRANSMISSION); + QuicTime::Delta advance_time = sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA); clock_.AdvanceTime(advance_time); sender_->OnIncomingAck(sequence_number - 1, packet_size, rtt_); sender_->OnIncomingAck(sequence_number - 2, packet_size, rtt_); acc_advance_time = acc_advance_time.Add(advance_time); } EXPECT_EQ(num_packets * packet_size * 1000000 / bitrate.ToBytesPerSecond(), - static_cast<uint64>(acc_advance_time.ToMicroseconds())); + static_cast<uint64>(acc_advance_time.Subtract(start_) + .ToMicroseconds())); } } // namespace test diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc index d4dcdb3..48091c3 100644 --- a/net/quic/congestion_control/quic_congestion_manager.cc +++ b/net/quic/congestion_control/quic_congestion_manager.cc @@ -46,10 +46,10 @@ QuicCongestionManager::~QuicCongestionManager() { void QuicCongestionManager::SentPacket(QuicPacketSequenceNumber sequence_number, QuicTime sent_time, QuicByteCount bytes, - bool is_retransmission) { + Retransmission retransmission) { DCHECK(!ContainsKey(pending_packets_, sequence_number)); send_algorithm_->SentPacket(sent_time, sequence_number, bytes, - is_retransmission); + retransmission); packet_history_map_[sequence_number] = new class SendAlgorithmInterface::SentPacket(bytes, sent_time); @@ -122,10 +122,9 @@ void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame, QuicTime::Delta QuicCongestionManager::TimeUntilSend( QuicTime now, - bool is_retransmission, - bool has_retransmittable_data) { - return send_algorithm_->TimeUntilSend(now, is_retransmission, - has_retransmittable_data); + Retransmission retransmission, + HasRetransmittableData retransmittable) { + return send_algorithm_->TimeUntilSend(now, retransmission, retransmittable); } bool QuicCongestionManager::GenerateCongestionFeedback( diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h index 4560957..5840444 100644 --- a/net/quic/congestion_control/quic_congestion_manager.h +++ b/net/quic/congestion_control/quic_congestion_manager.h @@ -46,7 +46,7 @@ class QuicCongestionManager { virtual void SentPacket(QuicPacketSequenceNumber sequence_number, QuicTime sent_time, QuicByteCount bytes, - bool is_retransmission); + Retransmission retransmission); // Called when a packet is timed out. virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number); @@ -57,8 +57,8 @@ class QuicCongestionManager { // Note 2: Send algorithms may or may not use |retransmit| in their // calculations. virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission, - bool has_retransmittable_data); + Retransmission retransmission, + HasRetransmittableData retransmittable); // Should be called before sending an ACK packet, to decide if we need // to attach a QuicCongestionFeedbackFrame block. diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h index effa671..fd8ec3e 100644 --- a/net/quic/congestion_control/send_algorithm_interface.h +++ b/net/quic/congestion_control/send_algorithm_interface.h @@ -7,6 +7,8 @@ #ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_ #define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_ +#include <map> + #include "base/basictypes.h" #include "net/base/net_export.h" #include "net/quic/quic_bandwidth.h" @@ -58,16 +60,17 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { virtual void SentPacket(QuicTime sent_time, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - bool is_retransmission) = 0; + Retransmission is_retransmission) = 0; // Called when a packet is timed out. virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, QuicByteCount abandoned_bytes) = 0; // Calculate the time until we can send the next packet. - virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission, - bool has_retransmittable_data) = 0; + virtual QuicTime::Delta TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data) = 0; // What's the current estimated bandwidth in bytes per second. // Returns 0 when it does not have an estimate. diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 70367ff..4bed7ed 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -85,7 +85,7 @@ void TcpCubicSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) { void TcpCubicSender::SentPacket(QuicTime /*sent_time*/, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - bool is_retransmission) { + Retransmission is_retransmission) { bytes_in_flight_ += bytes; if (!is_retransmission && update_end_sequence_number_) { end_sequence_number_ = sequence_number; @@ -101,9 +101,10 @@ void TcpCubicSender::AbandoningPacket(QuicPacketSequenceNumber sequence_number, bytes_in_flight_ -= abandoned_bytes; } -QuicTime::Delta TcpCubicSender::TimeUntilSend(QuicTime now, - bool is_retransmission, - bool has_retransmittable_data) { +QuicTime::Delta TcpCubicSender::TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data) { if (is_retransmission || !has_retransmittable_data) { // For TCP we can always send a retransmission and/or an ACK immediately. return QuicTime::Delta::Zero(); diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h index 6943044..2a1066d 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.h +++ b/net/quic/congestion_control/tcp_cubic_sender.h @@ -42,12 +42,13 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { virtual void SentPacket(QuicTime sent_time, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - bool is_retransmission) OVERRIDE; + Retransmission is_retransmission) OVERRIDE; virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, QuicByteCount abandoned_bytes) OVERRIDE; - virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission, - bool has_retransmittable_data) OVERRIDE; + virtual QuicTime::Delta TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data) OVERRIDE; virtual QuicBandwidth BandwidthEstimate() OVERRIDE; virtual QuicTime::Delta SmoothedRtt() OVERRIDE; // End implementation of SendAlgorithmInterface. diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index 2aab55a..0cd51fd 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -40,10 +40,11 @@ class TcpCubicSenderTest : public ::testing::Test { while (bytes_to_send > 0) { QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send); sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet, - false); + NOT_RETRANSMISSION); bytes_to_send -= bytes_in_packet; if (bytes_to_send > 0) { - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, + NO_RETRANSMITTABLE_DATA).IsZero()); } } } @@ -73,31 +74,36 @@ TEST_F(TcpCubicSenderTest, SimpleSender) { EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableCongestionWindow()); // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); // And that window is un-affected. EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableCongestionWindow()); // A retransmitt should always retun 0. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), true, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), IS_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); } TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { const int kNumberOfAck = 20; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); for (int n = 0; n < kNumberOfAck; ++n) { // Send our full congestion window. @@ -118,13 +124,15 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { const int kNumberOfAck = 65; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); for (int n = 0; n < kNumberOfAck; ++n) { // Send our full congestion window. @@ -159,13 +167,15 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { const int kNumberOfAck = 10; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsZero()); for (int i = 0; i < kNumberOfAck; ++i) { // Send our full congestion window. @@ -180,7 +190,8 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { sender_->OnIncomingLoss(clock_.Now()); // Make sure that we should not send right now. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsInfinite()); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA).IsInfinite()); // We should now have fallen out of slow start. // We expect window to be cut in half. diff --git a/net/quic/crypto/aes_128_gcm_decrypter.h b/net/quic/crypto/aes_128_gcm_decrypter.h index 88ce90e..bcd1d25 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter.h +++ b/net/quic/crypto/aes_128_gcm_decrypter.h @@ -32,22 +32,18 @@ class NET_EXPORT_PRIVATE Aes128GcmDecrypter : public QuicDecrypter { // QuicDecrypter implementation virtual bool SetKey(base::StringPiece key) OVERRIDE; virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; - virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece ciphertext) OVERRIDE; + virtual bool Decrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece ciphertext, + unsigned char* output, + size_t* output_length) OVERRIDE; + virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece ciphertext) OVERRIDE; virtual base::StringPiece GetKey() const OVERRIDE; virtual base::StringPiece GetNoncePrefix() const OVERRIDE; private: - friend class test::Aes128GcmDecrypterPeer; - - // The same as Decrypt(), except that the supplied |nonce| argument rather - // than the |nonce_| member is used as the nonce. This method is useful - // for testing the underlying AES GCM implementation. - QuicData* DecryptWithNonce(base::StringPiece nonce, - base::StringPiece associated_data, - base::StringPiece ciphertext); - // The 128-bit AES key. unsigned char key_[16]; // The nonce, a concatenation of a four-byte fixed prefix and a 8-byte diff --git a/net/quic/crypto/aes_128_gcm_decrypter_nss.cc b/net/quic/crypto/aes_128_gcm_decrypter_nss.cc index 1d5c485..6c9bf1d 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter_nss.cc +++ b/net/quic/crypto/aes_128_gcm_decrypter_nss.cc @@ -52,41 +52,23 @@ bool Aes128GcmDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -QuicData* Aes128GcmDecrypter::Decrypt(QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece ciphertext) { - COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), - incorrect_nonce_size); - memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - return DecryptWithNonce(StringPiece(reinterpret_cast<char*>(nonce_), - sizeof(nonce_)), - associated_data, ciphertext); -} - -StringPiece Aes128GcmDecrypter::GetKey() const { - return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); -} - -StringPiece Aes128GcmDecrypter::GetNoncePrefix() const { - return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); -} - -QuicData* Aes128GcmDecrypter::DecryptWithNonce(StringPiece nonce, - StringPiece associated_data, - StringPiece ciphertext) { +bool Aes128GcmDecrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + unsigned char* output, + size_t* output_length) { #if defined(USE_NSS) // See the comment in IsSupported(). - return NULL; + return false; #else - if (ciphertext.length() < kAuthTagSize) { - return NULL; + if (ciphertext.length() < kAuthTagSize || + nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { + return false; } // NSS 3.14.x incorrectly requires an output buffer at least as long as - // the ciphertext (NSS bug 853674). So the capacity of the |plaintext| - // buffer needs to be larger than the plaintext size. - size_t plaintext_capacity = ciphertext.length(); + // the ciphertext (NSS bug 853674). Fortunately QuicDecrypter::Decrypt() + // specifies that |output| must be as long as |ciphertext| on entry. size_t plaintext_size = ciphertext.length() - kAuthTagSize; - scoped_ptr<char[]> plaintext(new char[plaintext_capacity]); // Import key_ into NSS. SECItem key_item; @@ -103,7 +85,7 @@ QuicData* Aes128GcmDecrypter::DecryptWithNonce(StringPiece nonce, slot = NULL; if (!aes_key) { DLOG(INFO) << "PK11_ImportSymKey failed"; - return NULL; + return false; } CK_GCM_PARAMS gcm_params; @@ -124,21 +106,50 @@ QuicData* Aes128GcmDecrypter::DecryptWithNonce(StringPiece nonce, // If an incorrect authentication tag causes a decryption failure, the NSS // error is SEC_ERROR_BAD_DATA (-8190). if (PK11_Decrypt(aes_key.get(), CKM_AES_GCM, ¶m, - reinterpret_cast<unsigned char*>(plaintext.get()), - &output_len, plaintext_capacity, - reinterpret_cast<const unsigned char*>(ciphertext.data()), - ciphertext.size()) != SECSuccess) { + output, &output_len, ciphertext.length(), + reinterpret_cast<const unsigned char*>( + ciphertext.data()), ciphertext.length()) != SECSuccess) { DLOG(INFO) << "PK11_Decrypt failed: NSS error " << PORT_GetError(); - return NULL; + return false; } if (output_len != plaintext_size) { DLOG(INFO) << "Wrong output length"; + return false; + } + *output_length = output_len; + return true; +#endif +} + +QuicData* Aes128GcmDecrypter::DecryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) { + COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), + incorrect_nonce_size); + if (ciphertext.length() < kAuthTagSize) { return NULL; } + size_t plaintext_size; + scoped_ptr<char[]> plaintext(new char[ciphertext.length()]); + memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce_), sizeof(nonce_)), + associated_data, ciphertext, + reinterpret_cast<unsigned char*>(plaintext.get()), + &plaintext_size)) { + return NULL; + } return new QuicData(plaintext.release(), plaintext_size, true); -#endif +} + +StringPiece Aes128GcmDecrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +} + +StringPiece Aes128GcmDecrypter::GetNoncePrefix() const { + return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); } } // namespace net diff --git a/net/quic/crypto/aes_128_gcm_decrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_decrypter_openssl.cc index 3b73779..8cf52e6 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter_openssl.cc +++ b/net/quic/crypto/aes_128_gcm_decrypter_openssl.cc @@ -44,91 +44,105 @@ bool Aes128GcmDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -QuicData* Aes128GcmDecrypter::Decrypt(QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece ciphertext) { - COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), - incorrect_nonce_size); - memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - return DecryptWithNonce(StringPiece(reinterpret_cast<char*>(nonce_), - sizeof(nonce_)), - associated_data, ciphertext); -} - -StringPiece Aes128GcmDecrypter::GetKey() const { - return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); -} - -StringPiece Aes128GcmDecrypter::GetNoncePrefix() const { - return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); -} - -QuicData* Aes128GcmDecrypter::DecryptWithNonce(StringPiece nonce, - StringPiece associated_data, - StringPiece ciphertext) { - if (ciphertext.length() < kAuthTagSize) { - return NULL; +bool Aes128GcmDecrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + unsigned char* output, + size_t* output_length) { + if (ciphertext.length() < kAuthTagSize || + nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { + return false; } - size_t plaintext_size = ciphertext.length() - kAuthTagSize; - scoped_ptr<char[]> plaintext(new char[plaintext_size]); - - // |output| points to the position in the |plaintext| buffer to receive - // the next output. - unsigned char* output = reinterpret_cast<unsigned char*>(plaintext.get()); - // |output_len| is passed to an OpenSSL function to receive the output - // length. - int output_len; + const size_t plaintext_size = ciphertext.length() - kAuthTagSize; + // |len| is passed to an OpenSSL function to receive the output length. + int len; ScopedEVPCipherCtx ctx; // Set the cipher type and the key. The IV (nonce) is set below. if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), NULL, key_, NULL) == 0) { - return NULL; + return false; } // Set the IV (nonce) length. if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, nonce.size(), NULL) == 0) { - return NULL; + return false; } // Set the IV (nonce). if (EVP_DecryptInit_ex(ctx.get(), NULL, NULL, NULL, reinterpret_cast<const unsigned char*>( nonce.data())) == 0) { - return NULL; + return false; } // Set the authentication tag. if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kAuthTagSize, const_cast<char*>(ciphertext.data()) + plaintext_size) == 0) { - return NULL; + return false; } - // Set the associated data. The second argument (output buffer) must be - // NULL. - if (EVP_DecryptUpdate(ctx.get(), NULL, &output_len, - reinterpret_cast<const unsigned char*>( - associated_data.data()), - associated_data.size()) == 0) { - return NULL; + // If we pass a NULL, zero-length associated data to OpenSSL then it breaks. + // Thus we only set non-empty associated data. + if (!associated_data.empty()) { + // Set the associated data. The second argument (output buffer) must be + // NULL. + if (EVP_DecryptUpdate(ctx.get(), NULL, &len, + reinterpret_cast<const unsigned char*>( + associated_data.data()), + associated_data.size()) == 0) { + return false; + } } - if (EVP_DecryptUpdate(ctx.get(), output, &output_len, + if (EVP_DecryptUpdate(ctx.get(), output, &len, reinterpret_cast<const unsigned char*>( ciphertext.data()), plaintext_size) == 0) { - return NULL; + return false; + } + output += len; + + if (EVP_DecryptFinal_ex(ctx.get(), output, &len) == 0) { + return false; } - output += output_len; + output += len; - if (EVP_DecryptFinal_ex(ctx.get(), output, &output_len) == 0) { + *output_length = plaintext_size; + + return true; +} + +QuicData* Aes128GcmDecrypter::DecryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) { + COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), + incorrect_nonce_size); + if (ciphertext.length() < kAuthTagSize) { return NULL; } - output += output_len; + size_t plaintext_size; + scoped_ptr<char[]> plaintext(new char[ciphertext.length()]); + memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce_), sizeof(nonce_)), + associated_data, ciphertext, + reinterpret_cast<unsigned char*>(plaintext.get()), + &plaintext_size)) { + return NULL; + } return new QuicData(plaintext.release(), plaintext_size, true); } +StringPiece Aes128GcmDecrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +} + +StringPiece Aes128GcmDecrypter::GetNoncePrefix() const { + return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); +} + } // namespace net diff --git a/net/quic/crypto/aes_128_gcm_decrypter_test.cc b/net/quic/crypto/aes_128_gcm_decrypter_test.cc index ac09581..2ab0dd0 100644 --- a/net/quic/crypto/aes_128_gcm_decrypter_test.cc +++ b/net/quic/crypto/aes_128_gcm_decrypter_test.cc @@ -291,15 +291,22 @@ bool DecodeHexString(const char* in, namespace net { namespace test { -class Aes128GcmDecrypterPeer { - public: - static QuicData* Decrypt(Aes128GcmDecrypter* decrypter, +// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the plaintext. +QuicData* DecryptWithNonce(Aes128GcmDecrypter* decrypter, StringPiece nonce, StringPiece associated_data, StringPiece ciphertext) { - return decrypter->DecryptWithNonce(nonce, associated_data, ciphertext); + size_t plaintext_size = ciphertext.length(); + scoped_ptr<char[]> plaintext(new char[plaintext_size]); + + if (!decrypter->Decrypt(nonce, associated_data, ciphertext, + reinterpret_cast<unsigned char*>(plaintext.get()), + &plaintext_size)) { + return NULL; } -}; + return new QuicData(plaintext.release(), plaintext_size, true); +} TEST(Aes128GcmDecrypterTest, Decrypt) { if (!Aes128GcmDecrypter::IsSupported()) { @@ -321,6 +328,7 @@ TEST(Aes128GcmDecrypterTest, Decrypt) { size_t pt_len; for (size_t i = 0; i < arraysize(test_group_array); i++) { + SCOPED_TRACE(i); const TestVector* test_vector = test_group_array[i]; const TestGroupInfo& test_info = test_group_info[i]; for (size_t j = 0; test_vector[j].key != NULL; j++) { @@ -353,8 +361,11 @@ TEST(Aes128GcmDecrypterTest, Decrypt) { ASSERT_TRUE(decrypter.SetKey(StringPiece(key, key_len))); string ciphertext(ct, ct_len); ciphertext.append(tag, tag_len); - scoped_ptr<QuicData> decrypted(Aes128GcmDecrypterPeer::Decrypt( - &decrypter, StringPiece(iv, iv_len), StringPiece(aad, aad_len), + scoped_ptr<QuicData> decrypted(DecryptWithNonce( + &decrypter, StringPiece(iv, iv_len), + // OpenSSL fails if NULL is set as the AAD, as opposed to a + // zero-length, non-NULL pointer. + StringPiece(aad_len ? aad : NULL, aad_len), ciphertext)); if (!decrypted.get()) { EXPECT_EQ((size_t)-1, pt_len); diff --git a/net/quic/crypto/aes_128_gcm_encrypter.h b/net/quic/crypto/aes_128_gcm_encrypter.h index 1b1df9e..790c1d17 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter.h +++ b/net/quic/crypto/aes_128_gcm_encrypter.h @@ -32,9 +32,13 @@ class NET_EXPORT_PRIVATE Aes128GcmEncrypter : public QuicEncrypter { // QuicEncrypter implementation virtual bool SetKey(base::StringPiece key) OVERRIDE; virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; - virtual QuicData* Encrypt(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece plaintext) OVERRIDE; + virtual bool Encrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece plaintext, + unsigned char* output) OVERRIDE; + virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece plaintext) OVERRIDE; virtual size_t GetKeySize() const OVERRIDE; virtual size_t GetNoncePrefixSize() const OVERRIDE; virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE; @@ -43,15 +47,6 @@ class NET_EXPORT_PRIVATE Aes128GcmEncrypter : public QuicEncrypter { virtual base::StringPiece GetNoncePrefix() const OVERRIDE; private: - friend class test::Aes128GcmEncrypterPeer; - - // The same as Encrypt(), except that the supplied |nonce| argument rather - // than the |nonce_| member is used as the nonce. This method is useful - // for testing the underlying AES GCM implementation. - QuicData* EncryptWithNonce(base::StringPiece nonce, - base::StringPiece associated_data, - base::StringPiece plaintext); - // The 128-bit AES key. unsigned char key_[16]; // The nonce, a concatenation of a four-byte fixed prefix and a 8-byte diff --git a/net/quic/crypto/aes_128_gcm_encrypter_nss.cc b/net/quic/crypto/aes_128_gcm_encrypter_nss.cc index 57d364c..789c16d 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter_nss.cc +++ b/net/quic/crypto/aes_128_gcm_encrypter_nss.cc @@ -53,52 +53,19 @@ bool Aes128GcmEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -QuicData* Aes128GcmEncrypter::Encrypt(QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece plaintext) { - COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), - incorrect_nonce_size); - memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - return EncryptWithNonce(StringPiece(reinterpret_cast<char*>(nonce_), - sizeof(nonce_)), - associated_data, plaintext); -} - -size_t Aes128GcmEncrypter::GetKeySize() const { - return kKeySize; -} - -size_t Aes128GcmEncrypter::GetNoncePrefixSize() const { - return kNoncePrefixSize; -} - -size_t Aes128GcmEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { - return ciphertext_size - kAuthTagSize; -} - -// An AEAD_AES_128_GCM ciphertext is exactly 16 bytes longer than its -// corresponding plaintext. -size_t Aes128GcmEncrypter::GetCiphertextSize(size_t plaintext_size) const { - return plaintext_size + kAuthTagSize; -} - -StringPiece Aes128GcmEncrypter::GetKey() const { - return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); -} - -StringPiece Aes128GcmEncrypter::GetNoncePrefix() const { - return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); -} - -QuicData* Aes128GcmEncrypter::EncryptWithNonce(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext) { +bool Aes128GcmEncrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { #if defined(USE_NSS) // See the comment in IsSupported(). - return NULL; + return false; #else + if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { + return false; + } + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); - scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); // Import key_ into NSS. SECItem key_item; @@ -115,7 +82,7 @@ QuicData* Aes128GcmEncrypter::EncryptWithNonce(StringPiece nonce, slot = NULL; if (!aes_key) { DLOG(INFO) << "PK11_ImportSymKey failed"; - return NULL; + return false; } CK_GCM_PARAMS gcm_params; @@ -134,21 +101,66 @@ QuicData* Aes128GcmEncrypter::EncryptWithNonce(StringPiece nonce, unsigned int output_len; if (PK11_Encrypt(aes_key.get(), CKM_AES_GCM, ¶m, - reinterpret_cast<unsigned char*>(ciphertext.get()), - &output_len, ciphertext_size, + output, &output_len, ciphertext_size, reinterpret_cast<const unsigned char*>(plaintext.data()), plaintext.size()) != SECSuccess) { DLOG(INFO) << "PK11_Encrypt failed"; - return NULL; + return false; } if (output_len != ciphertext_size) { DLOG(INFO) << "Wrong output length"; + return false; + } + + return true; +#endif +} + +QuicData* Aes128GcmEncrypter::EncryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) { + COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), + incorrect_nonce_size); + memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce_), sizeof(nonce_)), + associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { return NULL; } return new QuicData(ciphertext.release(), ciphertext_size, true); -#endif +} + +size_t Aes128GcmEncrypter::GetKeySize() const { + return kKeySize; +} + +size_t Aes128GcmEncrypter::GetNoncePrefixSize() const { + return kNoncePrefixSize; +} + +size_t Aes128GcmEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - kAuthTagSize; +} + +// An AEAD_AES_128_GCM ciphertext is exactly 16 bytes longer than its +// corresponding plaintext. +size_t Aes128GcmEncrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + kAuthTagSize; +} + +StringPiece Aes128GcmEncrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +} + +StringPiece Aes128GcmEncrypter::GetNoncePrefix() const { + return StringPiece(reinterpret_cast<const char*>(nonce_), kNoncePrefixSize); } } // namespace net diff --git a/net/quic/crypto/aes_128_gcm_encrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_encrypter_openssl.cc index 41d9177..87b8f53 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter_openssl.cc +++ b/net/quic/crypto/aes_128_gcm_encrypter_openssl.cc @@ -45,98 +45,110 @@ bool Aes128GcmEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -QuicData* Aes128GcmEncrypter::Encrypt(QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece plaintext) { - COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), - incorrect_nonce_size); - memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - return EncryptWithNonce(StringPiece(reinterpret_cast<char*>(nonce_), - sizeof(nonce_)), - associated_data, plaintext); -} - -size_t Aes128GcmEncrypter::GetKeySize() const { - return kKeySize; -} - -size_t Aes128GcmEncrypter::GetNoncePrefixSize() const { - return kNoncePrefixSize; -} - -size_t Aes128GcmEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { - return ciphertext_size - kAuthTagSize; -} - -// An AEAD_AES_128_GCM ciphertext is exactly 16 bytes longer than its -// corresponding plaintext. -size_t Aes128GcmEncrypter::GetCiphertextSize(size_t plaintext_size) const { - return plaintext_size + kAuthTagSize; -} - -QuicData* Aes128GcmEncrypter::EncryptWithNonce(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext) { - size_t ciphertext_size = GetCiphertextSize(plaintext.length()); - scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); - - // |output| points to the position in the |ciphertext| buffer to receive - // the next output. - unsigned char* output = reinterpret_cast<unsigned char*>(ciphertext.get()); +bool Aes128GcmEncrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { // |output_len| is passed to an OpenSSL function to receive the output // length. int output_len; + if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { + return false; + } + ScopedEVPCipherCtx ctx; // Set the cipher type and the key. The IV (nonce) is set below. if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_gcm(), NULL, key_, NULL) == 0) { - return NULL; + return false; } // Set the IV (nonce) length. if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, nonce.size(), NULL) == 0) { - return NULL; + return false; } // Set the IV (nonce). if (EVP_EncryptInit_ex(ctx.get(), NULL, NULL, NULL, reinterpret_cast<const unsigned char*>( nonce.data())) == 0) { - return NULL; + return false; } - // Set the associated data. The second argument (output buffer) must be - // NULL. - if (EVP_EncryptUpdate(ctx.get(), NULL, &output_len, - reinterpret_cast<const unsigned char*>( - associated_data.data()), - associated_data.size()) == 0) { - return NULL; + // If we pass a NULL, zero-length associated data to OpenSSL then it breaks. + // Thus we only set non-empty associated data. + if (!associated_data.empty()) { + // Set the associated data. The second argument (output buffer) must be + // NULL. + if (EVP_EncryptUpdate(ctx.get(), NULL, &output_len, + reinterpret_cast<const unsigned char*>( + associated_data.data()), + associated_data.size()) == 0) { + return false; + } } if (EVP_EncryptUpdate(ctx.get(), output, &output_len, reinterpret_cast<const unsigned char*>( plaintext.data()), plaintext.size()) == 0) { - return NULL; + return false; } output += output_len; if (EVP_EncryptFinal_ex(ctx.get(), output, &output_len) == 0) { - return NULL; + return false; } output += output_len; if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize, output) == 0) { + return false; + } + + return true; +} + +QuicData* Aes128GcmEncrypter::EncryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) { + COMPILE_ASSERT(sizeof(nonce_) == kNoncePrefixSize + sizeof(sequence_number), + incorrect_nonce_size); + memcpy(nonce_ + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce_), sizeof(nonce_)), + associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { return NULL; } return new QuicData(ciphertext.release(), ciphertext_size, true); } +size_t Aes128GcmEncrypter::GetKeySize() const { + return kKeySize; +} + +size_t Aes128GcmEncrypter::GetNoncePrefixSize() const { + return kNoncePrefixSize; +} + +size_t Aes128GcmEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - kAuthTagSize; +} + +// An AEAD_AES_128_GCM ciphertext is exactly 16 bytes longer than its +// corresponding plaintext. +size_t Aes128GcmEncrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + kAuthTagSize; +} + StringPiece Aes128GcmEncrypter::GetKey() const { return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); } diff --git a/net/quic/crypto/aes_128_gcm_encrypter_test.cc b/net/quic/crypto/aes_128_gcm_encrypter_test.cc index 4dd38b4..0da7a8f 100644 --- a/net/quic/crypto/aes_128_gcm_encrypter_test.cc +++ b/net/quic/crypto/aes_128_gcm_encrypter_test.cc @@ -243,15 +243,22 @@ bool DecodeHexString(const char* in, namespace net { namespace test { -class Aes128GcmEncrypterPeer { - public: - static QuicData* Encrypt(Aes128GcmEncrypter* encrypter, +// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the ciphertext. +QuicData* EncryptWithNonce(Aes128GcmEncrypter* encrypter, StringPiece nonce, StringPiece associated_data, StringPiece plaintext) { - return encrypter->EncryptWithNonce(nonce, associated_data, plaintext); + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!encrypter->Encrypt(nonce, associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return NULL; } -}; + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} TEST(Aes128GcmEncrypterTest, Encrypt) { if (!Aes128GcmEncrypter::IsSupported()) { @@ -273,6 +280,7 @@ TEST(Aes128GcmEncrypterTest, Encrypt) { size_t tag_len; for (size_t i = 0; i < arraysize(test_group_array); i++) { + SCOPED_TRACE(i); const TestVector* test_vector = test_group_array[i]; const TestGroupInfo& test_info = test_group_info[i]; for (size_t j = 0; test_vector[j].key != NULL; j++) { @@ -301,8 +309,12 @@ TEST(Aes128GcmEncrypterTest, Encrypt) { Aes128GcmEncrypter encrypter; ASSERT_TRUE(encrypter.SetKey(StringPiece(key, key_len))); - scoped_ptr<QuicData> encrypted(Aes128GcmEncrypterPeer::Encrypt( - &encrypter, StringPiece(iv, iv_len), StringPiece(aad, aad_len), + scoped_ptr<QuicData> encrypted(EncryptWithNonce( + &encrypter, StringPiece(iv, iv_len), + // OpenSSL fails if NULL is set as the AAD, as opposed to a + // zero-length, non-NULL pointer. This deliberately tests that we + // handle this case. + StringPiece(aad_len ? aad : NULL, aad_len), StringPiece(pt, pt_len))); ASSERT_TRUE(encrypted.get()); ASSERT_EQ(ct_len + tag_len, encrypted->length()); diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index f25133d..5956e45 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -9,9 +9,12 @@ #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" #include "crypto/hkdf.h" #include "crypto/secure_hash.h" #include "net/base/net_util.h" +#include "net/quic/crypto/aes_128_gcm_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_encrypter.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/curve25519_key_exchange.h" @@ -319,6 +322,34 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { return ret; } +SourceAddressToken::SourceAddressToken() { +} + +SourceAddressToken::~SourceAddressToken() { +} + +string SourceAddressToken::SerializeAsString() const { + return ip_ + " " + base::Int64ToString(timestamp_); +} + +bool SourceAddressToken::ParseFromArray(unsigned char* plaintext, + size_t plaintext_length) { + string data(reinterpret_cast<const char*>(plaintext), plaintext_length); + std::vector<std::string> results; + base::SplitString(data, ' ', &results); + if (results.size() < 2) { + return false; + } + + int64 timestamp; + if (!base::StringToInt64(results[1], ×tamp)) { + return false; + } + + ip_ = results[0]; + timestamp_ = timestamp; + return true; +} QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() : version(0), @@ -392,13 +423,19 @@ const string& QuicCryptoClientConfig::CachedState::orbit() const { return orbit_; } +void QuicCryptoClientConfig::CachedState::set_source_address_token( + StringPiece token) { + source_address_token_ = token.as_string(); +} + void QuicCryptoClientConfig::SetDefaults() { // Version must be 0. version = kVersion; // Key exchange methods. - kexs.resize(1); + kexs.resize(2); kexs[0] = kC255; + kexs[1] = kP256; // Authenticated encryption algorithms. aead.resize(1); @@ -431,7 +468,7 @@ void QuicCryptoClientConfig::FillInchoateClientHello( out->SetValue(kVERS, version); if (cached && !cached->source_address_token().empty()) { - out->SetValue(kSRCT, cached->source_address_token()); + out->SetStringPiece(kSRCT, cached->source_address_token()); } } @@ -527,7 +564,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( break; case kP256: key_exchange.reset(P256KeyExchange::New( - Curve25519KeyExchange::NewPrivateKey(rand))); + P256KeyExchange::NewPrivateKey())); break; default: DCHECK(false); @@ -589,6 +626,11 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + StringPiece token; + if (rej.GetStringPiece(kSRCT, &token)) { + cached->set_source_address_token(token); + } + return QUIC_NO_ERROR; } @@ -610,7 +652,26 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( } -QuicCryptoServerConfig::QuicCryptoServerConfig() { +QuicCryptoServerConfig::QuicCryptoServerConfig( + StringPiece source_address_token_secret) + // AES-GCM is used to encrypt and authenticate source address tokens. The + // full, 96-bit nonce is used but we must ensure that an attacker cannot + // obtain two source address tokens with the same nonce. This occurs with + // probability 0.5 after 2**48 values. We assume that obtaining 2**48 + // source address tokens is not possible: at a rate of 10M packets per + // second, it would still take the attacker a year to obtain the needed + // number of packets. + // + // TODO(agl): switch to an encrypter with a larger nonce space (i.e. + // Salsa20+Poly1305). + : source_address_token_encrypter_(new Aes128GcmEncrypter), + source_address_token_decrypter_(new Aes128GcmDecrypter) { + crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, + "QUIC source address token key", + source_address_token_encrypter_->GetKeySize(), + 0 /* no fixed IV needed */); + source_address_token_encrypter_->SetKey(hkdf.server_write_key()); + source_address_token_decrypter_->SetKey(hkdf.server_write_key()); } QuicCryptoServerConfig::~QuicCryptoServerConfig() { @@ -624,21 +685,34 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::ConfigForTesting( const CryptoHandshakeMessage& extra_tags) { CryptoHandshakeMessage msg; - const string private_key = Curve25519KeyExchange::NewPrivateKey(rand); + const string curve25519_private_key = + Curve25519KeyExchange::NewPrivateKey(rand); scoped_ptr<Curve25519KeyExchange> curve25519( - Curve25519KeyExchange::New(private_key)); - StringPiece public_value = curve25519->public_value(); - string encoded_public_value; + Curve25519KeyExchange::New(curve25519_private_key)); + StringPiece curve25519_public_value = curve25519->public_value(); + + const string p256_private_key = + P256KeyExchange::NewPrivateKey(); + scoped_ptr<P256KeyExchange> p256( + P256KeyExchange::New(p256_private_key)); + StringPiece p256_public_value = p256->public_value(); + + string encoded_public_values; // First two bytes encode the length of the public value. - encoded_public_value.push_back(public_value.size()); - encoded_public_value.push_back(public_value.size() >> 8); - encoded_public_value.append(public_value.data(), public_value.size()); + encoded_public_values.push_back(curve25519_public_value.size()); + encoded_public_values.push_back(curve25519_public_value.size() >> 8); + encoded_public_values.append(curve25519_public_value.data(), + curve25519_public_value.size()); + encoded_public_values.push_back(p256_public_value.size()); + encoded_public_values.push_back(p256_public_value.size() >> 8); + encoded_public_values.append(p256_public_value.data(), + p256_public_value.size()); msg.set_tag(kSCFG); - msg.SetTaglist(kKEXS, kC255, 0); + msg.SetTaglist(kKEXS, kC255, kP256, 0); msg.SetTaglist(kAEAD, kAESG, 0); msg.SetValue(kVERS, static_cast<uint16>(0)); - msg.SetStringPiece(kPUBS, encoded_public_value); + msg.SetStringPiece(kPUBS, encoded_public_values); msg.Insert(extra_tags.tag_value_map().begin(), extra_tags.tag_value_map().end()); @@ -651,9 +725,12 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::ConfigForTesting( scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf); config->set_config(serialized->AsStringPiece()); - QuicServerConfigProtobuf::PrivateKey* key = config->add_key(); - key->set_tag(kC255); - key->set_private_key(private_key); + QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key(); + curve25519_key->set_tag(kC255); + curve25519_key->set_private_key(curve25519_private_key); + QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key(); + p256_key->set_tag(kP256); + p256_key->set_private_key(p256_private_key); return config.release(); } @@ -739,6 +816,14 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( return NULL; } break; + case kP256: + ka.reset(P256KeyExchange::New(private_key)); + if (!ka.get()) { + LOG(WARNING) << "Server config contained an invalid P-256" + " private key."; + return NULL; + } + break; default: LOG(WARNING) << "Server config message contains unknown key exchange " "method: " @@ -791,6 +876,9 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddTestingConfig( QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( const CryptoHandshakeMessage& client_hello, QuicGuid guid, + const IPEndPoint& client_ip, + QuicTime::Delta now_since_unix_epoch, + QuicRandom* rand, CryptoHandshakeMessage* out, QuicCryptoNegotiatedParameters *out_params, string* error_details) { @@ -798,15 +886,27 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( // FIXME(agl): we should use the client's SCID, not just the active config. const Config* config(configs_[active_config_]); + bool valid_source_address_token = false; + StringPiece srct; + if (client_hello.GetStringPiece(kSRCT, &srct) && + ValidateSourceAddressToken(srct, client_ip, now_since_unix_epoch)) { + valid_source_address_token = true; + } + + const string fresh_source_address_token = + NewSourceAddressToken(client_ip, rand, now_since_unix_epoch); + StringPiece scid; if (!client_hello.GetStringPiece(kSCID, &scid) || - scid.as_string() != config->id) { + scid.as_string() != config->id || + !valid_source_address_token) { // If the client didn't provide a server config ID, or gave the wrong one, // then the handshake cannot possibly complete. We reject the handshake and // give the client enough information to do better next time. out->Clear(); out->set_tag(kREJ); out->SetStringPiece(kSCFG, config->serialized); + out->SetStringPiece(kSRCT, fresh_source_address_token); return QUIC_NO_ERROR; } @@ -880,9 +980,102 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( CryptoUtils::SERVER); out->set_tag(kSHLO); + out->SetStringPiece(kSRCT, fresh_source_address_token); return QUIC_NO_ERROR; } +string QuicCryptoServerConfig::NewSourceAddressToken( + const IPEndPoint& ip, + QuicRandom* rand, + QuicTime::Delta now_since_epoch) { + SourceAddressToken source_address_token; + source_address_token.set_ip(ip.ToString()); + source_address_token.set_timestamp(now_since_epoch.ToSeconds()); + + string plaintext = source_address_token.SerializeAsString(); + char nonce[12]; + DCHECK_EQ(sizeof(nonce), + source_address_token_encrypter_->GetNoncePrefixSize() + + sizeof(QuicPacketSequenceNumber)); + rand->RandBytes(nonce, sizeof(nonce)); + + size_t ciphertext_size = + source_address_token_encrypter_->GetCiphertextSize(plaintext.size()); + string result; + result.resize(sizeof(nonce) + ciphertext_size); + memcpy(&result[0], &nonce, sizeof(nonce)); + + if (!source_address_token_encrypter_->Encrypt( + StringPiece(nonce, sizeof(nonce)), StringPiece(), plaintext, + reinterpret_cast<unsigned char*>(&result[sizeof(nonce)]))) { + DCHECK(false); + return string(); + } + + return result; +} + +bool QuicCryptoServerConfig::ValidateSourceAddressToken( + StringPiece token, + const IPEndPoint& ip, + QuicTime::Delta now_since_epoch) { + char nonce[12]; + DCHECK_EQ(sizeof(nonce), + source_address_token_encrypter_->GetNoncePrefixSize() + + sizeof(QuicPacketSequenceNumber)); + + if (token.size() <= sizeof(nonce)) { + return false; + } + memcpy(&nonce, token.data(), sizeof(nonce)); + token.remove_prefix(sizeof(nonce)); + + unsigned char plaintext_stack[128]; + scoped_ptr<unsigned char[]> plaintext_heap; + unsigned char* plaintext; + if (token.size() <= sizeof(plaintext_stack)) { + plaintext = plaintext_stack; + } else { + plaintext_heap.reset(new unsigned char[token.size()]); + plaintext = plaintext_heap.get(); + } + size_t plaintext_length; + + if (!source_address_token_decrypter_->Decrypt( + StringPiece(nonce, sizeof(nonce)), StringPiece(), token, + plaintext, &plaintext_length)) { + return false; + } + + SourceAddressToken source_address_token; + if (!source_address_token.ParseFromArray(plaintext, plaintext_length)) { + return false; + } + + if (source_address_token.ip() != ip.ToString()) { + // It's for a different IP address. + return false; + } + + const QuicTime::Delta delta(now_since_epoch.Subtract( + QuicTime::Delta::FromSeconds(source_address_token.timestamp()))); + const int64 delta_secs = delta.ToSeconds(); + + // TODO(agl): consider whether and how these magic values should be moved to + // a config. + if (delta_secs < -3600) { + // We only allow timestamps to be from an hour in the future. + return false; + } + + if (delta_secs > 86400) { + // We allow one day into the past. + return false; + } + + return true; +} + QuicCryptoServerConfig::Config::Config() { } diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index fe2565e..e433d15 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -11,16 +11,23 @@ #include "base/memory/scoped_ptr.h" #include "base/string_piece.h" +#include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_time.h" namespace net { class KeyExchange; +class QuicClock; class QuicDecrypter; class QuicEncrypter; class QuicRandom; -class QuicClock; +class QuicServerConfigProtobuf; + +namespace test { +class QuicCryptoServerConfigPeer; +} // namespace test // An intermediate format of a handshake message that's convenient for a // CryptoFramer to serialize from or parse into. @@ -170,6 +177,37 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf { std::string config_; }; +// TODO(rtenneti): sync with server more rationally. +class NET_EXPORT_PRIVATE SourceAddressToken { + public: + SourceAddressToken(); + ~SourceAddressToken(); + + std::string SerializeAsString() const; + + bool ParseFromArray(unsigned char* plaintext, size_t plaintext_length); + + std::string ip() const { + return ip_; + } + + int64 timestamp() const { + return timestamp_; + } + + void set_ip(base::StringPiece ip) { + ip_ = ip.as_string(); + } + + void set_timestamp(int64 timestamp) { + timestamp_ = timestamp; + } + + private: + std::string ip_; + int64 timestamp_; +}; + // Parameters negotiated by the crypto handshake. struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { // Initializes the members to 0 or empty values. @@ -233,6 +271,8 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { const std::string& source_address_token() const; const std::string& orbit() const; + void set_source_address_token(base::StringPiece token); + private: std::string server_config_id_; // An opaque id from the server. std::string server_config_; // A serialized handshake message. @@ -306,7 +346,11 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // need to consider locking. class NET_EXPORT_PRIVATE QuicCryptoServerConfig { public: - QuicCryptoServerConfig(); + // |source_address_token_secret|: secret key material used for encrypting and + // decrypting source address tokens. It can be of any length as it is fed + // into a KDF before use. + explicit QuicCryptoServerConfig( + base::StringPiece source_address_token_secret); ~QuicCryptoServerConfig(); // ConfigForTesting generates a QuicServerConfigProtobuf protobuf suitable @@ -332,15 +376,31 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // ProcessClientHello processes |client_hello| and decides whether to accept // or reject the connection. If the connection is to be accepted, |out| is // set to the contents of the ServerHello, |out_params| is completed and - // QUIC_NO_ERROR is returned. |nonce| is used as the server's nonce. - // Otherwise |out| is set to be a REJ message and an error code is returned. + // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ message and + // an error code is returned. + // + // client_hello: the incoming client hello message. + // guid: the GUID for the connection, which is used in key derivation. + // client_ip: the IP address of the client, which is used to generate and + // validate source-address tokens. + // now_since_epoch: the current time, as a delta since the unix epoch, + // which is used to validate client nonces. + // rand: an entropy source + // out: the resulting handshake message (either REJ or SHLO) + // out_params: the state of the handshake + // error_details: used to store a string describing any error. QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, QuicGuid guid, + const IPEndPoint& client_ip, + QuicTime::Delta now_since_epoch, + QuicRandom* rand, CryptoHandshakeMessage* out, QuicCryptoNegotiatedParameters* out_params, std::string* error_details); private: + friend class test::QuicCryptoServerConfigPeer; + // Config represents a server config: a collection of preferences and // Diffie-Hellman public values. struct Config : public QuicCryptoConfig { @@ -365,9 +425,27 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { DISALLOW_COPY_AND_ASSIGN(Config); }; + // NewSourceAddressToken returns a fresh source address token for the given + // IP address. + std::string NewSourceAddressToken(const IPEndPoint& ip, + QuicRandom* rand, + QuicTime::Delta now_since_epoch); + + // ValidateSourceAddressToken returns true if the source address token in + // |token| is a valid and timely token for the IP address |ip| given that the + // current time is |now|. + bool ValidateSourceAddressToken(base::StringPiece token, + const IPEndPoint& ip, + QuicTime::Delta now_since_epoch); + std::map<ServerConfigID, Config*> configs_; - std::string active_config_; + ServerConfigID active_config_; + + // These members are used to encrypt and decrypt the source address tokens + // that we receive from and send to clients. + scoped_ptr<QuicEncrypter> source_address_token_encrypter_; + scoped_ptr<QuicDecrypter> source_address_token_decrypter_; }; } // namespace net diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc index 0ce2ed4..911bc29 100644 --- a/net/quic/crypto/crypto_handshake_test.cc +++ b/net/quic/crypto/crypto_handshake_test.cc @@ -4,11 +4,83 @@ #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_time.h" +#include "net/quic/test_tools/mock_clock.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using base::StringPiece; +using std::string; + namespace net { namespace test { +class QuicCryptoServerConfigPeer { + public: + explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config) + : server_config_(server_config) { + } + + string NewSourceAddressToken(IPEndPoint ip, + QuicRandom* rand, + QuicTime::Delta now) { + return server_config_->NewSourceAddressToken(ip, rand, now); + } + + bool ValidateSourceAddressToken(StringPiece srct, + IPEndPoint ip, + QuicTime::Delta now) { + return server_config_->ValidateSourceAddressToken(srct, ip, now); + } + + private: + QuicCryptoServerConfig* const server_config_; +}; + +TEST(QuicCryptoServerConfigTest, ServerConfig) { + QuicCryptoServerConfig server("source address token secret"); + MockClock clock; + CryptoHandshakeMessage extra_tags; + + scoped_ptr<CryptoHandshakeMessage>( + server.AddTestingConfig(QuicRandom::GetInstance(), &clock, extra_tags)); +} + +TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + QuicCryptoServerConfig server("source address token secret"); + IPAddressNumber ip; + CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); + IPEndPoint ip4 = IPEndPoint(ip, 1); + CHECK(ParseIPLiteralToNumber("2001:db8:0::42", &ip)); + IPEndPoint ip6 = IPEndPoint(ip, 2); + QuicRandom* rand = QuicRandom::GetInstance(); + MockClock clock; + QuicCryptoServerConfigPeer peer(&server); + + QuicTime::Delta now = clock.NowAsDeltaSinceUnixEpoch(); + const QuicTime::Delta original_time = now; + + const string token4 = peer.NewSourceAddressToken(ip4, rand, now); + LOG(INFO) << __LINE__ << token4; + const string token6 = peer.NewSourceAddressToken(ip6, rand, now); + LOG(INFO) << __LINE__ << token6; + EXPECT_TRUE(peer.ValidateSourceAddressToken(token4, ip4, now)); + EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip6, now)); + EXPECT_TRUE(peer.ValidateSourceAddressToken(token6, ip6, now)); + + now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7)); + EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now)); + + now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2)); + EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now)); +} + } // namespace test } // namespace net diff --git a/net/quic/crypto/null_decrypter.cc b/net/quic/crypto/null_decrypter.cc index 05d6bda..0875558 100644 --- a/net/quic/crypto/null_decrypter.cc +++ b/net/quic/crypto/null_decrypter.cc @@ -19,9 +19,37 @@ bool NullDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return nonce_prefix.empty(); } -QuicData* NullDecrypter::Decrypt(QuicPacketSequenceNumber /*sequence_number*/, - StringPiece associated_data, - StringPiece ciphertext) { +bool NullDecrypter::Decrypt(StringPiece /*nonce*/, + StringPiece associated_data, + StringPiece ciphertext, + unsigned char* output, + size_t* output_length) { + QuicDataReader reader(ciphertext.data(), ciphertext.length()); + + uint128 hash; + if (!reader.ReadUInt128(&hash)) { + return false; + } + + StringPiece plaintext = reader.ReadRemainingPayload(); + + // TODO(rch): avoid buffer copy here + string buffer = associated_data.as_string(); + plaintext.AppendToString(&buffer); + + if (hash != QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length())) { + return false; + } + memcpy(output, plaintext.data(), plaintext.length()); + *output_length = plaintext.length(); + return true; +} + +QuicData* NullDecrypter::DecryptPacket(QuicPacketSequenceNumber /*seq_number*/, + StringPiece associated_data, + StringPiece ciphertext) { + // It's worth duplicating |Decrypt|, above, in order to save a copy by using + // the shared-data QuicData constructor directly. QuicDataReader reader(ciphertext.data(), ciphertext.length()); uint128 hash; diff --git a/net/quic/crypto/null_decrypter.h b/net/quic/crypto/null_decrypter.h index bd4a7ea..01beb2d 100644 --- a/net/quic/crypto/null_decrypter.h +++ b/net/quic/crypto/null_decrypter.h @@ -21,9 +21,14 @@ class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { // QuicDecrypter implementation virtual bool SetKey(base::StringPiece key) OVERRIDE; virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; - virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece ciphertext) OVERRIDE; + virtual bool Decrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece ciphertext, + unsigned char* output, + size_t* output_length) OVERRIDE; + virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece ciphertext) OVERRIDE; virtual base::StringPiece GetKey() const OVERRIDE; virtual base::StringPiece GetNoncePrefix() const OVERRIDE; }; diff --git a/net/quic/crypto/null_decrypter_test.cc b/net/quic/crypto/null_decrypter_test.cc index 56aa531..7854b04 100644 --- a/net/quic/crypto/null_decrypter_test.cc +++ b/net/quic/crypto/null_decrypter_test.cc @@ -23,9 +23,10 @@ TEST(NullDecrypterTest, Decrypt) { }; NullDecrypter decrypter; scoped_ptr<QuicData> decrypted( - decrypter.Decrypt(0, "hello world!", - StringPiece(reinterpret_cast<const char*>(expected), - arraysize(expected)))); + decrypter.DecryptPacket( + 0, "hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); ASSERT_TRUE(decrypted.get()); EXPECT_EQ("goodbye!", decrypted->AsStringPiece()); } @@ -43,9 +44,10 @@ TEST(NullDecrypterTest, BadHash) { }; NullDecrypter decrypter; scoped_ptr<QuicData> decrypted( - decrypter.Decrypt(0, "hello world!", - StringPiece(reinterpret_cast<const char*>(expected), - arraysize(expected)))); + decrypter.DecryptPacket( + 0, "hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); ASSERT_FALSE(decrypted.get()); } @@ -59,9 +61,10 @@ TEST(NullDecrypterTest, ShortInput) { }; NullDecrypter decrypter; scoped_ptr<QuicData> decrypted( - decrypter.Decrypt(0, "hello world!", - StringPiece(reinterpret_cast<const char*>(expected), - arraysize(expected)))); + decrypter.DecryptPacket( + 0, "hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); ASSERT_FALSE(decrypted.get()); } diff --git a/net/quic/crypto/null_encrypter.cc b/net/quic/crypto/null_encrypter.cc index b563107..22370ba 100644 --- a/net/quic/crypto/null_encrypter.cc +++ b/net/quic/crypto/null_encrypter.cc @@ -21,18 +21,27 @@ bool NullEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { return nonce_prefix.empty(); } -QuicData* NullEncrypter::Encrypt(QuicPacketSequenceNumber /*sequence_number*/, - StringPiece associated_data, - StringPiece plaintext) { - // TODO(rch): avoid buffer copy here +bool NullEncrypter::Encrypt( + StringPiece /*nonce*/, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { string buffer = associated_data.as_string(); plaintext.AppendToString(&buffer); uint128 hash = QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length()); - QuicDataWriter writer(plaintext.length() + kHashSize); - writer.WriteUInt128(hash); - writer.WriteBytes(plaintext.data(), plaintext.length()); - size_t len = writer.length(); - return new QuicData(writer.take(), len, true); + memcpy(output, &hash, sizeof(hash)); + memcpy(output + sizeof(hash), plaintext.data(), plaintext.size()); + return true; +} + +QuicData* NullEncrypter::EncryptPacket( + QuicPacketSequenceNumber /*sequence_number*/, + StringPiece associated_data, + StringPiece plaintext) { + const size_t len = plaintext.size() + sizeof(uint128); + uint8* buffer = new uint8[len]; + Encrypt(StringPiece(), associated_data, plaintext, buffer); + return new QuicData(reinterpret_cast<char*>(buffer), len, true); } size_t NullEncrypter::GetKeySize() const { diff --git a/net/quic/crypto/null_encrypter.h b/net/quic/crypto/null_encrypter.h index c452348..ed05e1f 100644 --- a/net/quic/crypto/null_encrypter.h +++ b/net/quic/crypto/null_encrypter.h @@ -21,9 +21,13 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { // QuicEncrypter implementation virtual bool SetKey(base::StringPiece key) OVERRIDE; virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; - virtual QuicData* Encrypt(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece plaintext) OVERRIDE; + virtual bool Encrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece plaintext, + unsigned char* output) OVERRIDE; + virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece plaintext) OVERRIDE; virtual size_t GetKeySize() const OVERRIDE; virtual size_t GetNoncePrefixSize() const OVERRIDE; virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE; diff --git a/net/quic/crypto/null_encrypter_test.cc b/net/quic/crypto/null_encrypter_test.cc index df9bcdc..e1e6834 100644 --- a/net/quic/crypto/null_encrypter_test.cc +++ b/net/quic/crypto/null_encrypter_test.cc @@ -22,8 +22,8 @@ TEST(NullEncrypterTest, Encrypt) { 'b', 'y', 'e', '!', }; NullEncrypter encrypter; - scoped_ptr<QuicData> encrypted(encrypter.Encrypt(0, "hello world!", - "goodbye!")); + scoped_ptr<QuicData> encrypted(encrypter.EncryptPacket(0, "hello world!", + "goodbye!")); ASSERT_TRUE(encrypted.get()); test::CompareCharArraysWithHexError( "encrypted data", encrypted->data(), encrypted->length(), diff --git a/net/quic/crypto/p256_key_exchange_nss.cc b/net/quic/crypto/p256_key_exchange_nss.cc index eff2028..c1de42e 100644 --- a/net/quic/crypto/p256_key_exchange_nss.cc +++ b/net/quic/crypto/p256_key_exchange_nss.cc @@ -7,6 +7,10 @@ #include "base/logging.h" #include "base/sys_byteorder.h" +using base::StringPiece; +using std::string; +using std::vector; + namespace net { namespace { @@ -17,9 +21,9 @@ namespace { // use the same approach. const char kExportPassword[] = ""; -// Convert StringPiece to vector of uint8 -static std::vector<uint8> StringPieceToVector(base::StringPiece piece) { - return std::vector<uint8>(piece.data(), piece.data() + piece.length()); +// Convert StringPiece to vector of uint8. +static vector<uint8> StringPieceToVector(StringPiece piece) { + return vector<uint8>(piece.data(), piece.data() + piece.length()); } } // namespace @@ -34,9 +38,9 @@ P256KeyExchange::~P256KeyExchange() { } // static -P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { +P256KeyExchange* P256KeyExchange::New(StringPiece key) { if (key.size() < 2) { - DLOG(INFO) << "Key pair is too small"; + DLOG(INFO) << "Key pair is too small."; return NULL; } @@ -45,18 +49,18 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { (static_cast<size_t>(data[1]) << 8); key.remove_prefix(2); if (key.size() < size) { - DLOG(INFO) << "Key pair does not contain key material"; + DLOG(INFO) << "Key pair does not contain key material."; return NULL; } - base::StringPiece private_piece(key.data(), size); + StringPiece private_piece(key.data(), size); key.remove_prefix(size); if (key.empty()) { - DLOG(INFO) << "Key pair does not contain public key"; + DLOG(INFO) << "Key pair does not contain public key."; return NULL; } - base::StringPiece public_piece(key); + StringPiece public_piece(key); scoped_ptr<crypto::ECPrivateKey> key_pair( crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( @@ -66,7 +70,7 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { StringPieceToVector(public_piece))); if (!key_pair.get()) { - DLOG(INFO) << "Can't decrypt private key"; + DLOG(INFO) << "Can't decrypt private key."; return NULL; } @@ -76,14 +80,14 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { public_key->u.ec.publicValue.len != kUncompressedP256PointBytes || !public_key->u.ec.publicValue.data || public_key->u.ec.publicValue.data[0] != kUncompressedECPointForm) { - DLOG(INFO) << "Key is invalid"; + DLOG(INFO) << "Key is invalid."; return NULL; } // Ensure that the key is using the correct curve, i.e., NIST P-256. const SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); if (!oid_data) { - DLOG(INFO) << "Can't get P-256's OID"; + DLOG(INFO) << "Can't get P-256's OID."; return NULL; } @@ -93,7 +97,7 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { public_key->u.ec.DEREncodedParams.data[1] != oid_data->oid.len || memcmp(public_key->u.ec.DEREncodedParams.data + 2, oid_data->oid.data, oid_data->oid.len) != 0) { - DLOG(INFO) << "Key is invalid"; + DLOG(INFO) << "Key is invalid."; } return new P256KeyExchange(key_pair.release(), @@ -101,29 +105,29 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { } // static -std::string P256KeyExchange::NewPrivateKey() { +string P256KeyExchange::NewPrivateKey() { scoped_ptr<crypto::ECPrivateKey> key_pair(crypto::ECPrivateKey::Create()); if (!key_pair.get()) { - DLOG(INFO) << "Can't generate new key pair"; - return std::string(); + DLOG(INFO) << "Can't generate new key pair."; + return string(); } - std::vector<uint8> private_key; + vector<uint8> private_key; if (!key_pair->ExportEncryptedPrivateKey(kExportPassword, 1 /* iteration */, &private_key)) { - DLOG(INFO) << "Can't export private key"; - return std::string(); + DLOG(INFO) << "Can't export private key."; + return string(); } // NSS lacks the ability to import an ECC private key without // also importing the public key, so it is necessary to also // store the public key. - std::vector<uint8> public_key; + vector<uint8> public_key; if (!key_pair->ExportPublicKey(&public_key)) { - DLOG(INFO) << "Can't export public key"; - return std::string(); + DLOG(INFO) << "Can't export public key."; + return string(); } // TODO(thaidn): determine how large encrypted private key can be @@ -131,7 +135,7 @@ std::string P256KeyExchange::NewPrivateKey() { const size_t result_size = sizeof(private_key_size) + private_key_size + public_key.size(); - std::vector<char> result(result_size); + vector<char> result(result_size); char* resultp = &result[0]; // Export the key string. // The first two bytes are the private key's size in little endian. @@ -142,15 +146,15 @@ std::string P256KeyExchange::NewPrivateKey() { resultp += private_key.size(); memcpy(resultp, &public_key[0], public_key.size()); - return std::string(&result[0], result_size); + return string(&result[0], result_size); } bool P256KeyExchange::CalculateSharedKey( - const base::StringPiece& peer_public_value, - std::string* out_result) const { + const StringPiece& peer_public_value, + string* out_result) const { if (peer_public_value.size() != kUncompressedP256PointBytes || peer_public_value[0] != kUncompressedECPointForm) { - DLOG(INFO) << "Peer public value is invalid"; + DLOG(INFO) << "Peer public value is invalid."; return false; } @@ -194,18 +198,18 @@ bool P256KeyExchange::CalculateSharedKey( NULL)); if (!premaster_secret.get()) { - DLOG(INFO) << "Can't derive ECDH shared key"; + DLOG(INFO) << "Can't derive ECDH shared key."; return false; } if (PK11_ExtractKeyValue(premaster_secret.get()) != SECSuccess) { - DLOG(INFO) << "Can't extract raw ECDH shared key"; + DLOG(INFO) << "Can't extract raw ECDH shared key."; return false; } SECItem* key_data = PK11_GetKeyData(premaster_secret.get()); if (!key_data || !key_data->data || key_data->len != kP256FieldBytes) { - DLOG(INFO) << "ECDH shared key is invalid"; + DLOG(INFO) << "ECDH shared key is invalid."; return false; } @@ -213,9 +217,9 @@ bool P256KeyExchange::CalculateSharedKey( return true; } -base::StringPiece P256KeyExchange::public_value() const { - return base::StringPiece(reinterpret_cast<const char*>(public_key_), - sizeof(public_key_)); +StringPiece P256KeyExchange::public_value() const { + return StringPiece(reinterpret_cast<const char*>(public_key_), + sizeof(public_key_)); } CryptoTag P256KeyExchange::tag() const { diff --git a/net/quic/crypto/p256_key_exchange_openssl.cc b/net/quic/crypto/p256_key_exchange_openssl.cc index ae7f30e..051adcf 100644 --- a/net/quic/crypto/p256_key_exchange_openssl.cc +++ b/net/quic/crypto/p256_key_exchange_openssl.cc @@ -10,6 +10,9 @@ #include "base/logging.h" +using base::StringPiece; +using std::string; + namespace net { P256KeyExchange::P256KeyExchange(EC_KEY* private_key, const uint8* public_key) @@ -21,7 +24,7 @@ P256KeyExchange::~P256KeyExchange() { } // static -P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { +P256KeyExchange* P256KeyExchange::New(StringPiece key) { if (key.empty()) { DLOG(INFO) << "Private key is empty"; return NULL; @@ -31,19 +34,19 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> private_key( d2i_ECPrivateKey(NULL, &keyp, key.size())); if (!private_key.get() || !EC_KEY_check_key(private_key.get())) { - DLOG(INFO) << "Private key is invalid"; + DLOG(INFO) << "Private key is invalid."; return NULL; } uint8 public_key[kUncompressedP256PointBytes]; if (EC_POINT_point2oct( - EC_KEY_get0_group(private_key.get()), - EC_KEY_get0_public_key(private_key.get()), - POINT_CONVERSION_UNCOMPRESSED, - public_key, - sizeof(public_key), - NULL) != sizeof(public_key)) { - DLOG(INFO) << "Can't get public key"; + EC_KEY_get0_group(private_key.get()), + EC_KEY_get0_public_key(private_key.get()), + POINT_CONVERSION_UNCOMPRESSED, + public_key, + sizeof(public_key), + NULL) != sizeof(public_key)) { + DLOG(INFO) << "Can't get public key."; return NULL; } @@ -51,38 +54,38 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { } // static -std::string P256KeyExchange::NewPrivateKey() { +string P256KeyExchange::NewPrivateKey() { crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> key( EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); if (!key.get() || !EC_KEY_generate_key(key.get())) { - DLOG(INFO) << "Can't generate a new private key"; - return std::string(); + DLOG(INFO) << "Can't generate a new private key."; + return string(); } int key_len = i2d_ECPrivateKey(key.get(), NULL); if (key_len <= 0) { DLOG(INFO) << "Can't convert private key to string"; - return std::string(); + return string(); } scoped_ptr<uint8[]> private_key(new uint8[key_len]); uint8* keyp = private_key.get(); if (!i2d_ECPrivateKey(key.get(), &keyp)) { - DLOG(INFO) << "Can't convert private key to string"; - return std::string(); + DLOG(INFO) << "Can't convert private key to string."; + return string(); } - return std::string(reinterpret_cast<char*>(private_key.get()), key_len); + return string(reinterpret_cast<char*>(private_key.get()), key_len); } bool P256KeyExchange::CalculateSharedKey( - const base::StringPiece& peer_public_value, - std::string* out_result) const { + const StringPiece& peer_public_value, + string* out_result) const { if (peer_public_value.size() != kUncompressedP256PointBytes) { DLOG(INFO) << "Peer public value is invalid"; return false; } crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free> point( - EC_POINT_new(EC_KEY_get0_group(private_key_.get()))); + EC_POINT_new(EC_KEY_get0_group(private_key_.get()))); if (!point.get() || !EC_POINT_oct2point( /* also test if point is on curve */ EC_KEY_get0_group(private_key_.get()), @@ -90,7 +93,7 @@ bool P256KeyExchange::CalculateSharedKey( reinterpret_cast<const uint8*>(peer_public_value.data()), peer_public_value.size(), NULL)) { - DLOG(INFO) << "Can't convert peer public value to curve point"; + DLOG(INFO) << "Can't convert peer public value to curve point."; return false; } @@ -101,7 +104,7 @@ bool P256KeyExchange::CalculateSharedKey( point.get(), private_key_.get(), NULL) != sizeof(result)) { - DLOG(INFO) << "Can't compute ECDH shared key"; + DLOG(INFO) << "Can't compute ECDH shared key."; return false; } @@ -109,9 +112,9 @@ bool P256KeyExchange::CalculateSharedKey( return true; } -base::StringPiece P256KeyExchange::public_value() const { - return base::StringPiece(reinterpret_cast<const char*>(public_key_), - sizeof(public_key_)); +StringPiece P256KeyExchange::public_value() const { + return StringPiece(reinterpret_cast<const char*>(public_key_), + sizeof(public_key_)); } CryptoTag P256KeyExchange::tag() const { diff --git a/net/quic/crypto/p256_key_exchange_test.cc b/net/quic/crypto/p256_key_exchange_test.cc index 799c853..8ea59da 100644 --- a/net/quic/crypto/p256_key_exchange_test.cc +++ b/net/quic/crypto/p256_key_exchange_test.cc @@ -7,6 +7,8 @@ #include "base/logging.h" #include "testing/gtest/include/gtest/gtest.h" +using std::string; + namespace net { namespace test { @@ -14,8 +16,8 @@ namespace test { // parties end up with the same key. TEST(P256KeyExchange, SharedKey) { for (int i = 0; i < 5; i++) { - std::string alice_private(P256KeyExchange::NewPrivateKey()); - std::string bob_private(P256KeyExchange::NewPrivateKey()); + string alice_private(P256KeyExchange::NewPrivateKey()); + string bob_private(P256KeyExchange::NewPrivateKey()); ASSERT_FALSE(alice_private.empty()); ASSERT_FALSE(bob_private.empty()); diff --git a/net/quic/crypto/quic_decrypter.h b/net/quic/crypto/quic_decrypter.h index c648668..349425a 100644 --- a/net/quic/crypto/quic_decrypter.h +++ b/net/quic/crypto/quic_decrypter.h @@ -40,15 +40,27 @@ class NET_EXPORT_PRIVATE QuicDecrypter { // packet sequence number, even when retransmitting a lost packet. virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) = 0; + // Decrypt authenticates |associated_data| and |ciphertext| and then decrypts + // |ciphertext| into |output|, using |nonce|. |nonce| must be 8 bytes longer + // than the nonce prefix length returned by GetNoncePrefixSize() (of the + // encrypter). |output| must be as long as |ciphertext| on entry and, on + // successful return, the true length of the plaintext will be written to + // |*output_length|. + virtual bool Decrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece ciphertext, + unsigned char* output, + size_t* output_length) = 0; + // Returns a newly created QuicData object containing the decrypted // |ciphertext| or NULL if there is an error. |sequence_number| is // appended to the |nonce_prefix| value provided in SetNoncePrefix() // to form the nonce. - // TODO(wtc): add a way for Decrypt to report decryption failure due + // TODO(wtc): add a way for DecryptPacket to report decryption failure due // to non-authentic inputs, as opposed to other reasons for failure. - virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece ciphertext) = 0; + virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece ciphertext) = 0; // For use by unit tests only. virtual base::StringPiece GetKey() const = 0; diff --git a/net/quic/crypto/quic_encrypter.h b/net/quic/crypto/quic_encrypter.h index 013aff1..86d0e3a 100644 --- a/net/quic/crypto/quic_encrypter.h +++ b/net/quic/crypto/quic_encrypter.h @@ -40,13 +40,23 @@ class NET_EXPORT_PRIVATE QuicEncrypter { // packet sequence number, even when retransmitting a lost packet. virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) = 0; + // Encrypt encrypts |plaintext| and writes the ciphertext, plus a MAC over + // both |associated_data| and |plaintext| to |output|, using |nonce| as the + // nonce. |nonce| must be |8+GetNoncePrefixSize()| bytes long and |output| + // must point to a buffer that is at least + // |GetCiphertextSize(plaintext.size()| bytes long. + virtual bool Encrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece plaintext, + unsigned char* output) = 0; + // Returns a newly created QuicData object containing the encrypted // |plaintext| as well as a MAC over both |plaintext| and |associated_data|, // or NULL if there is an error. |sequence_number| is appended to the // |nonce_prefix| value provided in SetNoncePrefix() to form the nonce. - virtual QuicData* Encrypt(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece plaintext) = 0; + virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece plaintext) = 0; // GetKeySize() and GetNoncePrefixSize() tell the HKDF class how many bytes // of key material needs to be derived from the master secret. diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index d87f903..1ca0080 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -10,6 +10,7 @@ #include "net/base/capturing_net_log.h" #include "net/base/net_log_unittest.h" #include "net/base/test_completion_callback.h" +#include "net/quic/crypto/aes_128_gcm_encrypter.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" @@ -51,10 +52,20 @@ class QuicClientSessionTest : public ::testing::Test { }; TEST_F(QuicClientSessionTest, CryptoConnect) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); } TEST_F(QuicClientSessionTest, MaxNumConnections) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); std::vector<QuicReliableClientStream*> streams; @@ -85,6 +96,11 @@ TEST_F(QuicClientSessionTest, GoAwayReceived) { } TEST_F(QuicClientSessionTest, Logging) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); // TODO(rch): Add some helper methods to simplify packet creation in tests. @@ -92,10 +108,11 @@ TEST_F(QuicClientSessionTest, Logging) { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false); QuicRstStreamFrame frame; frame.stream_id = 2; - frame.error_code = QUIC_CONNECTION_TIMED_OUT; + frame.error_code = QUIC_STREAM_CONNECTION_ERROR; frame.error_details = "doh!"; QuicFrames frames; @@ -143,7 +160,7 @@ TEST_F(QuicClientSessionTest, Logging) { EXPECT_EQ(frame.stream_id, static_cast<QuicStreamId>(stream_id)); int error_code; ASSERT_TRUE(entries[pos].GetIntegerValue("error_code", &error_code)); - EXPECT_EQ(frame.error_code, static_cast<QuicErrorCode>(error_code)); + EXPECT_EQ(frame.error_code, static_cast<QuicRstStreamErrorCode>(error_code)); std::string details; ASSERT_TRUE(entries[pos].GetStringValue("details", &details)); EXPECT_EQ(frame.error_details, details); diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 5bb15c6..6449ee1 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -64,13 +64,6 @@ const int kMaxPacketsToSerializeAtOnce = 6; // eventually cede. 10 is arbitrary. const int kMaxPacketsPerRetransmissionAlarm = 10; -// Named constant for WritePacket() -const bool kForce = true; -// Named constant for CanWrite(). -const bool kIsRetransmission = true; -// Named constant for WritePacket. -const bool kHasRetransmittableData = true; - bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { QuicPacketSequenceNumber delta = (a > b) ? a - b : b - a; return delta <= kMaxPacketGap; @@ -78,6 +71,8 @@ bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { } // namespace +#define ENDPOINT (is_server_ ? "Server: " : " Client: ") + QuicConnection::QuicConnection(QuicGuid guid, IPEndPoint address, QuicConnectionHelperInterface* helper, @@ -86,6 +81,7 @@ QuicConnection::QuicConnection(QuicGuid guid, framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + helper->GetClock()->ApproximateNow(), is_server), clock_(helper->GetClock()), random_generator_(helper->GetRandomGenerator()), @@ -116,7 +112,6 @@ QuicConnection::QuicConnection(QuicGuid guid, helper_->SetTimeoutAlarm(timeout_); framer_.set_visitor(this); framer_.set_entropy_calculator(&entropy_manager_); - memset(&last_header_, 0, sizeof(last_header_)); outgoing_ack_.sent_info.least_unacked = 0; outgoing_ack_.sent_info.entropy_hash = 0; outgoing_ack_.received_info.largest_observed = 0; @@ -125,7 +120,7 @@ QuicConnection::QuicConnection(QuicGuid guid, /* if (FLAGS_fake_packet_loss_percentage > 0) { int32 seed = RandomBase::WeakSeed32(); - LOG(INFO) << "Seeding packet loss with " << seed; + LOG(INFO) << ENDPOINT << "Seeding packet loss with " << seed; random_.reset(new MTRandom(seed)); } */ @@ -163,7 +158,7 @@ void QuicConnection::OnPacket() { // since it affects the timeout of the connection. time_of_last_received_packet_ = clock_->Now(); DVLOG(1) << "time of last received packet: " - << time_of_last_received_packet_.ToMicroseconds(); + << time_of_last_received_packet_.ToDebuggingValue(); // TODO(alyssar, rch) handle migration! self_address_ = last_self_address_; @@ -275,14 +270,14 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { ++stats_.packets_dropped; if (header.public_header.guid != guid_) { - DLOG(INFO) << "Ignoring packet from unexpected GUID: " + DLOG(INFO) << ENDPOINT << "Ignoring packet from unexpected GUID: " << header.public_header.guid << " instead of " << guid_; return false; } if (!Near(header.packet_sequence_number, last_header_.packet_sequence_number)) { - DLOG(INFO) << "Packet " << header.packet_sequence_number + DLOG(INFO) << ENDPOINT << "Packet " << header.packet_sequence_number << " out of bounds. Discarding"; // TODO(alyssar) close the connection entirely. return false; @@ -298,8 +293,8 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { if (version_negotiation_state_ != NEGOTIATED_VERSION) { if (is_server_) { if (!header.public_header.version_flag) { - DLOG(WARNING) << "Got packet without version flag before version " - << "negotiated."; + DLOG(WARNING) << ENDPOINT << "Got packet without version flag before " + << "version negotiated."; // Packets should have the version flag till version negotiation is // done. CloseConnection(QUIC_INVALID_VERSION, false); @@ -346,7 +341,7 @@ void QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { DVLOG(1) << "OnAckFrame: " << incoming_ack; if (last_header_.packet_sequence_number <= largest_seen_packet_with_ack_) { - DLOG(INFO) << "Received an old ack frame: ignoring"; + DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; return; } largest_seen_packet_with_ack_ = last_header_.packet_sequence_number; @@ -376,10 +371,9 @@ void QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { if (queued_packets_.empty()) { return; } - bool has_retransmittable_data = true; - QuicTime::Delta delay = congestion_manager_.TimeUntilSend( - time_of_last_received_packet_, false, has_retransmittable_data); + time_of_last_received_packet_, NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA); if (delay.IsZero()) { helper_->UnregisterSendAlarmIfRegistered(); if (!write_blocked_) { @@ -402,7 +396,7 @@ void QuicConnection::OnCongestionFeedbackFrame( bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (incoming_ack.received_info.largest_observed > packet_creator_.sequence_number()) { - DLOG(ERROR) << "Client observed unsent packet:" + DLOG(ERROR) << ENDPOINT << "Peer's observed unsent packet:" << incoming_ack.received_info.largest_observed << " vs " << packet_creator_.sequence_number(); // We got an error for data we have not sent. Error out. @@ -411,7 +405,7 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (incoming_ack.received_info.largest_observed < peer_largest_observed_packet_) { - DLOG(ERROR) << "Client's largest_observed packet decreased:" + DLOG(ERROR) << ENDPOINT << "Peer's largest_observed packet decreased:" << incoming_ack.received_info.largest_observed << " vs " << peer_largest_observed_packet_; // We got an error for data we have not sent. Error out. @@ -424,7 +418,7 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { GetMaxUnackedPackets(last_header_.public_header.version_flag)); if (incoming_ack.sent_info.least_unacked < peer_least_packet_awaiting_ack_) { - DLOG(ERROR) << "Client sent low least_unacked: " + DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " << incoming_ack.sent_info.least_unacked << " vs " << peer_least_packet_awaiting_ack_; // We never process old ack frames, so this number should only increase. @@ -433,7 +427,7 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (incoming_ack.sent_info.least_unacked > last_header_.packet_sequence_number) { - DLOG(ERROR) << "Client sent least_unacked:" + DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:" << incoming_ack.sent_info.least_unacked << " greater than the enclosing packet sequence number:" << last_header_.packet_sequence_number; @@ -443,7 +437,7 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (!incoming_ack.received_info.missing_packets.empty() && *incoming_ack.received_info.missing_packets.rbegin() > incoming_ack.received_info.largest_observed) { - DLOG(ERROR) << "Client sent missing packet: " + DLOG(ERROR) << ENDPOINT << "Peer sent missing packet: " << *incoming_ack.received_info.missing_packets.rbegin() << " greater than largest observed: " << incoming_ack.received_info.largest_observed; @@ -453,7 +447,7 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (!incoming_ack.received_info.missing_packets.empty() && *incoming_ack.received_info.missing_packets.begin() < least_packet_awaited_by_peer_) { - DLOG(ERROR) << "Client sent missing packet: " + DLOG(ERROR) << ENDPOINT << "Peer sent missing packet: " << *incoming_ack.received_info.missing_packets.begin() << "smaller than least_packet_awaited_by_peer_: " << least_packet_awaited_by_peer_; @@ -464,7 +458,7 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { incoming_ack.received_info.largest_observed, incoming_ack.received_info.missing_packets, incoming_ack.received_info.entropy_hash)) { - DLOG(ERROR) << "Client sent invalid entropy."; + DLOG(ERROR) << ENDPOINT << "Peer sent invalid entropy."; return false; } @@ -589,7 +583,7 @@ void QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { debug_visitor_->OnRstStreamFrame(frame); } DLOG(INFO) << "Stream reset with error " - << QuicUtils::ErrorToString(frame.error_code); + << QuicUtils::StreamErrorToString(frame.error_code); visitor_->OnRstStream(frame); } @@ -598,13 +592,13 @@ void QuicConnection::OnConnectionCloseFrame( if (debug_visitor_) { debug_visitor_->OnConnectionCloseFrame(frame); } - DLOG(INFO) << "Connection closed with error " + DLOG(INFO) << ENDPOINT << "Connection closed with error " << QuicUtils::ErrorToString(frame.error_code); CloseConnection(frame.error_code, true); } void QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { - DLOG(INFO) << "Go away received with error " + DLOG(INFO) << ENDPOINT << "Go away received with error " << QuicUtils::ErrorToString(frame.error_code) << " and reason:" << frame.reason_phrase; visitor_->OnGoAway(frame); @@ -614,15 +608,16 @@ void QuicConnection::OnPacketComplete() { // TODO(satyamshekhar): Don't do anything if this packet closed the // connection. if (!last_packet_revived_) { - DLOG(INFO) << "Got packet " << last_header_.packet_sequence_number + DLOG(INFO) << ENDPOINT << "Got packet " + << last_header_.packet_sequence_number << " with " << last_stream_frames_.size() << " stream frames for " << last_header_.public_header.guid; congestion_manager_.RecordIncomingPacket( last_size_, last_header_.packet_sequence_number, time_of_last_received_packet_, last_packet_revived_); } else { - DLOG(INFO) << "Got revived packet with " << last_stream_frames_.size() - << " frames."; + DLOG(INFO) << ENDPOINT << "Got revived packet with " + << last_stream_frames_.size() << " frames."; } if ((last_stream_frames_.empty() || @@ -673,7 +668,7 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, } void QuicConnection::SendRstStream(QuicStreamId id, - QuicErrorCode error) { + QuicRstStreamErrorCode error) { packet_generator_.AddControlFrame( QuicFrame(new QuicRstStreamFrame(id, error))); } @@ -716,14 +711,15 @@ bool QuicConnection::OnCanWrite() { // or the congestion manager to prohibit sending. If we've sent everything // we had queued and we're still not blocked, let the visitor know it can // write more. - if (CanWrite(false, true)) { + if (CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA)) { packet_generator_.StartBatchOperations(); bool all_bytes_written = visitor_->OnCanWrite(); packet_generator_.FinishBatchOperations(); // After the visitor writes, it may have caused the socket to become write // blocked or the congestion manager to prohibit sending, so check again. - if (!write_blocked_ && !all_bytes_written && CanWrite(false, true)) { + if (!write_blocked_ && !all_bytes_written && + CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA)) { // We're not write blocked, but some stream didn't write out all of its // bytes. Register for 'immediate' resumption so we'll keep writing after // other quic connections have had a chance to use the socket. @@ -747,8 +743,8 @@ bool QuicConnection::WriteQueuedPackets() { num_queued_packets = queued_packets_.size(); if (WritePacket(packet_iterator->sequence_number, packet_iterator->packet, - packet_iterator->has_retransmittable_data, - !kForce)) { + packet_iterator->retransmittable, + NO_FORCE)) { packet_iterator = queued_packets_.erase(packet_iterator); } else { // Continue, because some queued packets may still be writable. @@ -761,7 +757,8 @@ bool QuicConnection::WriteQueuedPackets() { } void QuicConnection::RecordPacketReceived(const QuicPacketHeader& header) { - DLOG(INFO) << "Recording received packet: " << header.packet_sequence_number; + DLOG(INFO) << ENDPOINT << "Recording received packet: " + << header.packet_sequence_number; QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; DCHECK(IsAwaitingPacket(outgoing_ack_.received_info, sequence_number)); @@ -858,11 +855,11 @@ void QuicConnection::RetransmitPacket( SendOrQueuePacket(serialized_packet.sequence_number, serialized_packet.packet, serialized_packet.entropy_hash, - true); + HAS_RETRANSMITTABLE_DATA); } -bool QuicConnection::CanWrite(bool is_retransmission, - bool has_retransmittable_data) { +bool QuicConnection::CanWrite(Retransmission retransmission, + HasRetransmittableData retransmittable) { // TODO(ianswett): If the packet is a retransmit, the current send alarm may // be too long. if (write_blocked_ || helper_->IsSendAlarmSet()) { @@ -871,7 +868,7 @@ bool QuicConnection::CanWrite(bool is_retransmission, QuicTime now = clock_->Now(); QuicTime::Delta delay = congestion_manager_.TimeUntilSend( - now, is_retransmission, has_retransmittable_data); + now, retransmission, retransmittable); if (delay.IsInfinite()) { // TODO(pwestin): should be false but trigger other bugs see b/8350327. return true; @@ -915,40 +912,42 @@ void QuicConnection::MaybeSetupRetransmission( if (!handling_retransmission_timeout_) { helper_->SetRetransmissionAlarm(retransmission_delay); } - // TODO(satyamshekhar): restore pacekt reordering with Ian's TODO in + // TODO(satyamshekhar): restore packet reordering with Ian's TODO in // SendStreamData(). } bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, QuicPacket* packet, - bool has_retransmittable_data, - bool forced) { + HasRetransmittableData retransmittable, + Force forced) { if (!connected_) { - DLOG(INFO) - << "Dropping packet to be sent since connection is disconnected."; + DLOG(INFO) << ENDPOINT + << "Not sending packet as connection is disconnected."; delete packet; // Returning true because we deleted the packet and the caller shouldn't // delete it again. return true; } - bool is_retransmission = IsRetransmission(sequence_number); + Retransmission retransmission = IsRetransmission(sequence_number) ? + IS_RETRANSMISSION : NOT_RETRANSMISSION; // If we are not forced and we can't write, then simply return false; - if (!forced && !CanWrite(is_retransmission, has_retransmittable_data)) { + if (forced == NO_FORCE && !CanWrite(retransmission, retransmittable)) { return false; } scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(sequence_number, *packet)); - DLOG(INFO) << "Sending packet number " << sequence_number << " : " - << (packet->is_fec_packet() ? "FEC " : - (has_retransmittable_data ? "data bearing " : " ack only ")) + DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number + << " : " << (packet->is_fec_packet() ? "FEC " : + (retransmittable == HAS_RETRANSMITTABLE_DATA + ? "data bearing " : " ack only ")) << " Packet length:" << packet->length(); DCHECK(encrypted->length() <= kMaxPacketSize) << "Packet " << sequence_number << " will not be read; too large: " << packet->length() << " " << encrypted->length() << " " - << outgoing_ack_ << " forced: " << (forced ? "yes" : "no"); + << outgoing_ack_ << " forced: " << (forced == FORCE ? "yes" : "no"); int error; QuicTime now = clock_->Now(); @@ -964,7 +963,7 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, return helper_->IsWriteBlockedDataBuffered(); } time_of_last_sent_packet_ = now; - DVLOG(1) << "time of last sent packet: " << now.ToMicroseconds(); + DVLOG(1) << "time of last sent packet: " << now.ToDebuggingValue(); // TODO(wtc): Is it correct to continue if the write failed. // Set the retransmit alarm only when we have sent the packet to the client @@ -973,12 +972,12 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, MaybeSetupRetransmission(sequence_number); congestion_manager_.SentPacket(sequence_number, now, packet->length(), - is_retransmission); + retransmission); stats_.bytes_sent += encrypted->length(); ++stats_.packets_sent; - if (is_retransmission) { + if (retransmission == IS_RETRANSMISSION) { stats_.bytes_retransmitted += encrypted->length(); ++stats_.packets_retransmitted; } @@ -1004,19 +1003,19 @@ bool QuicConnection::OnSerializedPacket( return SendOrQueuePacket(serialized_packet.sequence_number, serialized_packet.packet, serialized_packet.entropy_hash, - serialized_packet.retransmittable_frames != NULL); + serialized_packet.retransmittable_frames != NULL ? + HAS_RETRANSMITTABLE_DATA : + NO_RETRANSMITTABLE_DATA); } bool QuicConnection::SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, QuicPacket* packet, QuicPacketEntropyHash entropy_hash, - bool has_retransmittable_data) { - entropy_manager_.RecordSentPacketEntropyHash(sequence_number, - entropy_hash); - if (!WritePacket(sequence_number, packet, has_retransmittable_data, - !kForce)) { + 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, - has_retransmittable_data)); + retransmittable)); return false; } return true; @@ -1085,7 +1084,7 @@ QuicTime QuicConnection::OnRetransmissionTimeout() { } retransmission_timeouts_.pop(); if (!MaybeRetransmitPacketForRTO(retransmission_info.sequence_number)) { - DLOG(INFO) << "MaybeRetransmitPacketForRTO failed: " + DLOG(INFO) << ENDPOINT << "MaybeRetransmitPacketForRTO failed: " << "adding an extra delay for " << retransmission_info.sequence_number; retransmission_info.scheduled_time = clock_->ApproximateNow().Add( @@ -1097,7 +1096,7 @@ QuicTime QuicConnection::OnRetransmissionTimeout() { handling_retransmission_timeout_ = false; if (retransmission_timeouts_.empty()) { - return QuicTime::FromMilliseconds(0); + return QuicTime::Zero(); } // We have packets remaining. Return the absolute RTO of the oldest packet @@ -1105,6 +1104,18 @@ QuicTime QuicConnection::OnRetransmissionTimeout() { return retransmission_timeouts_.top().scheduled_time; } +void QuicConnection::ChangeEncrypter(QuicEncrypter* encrypter) { + framer_.set_encrypter(encrypter); +} + +void QuicConnection::PushDecrypter(QuicDecrypter* decrypter) { + framer_.push_decrypter(decrypter); +} + +void QuicConnection::PopDecrypter() { + framer_.pop_decrypter(); +} + void QuicConnection::MaybeProcessRevivedPacket() { QuicFecGroup* group = GetFecGroup(); if (group == NULL || !group->CanRevive()) { @@ -1150,8 +1161,8 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) { void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, const string& details) { - DLOG(INFO) << "Force closing with error " << QuicUtils::ErrorToString(error) - << " (" << error << ")"; + DLOG(INFO) << ENDPOINT << "Force closing with error " + << QuicUtils::ErrorToString(error) << " (" << error << ")"; QuicConnectionCloseFrame frame; frame.error_code = error; frame.error_details = details; @@ -1168,8 +1179,9 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, WritePacket(serialized_packet.sequence_number, serialized_packet.packet, - serialized_packet.retransmittable_frames != NULL, - kForce); + serialized_packet.retransmittable_frames != NULL ? + HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA, + FORCE); } void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error, @@ -1186,7 +1198,8 @@ void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { void QuicConnection::SendGoAway(QuicErrorCode error, QuicStreamId last_good_stream_id, const string& reason) { - DLOG(INFO) << "Going away with error " << QuicUtils::ErrorToString(error) + DLOG(INFO) << ENDPOINT << "Going away with error " + << QuicUtils::ErrorToString(error) << " (" << error << ")"; packet_generator_.AddControlFrame( QuicFrame(new QuicGoAwayFrame(error, last_good_stream_id, reason))); @@ -1223,8 +1236,8 @@ bool QuicConnection::CheckForTimeout() { time_of_last_sent_packet_); QuicTime::Delta delta = now.Subtract(time_of_last_packet); - DVLOG(1) << "last packet " << time_of_last_packet.ToMicroseconds() - << " now:" << now.ToMicroseconds() + DVLOG(1) << "last packet " << time_of_last_packet.ToDebuggingValue() + << " now:" << now.ToDebuggingValue() << " delta:" << delta.ToMicroseconds(); if (delta >= timeout_) { SendConnectionClose(QUIC_CONNECTION_TIMED_OUT); diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index 3c3c00d..d097acb 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -66,7 +66,8 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called when the connection is closed either locally by the framer, or // remotely by the peer. - virtual void ConnectionClose(QuicErrorCode error, bool from_peer) = 0; + virtual void ConnectionClose(QuicErrorCode error, + bool from_peer) = 0; // Called when packets are acked by the peer. virtual void OnAck(const SequenceNumberSet& acked_packets) = 0; @@ -197,6 +198,11 @@ class NET_EXPORT_PRIVATE QuicConnection public QuicBlockedWriterInterface, public QuicPacketGenerator::DelegateInterface { public: + enum Force { + NO_FORCE, + FORCE + }; + // Constructs a new QuicConnection for the specified |guid| and |address|. // |helper| will be owned by this connection. QuicConnection(QuicGuid guid, @@ -218,7 +224,7 @@ class NET_EXPORT_PRIVATE QuicConnection bool fin); // Send a stream reset frame to the peer. virtual void SendRstStream(QuicStreamId id, - QuicErrorCode error); + QuicRstStreamErrorCode error); // Sends the connection close packet without affecting the state of the // connection. This should only be called if the session is actively being @@ -280,6 +286,9 @@ class NET_EXPORT_PRIVATE QuicConnection virtual void OnPacketComplete() OVERRIDE; // QuicPacketGenerator::DelegateInterface + virtual bool CanWrite( + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data) OVERRIDE; virtual QuicAckFrame* CreateAckFrame() OVERRIDE; virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE; virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE; @@ -336,6 +345,27 @@ 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 + // 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(); } + protected: // Deletes all missing packets before least unacked. The connection won't // process any packets with sequence number before |least_unacked| that it @@ -353,7 +383,7 @@ class NET_EXPORT_PRIVATE QuicConnection virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, QuicPacket* packet, QuicPacketEntropyHash entropy_hash, - bool has_retransmittable_data); + 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 @@ -361,12 +391,12 @@ class NET_EXPORT_PRIVATE QuicConnection // 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 true, then the packet will be sent immediately and the send + // |force| is FORCE, then the packet will be sent immediately and the send // scheduler will not be consulted. bool WritePacket(QuicPacketSequenceNumber sequence_number, QuicPacket* packet, - bool has_retransmittable_data, - bool force); + HasRetransmittableData retransmittable, + Force force); // Make sure an ack we got from our peer is sane. bool ValidateAckFrame(const QuicAckFrame& incoming_ack); @@ -390,15 +420,15 @@ class NET_EXPORT_PRIVATE QuicConnection struct QueuedPacket { QueuedPacket(QuicPacketSequenceNumber sequence_number, QuicPacket* packet, - bool has_retransmittable_data) + HasRetransmittableData retransmittable) : sequence_number(sequence_number), packet(packet), - has_retransmittable_data(has_retransmittable_data) { + retransmittable(retransmittable) { } QuicPacketSequenceNumber sequence_number; QuicPacket* packet; - bool has_retransmittable_data; + HasRetransmittableData retransmittable; }; struct RetransmissionInfo { @@ -444,10 +474,6 @@ class NET_EXPORT_PRIVATE QuicConnection // Sends a version negotiation packet to the peer. void SendVersionNegotiationPacket(); - // Checks if a packet can be written now, and sets the timer if necessary. - virtual bool CanWrite(bool is_retransmission, - bool has_retransmittable_data) OVERRIDE; - void MaybeSetupRetransmission(QuicPacketSequenceNumber sequence_number); bool IsRetransmission(QuicPacketSequenceNumber sequence_number); void RetransmitAllUnackedPackets(); diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index 08743d05..2b2c633 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -62,6 +62,7 @@ class QuicConnectionHelperTest : public ::testing::Test { framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false), net_log_(BoundNetLog()), frame_(1, false, 0, kData) { @@ -251,7 +252,7 @@ TEST_F(QuicConnectionHelperTest, SetAckAlarm) { EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()), runner_->GetPostedTasks()[1].delay); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); runner_->RunNextTask(); EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.ApproximateNow()); } @@ -295,7 +296,7 @@ TEST_F(QuicConnectionHelperTest, ResetAckAlarm) { // Verify that the ack alarm task has been re-posted. ASSERT_EQ(2u, runner_->GetPostedTasks().size()); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); runner_->RunNextTask(); EXPECT_EQ(QuicTime::Zero().Add(delta2), clock_.ApproximateNow()); } @@ -309,11 +310,11 @@ TEST_F(QuicConnectionHelperTest, TestRetransmission) { QuicTime::Delta::FromMilliseconds(500); QuicTime start = clock_.ApproximateNow(); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)); // Send a packet. connection_->SendStreamData(1, kData, 0, false); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, true)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, IS_RETRANSMISSION)); // Since no ack was received, the retransmission alarm will fire and // retransmit it. runner_->RunNextTask(); @@ -332,12 +333,13 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) { EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs), runner_->GetPostedTasks().front().delay); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); // After we run the next task, we should close the connection. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); runner_->RunNextTask(); - EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), + EXPECT_EQ(QuicTime::Zero().Add( + QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)), clock_.ApproximateNow()); EXPECT_FALSE(connection_->connected()); EXPECT_TRUE(AtEof()); @@ -372,12 +374,12 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { Initialize(); EXPECT_TRUE(connection_->connected()); - EXPECT_EQ(0u, clock_.ApproximateNow().ToMicroseconds()); + EXPECT_EQ(0u, clock_.NowAsDeltaSinceUnixEpoch().ToMicroseconds()); // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000)); - EXPECT_EQ(5000u, clock_.ApproximateNow().ToMicroseconds()); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); + EXPECT_EQ(5000u, clock_.NowAsDeltaSinceUnixEpoch().ToMicroseconds()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); // Send an ack so we don't set the retransmission alarm. connection_->SendAck(); @@ -386,15 +388,17 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { // network event at t=5000. The alarm will reregister. runner_->RunNextTask(); - EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), + EXPECT_EQ(QuicTime::Zero().Add( + QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)), clock_.ApproximateNow()); EXPECT_TRUE(connection_->connected()); // This time, we should time out. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION)); runner_->RunNextTask(); - EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.ApproximateNow().ToMicroseconds()); + EXPECT_EQ(kDefaultTimeoutUs + 5000, + clock_.NowAsDeltaSinceUnixEpoch().ToMicroseconds()); EXPECT_FALSE(connection_->connected()); EXPECT_TRUE(AtEof()); } @@ -404,18 +408,20 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) { Initialize(); // Test that if we send a packet with a delay, it ends up queued. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false, _)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(1))); + EXPECT_CALL( + *send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); QuicPacket* packet = ConstructRawDataPacket(1); - connection_->SendOrQueuePacket(1, packet, 0, kHasData); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); + connection_->SendOrQueuePacket(1, packet, 0, HAS_RETRANSMITTABLE_DATA); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); EXPECT_EQ(1u, connection_->NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false, _)).WillRepeatedly( - testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL( + *send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true)); runner_->RunNextTask(); EXPECT_EQ(0u, connection_->NumQueuedPackets()); diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index 3679d1ce3..5b13c92 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc @@ -89,7 +89,7 @@ Value* NetLogQuicCongestionFeedbackFrameCallback( frame->inter_arrival.received_packet_times.begin(); it != frame->inter_arrival.received_packet_times.end(); ++it) { std::string value = base::Uint64ToString(it->first) + "@" + - base::Uint64ToString(it->second.ToMilliseconds()); + base::Uint64ToString(it->second.ToDebuggingValue()); received->Append(new base::StringValue(value)); } break; diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 72322ff..f2811f3 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -44,9 +44,6 @@ const char data1[] = "foo"; const char data2[] = "bar"; const bool kFin = true; -const bool kForce = true; -const bool kIsRetransmission = true; -const bool kHasData = true; const bool kEntropyFlag = true; const bool kFecEntropyFlag = true; const QuicPacketEntropyHash kTestEntropyHash = 76; @@ -81,7 +78,8 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { : clock_(clock), random_generator_(random_generator), retransmission_alarm_(QuicTime::Zero()), - send_alarm_(QuicTime::FromMilliseconds(-1)), + send_alarm_(QuicTime::Zero().Subtract( + QuicTime::Delta::FromMilliseconds(1))), timeout_alarm_(QuicTime::Zero()), blocked_(false), is_server_(true) { @@ -103,6 +101,7 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), is_server_); FramerVisitorCapturingFrames visitor; framer.set_visitor(&visitor); @@ -156,7 +155,8 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { } virtual void UnregisterSendAlarmIfRegistered() OVERRIDE { - send_alarm_ = QuicTime::FromMilliseconds(-1); + send_alarm_ = + QuicTime::Zero().Subtract(QuicTime::Delta::FromMilliseconds(1)); } virtual void SetAckAlarm(QuicTime::Delta delay) OVERRIDE {} @@ -248,6 +248,8 @@ class TestConnection : public QuicConnection { void set_is_server(bool is_server) { helper_->set_is_server(!is_server); + QuicPacketCreatorPeer::SetIsServer( + QuicConnectionPeer::GetPacketCreator(this), is_server); QuicConnectionPeer::SetIsServer(this, is_server); } @@ -267,6 +269,7 @@ class QuicConnectionTest : public ::testing::Test { framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false), creator_(guid_, &framer_, QuicRandom::GetInstance(), false), send_algorithm_(new StrictMock<MockSendAlgorithm>), @@ -832,7 +835,7 @@ TEST_F(QuicConnectionTest, FramePacking) { // Unblock the connection. helper_->UnregisterSendAlarmIfRegistered(); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission)) + SentPacket(_, _, _, NOT_RETRANSMISSION)) .Times(1); connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); @@ -866,7 +869,7 @@ TEST_F(QuicConnectionTest, FramePackingFEC) { // Unblock the connection. helper_->UnregisterSendAlarmIfRegistered(); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission)).Times(2); + SentPacket(_, _, _, NOT_RETRANSMISSION)).Times(2); connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); @@ -886,7 +889,7 @@ TEST_F(QuicConnectionTest, OnCanWrite) { Return(false))); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillRepeatedly( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); // Unblock the connection. @@ -941,7 +944,7 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) { // The third nack should trigger a retransimission. EXPECT_CALL(*send_algorithm_, SentPacket(_, _, second_packet_size - kQuicVersionSize, - kIsRetransmission)).Times(1); + IS_RETRANSMISSION)).Times(1); ProcessAckPacket(&nack_two); } @@ -949,7 +952,7 @@ TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) { EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); QuicPacketSequenceNumber largest_observed; QuicByteCount packet_size; - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission)) + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION)) .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size))); EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); connection_.SendStreamData(1, "foo", 0, !kFin); @@ -960,11 +963,11 @@ TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) { ProcessAckPacket(&frame); // Second udp packet will force an ack frame. EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission)); + SentPacket(_, _, _, NOT_RETRANSMISSION)); ProcessAckPacket(&frame); // Third nack should retransmit the largest observed packet. EXPECT_CALL(*send_algorithm_, SentPacket(_, _, packet_size - kQuicVersionSize, - kIsRetransmission)); + IS_RETRANSMISSION)); ProcessAckPacket(&frame); } @@ -1176,7 +1179,7 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); QuicPacketSequenceNumber original_sequence_number; EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission)) + SentPacket(_, _, _, NOT_RETRANSMISSION)) .WillOnce(SaveArg<1>(&original_sequence_number)); connection_.SendStreamData(1, "foo", 0, !kFin); EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( @@ -1187,7 +1190,7 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); QuicPacketSequenceNumber rto_sequence_number; EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, kIsRetransmission)) + SentPacket(_, _, _, IS_RETRANSMISSION)) .WillOnce(SaveArg<1>(&rto_sequence_number)); connection_.OnRetransmissionTimeout(); EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( @@ -1200,9 +1203,9 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { QuicPacketSequenceNumber nack_sequence_number; // Ack packets might generate some other packets, which are not // retransmissions. (More ack packets). - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission)) + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION)) .Times(AnyNumber()); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, kIsRetransmission)) + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, IS_RETRANSMISSION)) .WillOnce(SaveArg<1>(&nack_sequence_number)); QuicAckFrame ack(rto_sequence_number, QuicTime::Zero(), 0); // Ack the retransmitted packet. @@ -1348,10 +1351,11 @@ TEST_F(QuicConnectionTest, SendScheduler) { // Test that if we send a packet without delay, it is not queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } @@ -1359,10 +1363,11 @@ TEST_F(QuicConnectionTest, SendSchedulerDelay) { // Test that if we send a packet with a delay, it ends up queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } @@ -1370,9 +1375,10 @@ TEST_F(QuicConnectionTest, SendSchedulerForce) { // Test that if we force send a packet, it is not queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, kIsRetransmission, _)).Times(0); + TimeUntilSend(_, IS_RETRANSMISSION, _)).Times(0); EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); // XXX: fixme. was: connection_.SendOrQueuePacket(1, packet, kForce); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } @@ -1381,10 +1387,11 @@ TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); helper_->set_blocked(true); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } @@ -1392,15 +1399,16 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { // Test that if we send a packet with a delay, it ends up queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillRepeatedly( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); helper_->UnregisterSendAlarmIfRegistered(); @@ -1411,18 +1419,18 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { } TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission, _)) + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _)) .WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); EXPECT_CALL(*send_algorithm_, - SentPacket(_, 1, _, !kIsRetransmission)); + SentPacket(_, 1, _, NOT_RETRANSMISSION)); connection_.SendStreamData(1, "foo", 0, !kFin); EXPECT_EQ(0u, connection_.NumQueuedPackets()); // Advance the time for retransmission of lost packet. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501)); // Test that if we send a retransmit with a delay, it ends up queued. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, IS_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.OnRetransmissionTimeout(); EXPECT_EQ(1u, connection_.NumQueuedPackets()); @@ -1430,12 +1438,12 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, IS_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::Zero())); // Ensure the scheduler is notified this is a retransmit. EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, kIsRetransmission)); + SentPacket(_, _, _, IS_RETRANSMISSION)); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); helper_->UnregisterSendAlarmIfRegistered(); EXPECT_CALL(visitor_, OnCanWrite()); @@ -1446,30 +1454,33 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); + connection_.SendOrQueuePacket( + 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, true); + connection_.SendOrQueuePacket( + 2, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(2u, connection_.NumQueuedPackets()); } TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Now send non-retransmitting information, that we're not going to // retransmit 3. The far end should stop waiting for it. QuicAckFrame frame(0, QuicTime::Zero(), 1); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillRepeatedly( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); @@ -1484,16 +1495,17 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, kHasData); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Now send non-retransmitting information, that we're not going to // retransmit 3. The far end should stop waiting for it. QuicAckFrame frame(0, QuicTime::Zero(), 1); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); ProcessAckPacket(&frame); @@ -1503,9 +1515,10 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, kHasData); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // OnCanWrite should not send the packet (because of the delay) @@ -1522,7 +1535,7 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { // Queue the first packet. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + TimeUntilSend(_, NOT_RETRANSMISSION, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); EXPECT_EQ(0u, connection_.SendStreamData( 1, "EnoughDataToQueue", 0, !kFin).bytes_consumed); @@ -1556,7 +1569,8 @@ TEST_F(QuicConnectionTest, SendWhenDisconnected) { EXPECT_FALSE(connection_.connected()); QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); - connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, kHasData); + connection_.SendOrQueuePacket( + 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); } TEST_F(QuicConnectionTest, PublicReset) { @@ -1674,7 +1688,8 @@ TEST_F(QuicConnectionTest, CheckSentEntropyHash) { packet_entropy_hash = 1 << (i % 8); } QuicPacket* packet = ConstructDataPacket(i, 0, entropy_flag); - connection_.SendOrQueuePacket(i, packet, packet_entropy_hash, true); + connection_.SendOrQueuePacket( + i, packet, packet_entropy_hash, HAS_RETRANSMITTABLE_DATA); if (is_missing) { missing_packets.insert(i); @@ -1723,18 +1738,18 @@ TEST_F(QuicConnectionTest, SendVersionNegotiationPacket) { TEST_F(QuicConnectionTest, CheckSendStats) { EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(3); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission)); + SentPacket(_, _, _, NOT_RETRANSMISSION)); connection_.SendStreamData(1u, "first", 0, !kFin); size_t first_packet_size = last_sent_packet_size(); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission)).Times(2); + SentPacket(_, _, _, NOT_RETRANSMISSION)).Times(2); connection_.SendStreamData(1u, "second", 0, !kFin); size_t second_packet_size = last_sent_packet_size(); // 2 retransmissions due to rto, 1 due to explicit nack. EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, kIsRetransmission)).Times(3); + SentPacket(_, _, _, IS_RETRANSMISSION)).Times(3); // Retransmit due to RTO. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index badddce..63a4f44 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -15,6 +15,7 @@ QuicCryptoClientStream::QuicCryptoClientStream(QuicSession* session, const string& server_hostname) : QuicCryptoStream(session), next_state_(STATE_IDLE), + decrypter_pushed_(false), server_hostname_(server_hostname) { config_.SetDefaults(); crypto_config_.SetDefaults(); @@ -94,6 +95,10 @@ void QuicCryptoClientStream::DoHandshakeLoop( next_state_ = STATE_RECV_SHLO; 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; return; } case STATE_RECV_REJ: @@ -111,6 +116,11 @@ void QuicCryptoClientStream::DoHandshakeLoop( CloseConnectionWithDetails(error, error_details); return; } + // Clear any new server write key that we may have set before. + if (decrypter_pushed_) { + session()->connection()->PopDecrypter(); + decrypter_pushed_ = false; + } next_state_ = STATE_SEND_CHLO; break; case STATE_RECV_SHLO: @@ -122,6 +132,14 @@ void QuicCryptoClientStream::DoHandshakeLoop( "Expected SHLO"); 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); return; case STATE_IDLE: diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 463994e..fb67ee4 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -57,6 +57,8 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { QuicNegotiatedParameters negotiated_params_; QuicCryptoNegotiatedParameters crypto_negotiated_params_; + 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 9c33d5b..251a8ef 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -5,6 +5,7 @@ #include "net/quic/quic_crypto_client_stream.h" #include "base/memory/scoped_ptr.h" +#include "net/quic/crypto/aes_128_gcm_encrypter.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/test_tools/crypto_test_utils.h" @@ -87,15 +88,30 @@ class QuicCryptoClientStreamTest : public ::testing::Test { }; TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + EXPECT_FALSE(stream_.handshake_complete()); } TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); EXPECT_TRUE(stream_.handshake_complete()); } TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); EXPECT_CALL(*connection_, SendConnectionClose( @@ -106,6 +122,11 @@ TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { } TEST_F(QuicCryptoClientStreamTest, BadMessageType) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + EXPECT_TRUE(stream_.CryptoConnect()); message_.set_tag(kCHLO); @@ -117,6 +138,11 @@ TEST_F(QuicCryptoClientStreamTest, BadMessageType) { } TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); const QuicNegotiatedParameters& params(stream_.negotiated_params()); diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 6fc508d..d1c21ff 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -12,7 +12,9 @@ namespace net { QuicCryptoServerStream::QuicCryptoServerStream(QuicSession* session) - : QuicCryptoStream(session) { + : QuicCryptoStream(session), + // TODO(agl): use real secret. + crypto_config_("secret") { config_.SetDefaults(); // Use hardcoded crypto parameters for now. CryptoHandshakeMessage extra_tags; @@ -56,8 +58,11 @@ void QuicCryptoServerStream::OnHandshakeMessage( string error_details; CryptoHandshakeMessage reply; crypto_config_.ProcessClientHello( - message, session()->connection()->guid(), &reply, - &crypto_negotiated_params_, &error_details); + message, session()->connection()->guid(), + session()->connection()->peer_address(), + session()->connection()->clock()->NowAsDeltaSinceUnixEpoch(), + session()->connection()->random_generator(), + &reply, &crypto_negotiated_params_, &error_details); if (reply.tag() == kSHLO) { // If we are returning a SHLO then we accepted the handshake. @@ -69,6 +74,17 @@ void QuicCryptoServerStream::OnHandshakeMessage( return; } + // Receiving a full CHLO implies the client is prepared to decrypt with + // the new server write key. We can start to encrypt with the new server + // write key. + // + // NOTE: the SHLO will be encrypted with the new server write key. + session()->connection()->ChangeEncrypter( + 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( + crypto_negotiated_params_.decrypter.release()); SetHandshakeComplete(QUIC_NO_ERROR); } diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index 1d2b11d..173122d8 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -8,6 +8,7 @@ #include <vector> #include "base/memory/scoped_ptr.h" +#include "net/quic/crypto/aes_128_gcm_encrypter.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" @@ -66,7 +67,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test { public: QuicCryptoServerStreamTest() : guid_(1), - addr_(), + addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? + ip_ : IPAddressNumber(), 1), connection_(new PacketSavingConnection(guid_, addr_, true)), session_(connection_, true), stream_(&session_) { @@ -82,6 +84,7 @@ class QuicCryptoServerStreamTest : public ::testing::Test { } protected: + IPAddressNumber ip_; QuicGuid guid_; IPEndPoint addr_; PacketSavingConnection* connection_; @@ -92,15 +95,30 @@ class QuicCryptoServerStreamTest : public ::testing::Test { }; TEST_F(QuicCryptoServerStreamTest, NotInitiallyConected) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + EXPECT_FALSE(stream_.handshake_complete()); } TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); EXPECT_TRUE(stream_.handshake_complete()); } TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + CompleteCryptoHandshake(); EXPECT_CALL(*connection_, SendConnectionClose( QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); @@ -110,6 +128,11 @@ TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) { } TEST_F(QuicCryptoServerStreamTest, BadMessageType) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + message_.set_tag(kSHLO); ConstructHandshakeMessage(); EXPECT_CALL(*connection_, SendConnectionClose( diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 0b8e27b..c97b857 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -46,6 +46,7 @@ QuicPacketSequenceNumber ClosestTo(QuicPacketSequenceNumber target, QuicFramer::QuicFramer(QuicVersionTag version, QuicDecrypter* decrypter, QuicEncrypter* encrypter, + QuicTime creation_time, bool is_server) : visitor_(NULL), fec_builder_(NULL), @@ -54,7 +55,8 @@ QuicFramer::QuicFramer(QuicVersionTag version, quic_version_(version), decrypter_(decrypter), encrypter_(encrypter), - is_server_(is_server) { + is_server_(is_server), + creation_time_(creation_time) { DCHECK(IsSupportedVersion(version)); } @@ -814,10 +816,11 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( set_detailed_error("Unable to read time received."); return false; } + QuicTime time_received = creation_time_.Add( + QuicTime::Delta::FromMicroseconds(time_received_us)); inter_arrival->received_packet_times.insert( - make_pair(smallest_received, - QuicTime::FromMicroseconds(time_received_us))); + make_pair(smallest_received, time_received)); for (int i = 0; i < num_received_packets - 1; ++i) { uint16 sequence_delta; @@ -835,8 +838,8 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( } QuicPacketSequenceNumber packet = smallest_received + sequence_delta; inter_arrival->received_packet_times.insert( - make_pair(packet, QuicTime::FromMicroseconds(time_received_us + - time_delta_us))); + make_pair(packet, time_received.Add( + QuicTime::Delta::FromMicroseconds(time_delta_us)))); } } break; @@ -889,7 +892,14 @@ bool QuicFramer::ProcessRstStreamFrame() { set_detailed_error("Unable to read rst stream error code."); return false; } - frame.error_code = static_cast<QuicErrorCode>(error_code); + + if (error_code >= QUIC_STREAM_LAST_ERROR || + error_code < QUIC_STREAM_NO_ERROR) { + set_detailed_error("Invalid rst stream error code."); + return false; + } + + frame.error_code = static_cast<QuicRstStreamErrorCode>(error_code); StringPiece error_details; if (!reader_->ReadStringPiece16(&error_details)) { @@ -910,6 +920,13 @@ bool QuicFramer::ProcessConnectionCloseFrame() { set_detailed_error("Unable to read connection close error code."); return false; } + + if (error_code >= QUIC_LAST_ERROR || + error_code < QUIC_NO_ERROR) { + set_detailed_error("Invalid error code."); + return false; + } + frame.error_code = static_cast<QuicErrorCode>(error_code); StringPiece error_details; @@ -938,12 +955,18 @@ bool QuicFramer::ProcessGoAwayFrame() { } frame.error_code = static_cast<QuicErrorCode>(error_code); + if (error_code >= QUIC_LAST_ERROR || + error_code < QUIC_NO_ERROR) { + set_detailed_error("Invalid error code."); + return false; + } + uint32 stream_id; if (!reader_->ReadUInt32(&stream_id)) { set_detailed_error("Unable to read last good stream id."); return false; } - frame.last_good_stream_id = static_cast<QuicErrorCode>(stream_id); + frame.last_good_stream_id = static_cast<QuicStreamId>(stream_id); StringPiece reason_phrase; if (!reader_->ReadStringPiece16(&reason_phrase)) { @@ -956,6 +979,7 @@ bool QuicFramer::ProcessGoAwayFrame() { return true; } +// static StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( const QuicEncryptedPacket& encrypted, bool includes_version) { return StringPiece(encrypted.data() + kStartOfHashData, @@ -963,12 +987,27 @@ StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( kStartOfHashData); } +void QuicFramer::push_decrypter(QuicDecrypter* decrypter) { + DCHECK(backup_decrypter_.get() == NULL); + backup_decrypter_.reset(decrypter_.release()); + decrypter_.reset(decrypter); +} + +void QuicFramer::pop_decrypter() { + DCHECK(backup_decrypter_.get() != NULL); + decrypter_.reset(backup_decrypter_.release()); +} + +void QuicFramer::set_encrypter(QuicEncrypter* encrypter) { + encrypter_.reset(encrypter); +} + QuicEncryptedPacket* QuicFramer::EncryptPacket( QuicPacketSequenceNumber packet_sequence_number, const QuicPacket& packet) { - scoped_ptr<QuicData> out(encrypter_->Encrypt(packet_sequence_number, - packet.AssociatedData(), - packet.Plaintext())); + scoped_ptr<QuicData> out(encrypter_->EncryptPacket(packet_sequence_number, + packet.AssociatedData(), + packet.Plaintext())); if (out.get() == NULL) { RaiseError(QUIC_ENCRYPTION_FAILURE); return NULL; @@ -994,10 +1033,19 @@ bool QuicFramer::DecryptPayload(QuicPacketSequenceNumber sequence_number, return false; } DCHECK(decrypter_.get() != NULL); - decrypted_.reset(decrypter_->Decrypt( + decrypted_.reset(decrypter_->DecryptPacket( sequence_number, GetAssociatedDataFromEncryptedPacket(packet, version_flag), encrypted)); + if (decrypted_.get() == NULL && backup_decrypter_.get() != NULL) { + decrypted_.reset(backup_decrypter_->DecryptPacket( + sequence_number, + GetAssociatedDataFromEncryptedPacket(packet, version_flag), + encrypted)); + } + // 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; } @@ -1221,9 +1269,8 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( } QuicTime lowest_time = it->second; - // TODO(ianswett): Use time deltas from the connection's first received - // packet. - if (!writer->WriteUInt64(lowest_time.ToMicroseconds())) { + if (!writer->WriteUInt64( + lowest_time.Subtract(creation_time_).ToMicroseconds())) { return false; } diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 7a00aaa..80d7854 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -151,6 +151,7 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicFramer(QuicVersionTag quic_version, QuicDecrypter* decrypter, QuicEncrypter* encrypter, + QuicTime creation_time, bool is_server); virtual ~QuicFramer(); @@ -269,6 +270,13 @@ 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); + // Returns a new encrypted packet, owned by the caller. QuicEncryptedPacket* EncryptPacket(QuicPacketSequenceNumber sequence_number, const QuicPacket& packet); @@ -369,13 +377,18 @@ class NET_EXPORT_PRIVATE QuicFramer { scoped_ptr<QuicData> decrypted_; // Version of the protocol being used. QuicVersionTag quic_version_; - // Decrypter used to decrypt packets during parsing. + // 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_; // Tracks if the framer is being used by the entity that received the // connection or the entity that initiated it. bool is_server_; + // The time this frames was created. Time written to the wire will be + // written as a delta from this value. + QuicTime creation_time_; DISALLOW_COPY_AND_ASSIGN(QuicFramer); }; diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 12d6b2f..8856897 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -73,9 +73,16 @@ class TestEncrypter : public QuicEncrypter { virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE { return true; } - virtual QuicData* Encrypt(QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece plaintext) OVERRIDE { + virtual bool Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { + CHECK(false) << "Not implemented"; + return false; + } + virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) { sequence_number_ = sequence_number; associated_data_ = associated_data.as_string(); plaintext_ = plaintext.as_string(); @@ -113,9 +120,17 @@ class TestDecrypter : public QuicDecrypter { virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE { return true; } - virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece ciphertext) OVERRIDE { + virtual bool Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + unsigned char* output, + size_t* output_length) { + CHECK(false) << "Not implemented"; + return false; + } + virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) { sequence_number_ = sequence_number; associated_data_ = associated_data.as_string(); ciphertext_ = ciphertext.as_string(); @@ -255,7 +270,8 @@ class QuicFramerTest : public ::testing::Test { QuicFramerTest() : encrypter_(new test::TestEncrypter()), decrypter_(new test::TestDecrypter()), - framer_(kQuicVersion1, decrypter_, encrypter_, true) { + start_(QuicTime::Zero().Add(QuicTime::Delta::FromMicroseconds(0x10))), + framer_(kQuicVersion1, decrypter_, encrypter_, start_, true) { framer_.set_visitor(&visitor_); framer_.set_entropy_calculator(&entropy_calculator_); } @@ -347,6 +363,7 @@ class QuicFramerTest : public ::testing::Test { test::TestEncrypter* encrypter_; test::TestDecrypter* decrypter_; + QuicTime start_; QuicFramer framer_; test::TestQuicVisitor visitor_; test::TestEntropyCalculator entropy_calculator_; @@ -1183,10 +1200,10 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { 0x02, 0x03, // num received packets 0x03, - // smallest ack sequence number + // lowest sequence number 0xBA, 0x9A, 0x78, 0x56, 0x34, 0x12, - // ack time + // receive time 0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0x07, // sequence delta @@ -1217,16 +1234,16 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { TimeMap::const_iterator iter = frame.inter_arrival.received_packet_times.begin(); EXPECT_EQ(GG_UINT64_C(0x0123456789ABA), iter->first); - EXPECT_EQ(QuicTime::FromMicroseconds(GG_UINT64_C(0x07E1D2C3B4A59687)), - iter->second); + EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59687), + iter->second.Subtract(start_).ToMicroseconds()); ++iter; EXPECT_EQ(GG_UINT64_C(0x0123456789ABB), iter->first); - EXPECT_EQ(QuicTime::FromMicroseconds(GG_UINT64_C(0x07E1D2C3B4A59688)), - iter->second); + EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59688), + iter->second.Subtract(start_).ToMicroseconds()); ++iter; EXPECT_EQ(GG_UINT64_C(0x0123456789ABD), iter->first); - EXPECT_EQ(QuicTime::FromMicroseconds(GG_UINT64_C(0x07E1D2C3B4A59689)), - iter->second); + EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59689), + iter->second.Subtract(start_).ToMicroseconds()); // Now test framing boundaries for (size_t i = 0; i < 31; ++i) { @@ -1358,7 +1375,7 @@ TEST_F(QuicFramerTest, RstStreamFrame) { // stream id 0x04, 0x03, 0x02, 0x01, // error code - 0x08, 0x07, 0x06, 0x05, + 0x01, 0x00, 0x00, 0x00, // error details length 0x0d, 0x00, @@ -1377,7 +1394,7 @@ TEST_F(QuicFramerTest, RstStreamFrame) { EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id); - EXPECT_EQ(0x05060708, visitor_.rst_stream_frame_.error_code); + EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code); EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details); // Now test framing boundaries @@ -1414,7 +1431,7 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { // frame type (connection close frame) 0x05, // error code - 0x08, 0x07, 0x06, 0x05, + 0x11, 0x00, 0x00, 0x00, // error details length 0x0d, 0x00, @@ -1451,7 +1468,7 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { EXPECT_EQ(0u, visitor_.stream_frames_.size()); - EXPECT_EQ(0x05060708, visitor_.connection_close_frame_.error_code); + EXPECT_EQ(0x11, visitor_.connection_close_frame_.error_code); EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details); ASSERT_EQ(1u, visitor_.ack_frames_.size()); @@ -1498,7 +1515,7 @@ TEST_F(QuicFramerTest, GoAwayFrame) { // frame type (go away frame) 0x06, // error code - 0x08, 0x07, 0x06, 0x05, + 0x09, 0x00, 0x00, 0x00, // stream id 0x04, 0x03, 0x02, 0x01, // error details length @@ -1519,7 +1536,7 @@ TEST_F(QuicFramerTest, GoAwayFrame) { EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.goaway_frame_.last_good_stream_id); - EXPECT_EQ(0x05060708, visitor_.goaway_frame_.error_code); + EXPECT_EQ(0x9, visitor_.goaway_frame_.error_code); EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase); const size_t reason_size = arraysize("because I can") - 1; @@ -1987,13 +2004,16 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { frame.inter_arrival.accumulated_number_of_lost_packets = 0x0302; frame.inter_arrival.received_packet_times.insert( make_pair(GG_UINT64_C(0x0123456789ABA), - QuicTime::FromMicroseconds(GG_UINT64_C(0x07E1D2C3B4A59687)))); + start_.Add(QuicTime::Delta::FromMicroseconds( + GG_UINT64_C(0x07E1D2C3B4A59687))))); frame.inter_arrival.received_packet_times.insert( make_pair(GG_UINT64_C(0x0123456789ABB), - QuicTime::FromMicroseconds(GG_UINT64_C(0x07E1D2C3B4A59688)))); + start_.Add(QuicTime::Delta::FromMicroseconds( + GG_UINT64_C(0x07E1D2C3B4A59688))))); frame.inter_arrival.received_packet_times.insert( make_pair(GG_UINT64_C(0x0123456789ABD), - QuicTime::FromMicroseconds(GG_UINT64_C(0x07E1D2C3B4A59689)))); + start_.Add(QuicTime::Delta::FromMicroseconds( + GG_UINT64_C(0x07E1D2C3B4A59689))))); QuicFrames frames; frames.push_back(QuicFrame(&frame)); @@ -2019,10 +2039,10 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { 0x02, 0x03, // num received packets 0x03, - // smallest ack sequence number + // lowest sequence number 0xBA, 0x9A, 0x78, 0x56, 0x34, 0x12, - // ack time + // receive time 0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0x07, // sequence delta @@ -2130,7 +2150,7 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) { QuicRstStreamFrame rst_frame; rst_frame.stream_id = 0x01020304; - rst_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708); rst_frame.error_details = "because I can"; unsigned char packet[] = { @@ -2488,7 +2508,7 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) { QuicConnectionCloseFrame close_frame; QuicAckFrame* ack_frame = &close_frame.ack_frame; - close_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + close_frame.error_code = static_cast<QuicErrorCode>(0x05); close_frame.error_details = "because I can"; ack_frame->received_info.largest_observed = 201; ack_frame->sent_info.least_unacked = 0; @@ -2543,7 +2563,7 @@ TEST_F(QuicFramerTest, CleanTruncation) { QuicConnectionCloseFrame close_frame; QuicAckFrame* ack_frame = &close_frame.ack_frame; - close_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + close_frame.error_code = static_cast<QuicErrorCode>(0x05); close_frame.error_details = "because I can"; ack_frame->received_info.largest_observed = 201; ack_frame->sent_info.least_unacked = 0; diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc index 3020610..8aa3723 100644 --- a/net/quic/quic_http_stream.cc +++ b/net/quic/quic_http_stream.cc @@ -181,7 +181,7 @@ void QuicHttpStream::Close(bool not_reusable) { // Note: the not_reusable flag has no meaning for SPDY streams. if (stream_) { stream_->SetDelegate(NULL); - stream_->Close(QUIC_NO_ERROR); + stream_->Close(QUIC_STREAM_NO_ERROR); stream_ = NULL; } } diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index 7f4fef7..34165d6 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -121,6 +121,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false), creator_(guid_, &framer_, &random_, false) { IPAddressNumber ip; @@ -249,7 +250,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { QuicStreamId stream_id) { InitializeHeader(sequence_number, false); - QuicRstStreamFrame rst(stream_id, QUIC_NO_ERROR); + QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); return ConstructPacket(header_, QuicFrame(&rst)); } diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index e18f357..82b1075 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc @@ -96,7 +96,7 @@ class QuicNetworkTransactionTest : public PlatformTest { header.fec_entropy_flag = false; header.fec_group = 0; - QuicRstStreamFrame rst(stream_id, QUIC_NO_ERROR); + QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); return scoped_ptr<QuicEncryptedPacket>( ConstructPacket(header, QuicFrame(&rst))); } @@ -124,6 +124,7 @@ class QuicNetworkTransactionTest : public PlatformTest { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false); QuicFrames frames; frames.push_back(QuicFrame(&ack)); @@ -180,6 +181,7 @@ class QuicNetworkTransactionTest : public PlatformTest { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false); QuicFrames frames; frames.push_back(frame); diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index 05a6893..5339a7f 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -178,7 +178,7 @@ SerializedPacket QuicPacketCreator::SerializeConnectionClose( QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket( const QuicVersionTagList& supported_versions) { - DCHECK(!is_server_); + DCHECK(is_server_); QuicPacketPublicHeader header; header.guid = guid_; header.reset_flag = false; diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index 423028a..5e7ed61 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -33,10 +33,12 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> { : server_framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), true), client_framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false), id_(1), sequence_number_(0), @@ -183,6 +185,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { } TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { + QuicPacketCreatorPeer::SetIsServer(&creator_, true); QuicVersionTagList versions; versions.push_back(kQuicVersion1); scoped_ptr<QuicEncryptedPacket> encrypted( diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index 87464f7..d0e491a 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -72,9 +72,8 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, size_t total_bytes_consumed = 0; bool fin_consumed = false; - bool has_retransmittable_data = true; - while (delegate_->CanWrite(false, has_retransmittable_data)) { + while (delegate_->CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA)) { // TODO(rch) figure out FEC. // packet_creator_.MaybeStartFEC(); QuicFrame frame; @@ -114,8 +113,9 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, } void QuicPacketGenerator::SendQueuedData() { - while (HasPendingData() && - delegate_->CanWrite(false, packet_creator_->HasPendingFrames())) { + while (HasPendingData() && delegate_->CanWrite(NOT_RETRANSMISSION, + packet_creator_->HasPendingFrames() ? + HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA)) { if (!AddNextPendingFrame()) { // Packet was full, so serialize and send it. SerializeAndSendPacket(); diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h index 73e310f..dad1924 100644 --- a/net/quic/quic_packet_generator.h +++ b/net/quic/quic_packet_generator.h @@ -62,8 +62,8 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { class NET_EXPORT_PRIVATE DelegateInterface { public: virtual ~DelegateInterface() {} - virtual bool CanWrite(bool is_retransmission, - bool has_retransmittable_data) = 0; + virtual bool CanWrite(Retransmission retransmission, + HasRetransmittableData retransmittable) = 0; virtual QuicAckFrame* CreateAckFrame() = 0; virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0; // Takes ownership of |packet.packet| and |packet.retransmittable_frames|. diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc index 5ed1df5..c7e2918 100644 --- a/net/quic/quic_packet_generator_test.cc +++ b/net/quic/quic_packet_generator_test.cc @@ -31,15 +31,16 @@ class MockDelegate : public QuicPacketGenerator::DelegateInterface { MockDelegate() {} virtual ~MockDelegate() {} - MOCK_METHOD2(CanWrite, bool(bool is_retransmission, - bool has_retransmittable_data)); + MOCK_METHOD2(CanWrite, bool(Retransmission retransmission, + HasRetransmittableData retransmittable)); MOCK_METHOD0(CreateAckFrame, QuicAckFrame*()); MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*()); MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet)); void SetCanWrite(bool can_write) { - EXPECT_CALL(*this, CanWrite(false, _)).WillRepeatedly(Return(can_write)); + EXPECT_CALL(*this, CanWrite(NOT_RETRANSMISSION, _)) + .WillRepeatedly(Return(can_write)); } private: @@ -78,6 +79,7 @@ class QuicPacketGeneratorTest : public ::testing::Test { : framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false), creator_(42, &framer_, &random_, false), generator_(&delegate_, &creator_), @@ -114,7 +116,7 @@ class QuicPacketGeneratorTest : public ::testing::Test { } QuicRstStreamFrame* CreateRstStreamFrame() { - return new QuicRstStreamFrame(1, QUIC_NO_ERROR); + return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR); } QuicGoAwayFrame* CreateGoAwayFrame() { diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index 6fca452..4faec5e 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -15,8 +15,8 @@ namespace net { size_t GetPacketHeaderSize(bool include_version) { return kQuicGuidSize + kPublicFlagsSize + - (include_version ? kQuicVersionSize : 0) + kPrivateFlagsSize + - kSequenceNumberSize + kFecGroupSize; + (include_version ? kQuicVersionSize : 0) + kSequenceNumberSize + + kPrivateFlagsSize + kFecGroupSize; } size_t GetPublicResetPacketSize() { @@ -29,11 +29,15 @@ size_t GetStartOfFecProtectedData(bool include_version) { } size_t GetStartOfEncryptedData(bool include_version) { - return GetPacketHeaderSize(include_version) - kPrivateFlagsSize - + return GetPacketHeaderSize(include_version) - kPrivateFlagsSize - kFecGroupSize; } -QuicPacketPublicHeader::QuicPacketPublicHeader() {} +QuicPacketPublicHeader::QuicPacketPublicHeader() + : guid(0), + reset_flag(false), + version_flag(false) { +} QuicPacketPublicHeader::QuicPacketPublicHeader( const QuicPacketPublicHeader& other) @@ -50,12 +54,29 @@ QuicPacketPublicHeader& QuicPacketPublicHeader::operator=( guid = other.guid; reset_flag = other.reset_flag; version_flag = other.version_flag; - // Window's STL crashes when empty std::vectors are copied. - if (other.versions.size() > 0) - versions = other.versions; + versions = other.versions; return *this; } +QuicPacketHeader::QuicPacketHeader() + : fec_flag(false), + fec_entropy_flag(false), + entropy_flag(false), + entropy_hash(0), + packet_sequence_number(0), + fec_group(0) { +} + +QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header) + : public_header(header), + fec_flag(false), + fec_entropy_flag(false), + entropy_flag(false), + entropy_hash(0), + packet_sequence_number(0), + fec_group(0) { +} + QuicStreamFrame::QuicStreamFrame() {} QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, @@ -160,7 +181,7 @@ ostream& operator<<(ostream& os, for (TimeMap::const_iterator it = inter_arrival.received_packet_times.begin(); it != inter_arrival.received_packet_times.end(); ++it) { - os << it->first << "@" << it->second.ToMilliseconds() << " "; + os << it->first << "@" << it->second.ToDebuggingValue() << " "; } os << "]"; break; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index c72c7a7..44aedf9 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -91,6 +91,16 @@ const uint8 kNoFecOffset = 0xFF; const int64 kDefaultTimeoutUs = 600000000; // 10 minutes. +enum Retransmission { + NOT_RETRANSMISSION = 0, + IS_RETRANSMISSION = 1, +}; + +enum HasRetransmittableData { + HAS_RETRANSMITTABLE_DATA = 0, + NO_RETRANSMITTABLE_DATA = 1, +}; + enum QuicFrameType { PADDING_FRAME = 0, STREAM_FRAME, @@ -117,24 +127,32 @@ enum QuicPacketPrivateFlags { PACKET_PRIVATE_FLAGS_MAX = (1 << 3) - 1 // All bits set. }; -enum QuicErrorCode { - // Stream errors. - QUIC_NO_ERROR = 0, - - // Connection has reached an invalid state. - QUIC_INTERNAL_ERROR, +enum QuicRstStreamErrorCode { + QUIC_STREAM_NO_ERROR = 0, - // There were data frames after the a fin or reset. - QUIC_STREAM_DATA_AFTER_TERMINATION, // There was some server error which halted stream processing. QUIC_SERVER_ERROR_PROCESSING_STREAM, // We got two fin or reset offsets which did not match. QUIC_MULTIPLE_TERMINATION_OFFSETS, // We got bad payload and can not respond to it at the protocol level. QUIC_BAD_APPLICATION_PAYLOAD, + // Stream closed due to connection error. No reset frame is sent when this + // happens. + QUIC_STREAM_CONNECTION_ERROR, + // GoAway frame sent. No more stream can be created. + QUIC_STREAM_PEER_GOING_AWAY, + + // No error. Used as bound while iterating. + QUIC_STREAM_LAST_ERROR, +}; - // Connection errors. +enum QuicErrorCode { + QUIC_NO_ERROR = 0, + // Connection has reached an invalid state. + QUIC_INTERNAL_ERROR, + // There were data frames after the a fin or reset. + QUIC_STREAM_DATA_AFTER_TERMINATION, // Control frame is malformed. QUIC_INVALID_PACKET_HEADER, // Frame data is malformed. @@ -203,6 +221,7 @@ enum QuicErrorCode { // peer and ourselves. QUIC_CRYPTO_NO_SUPPORT, + // No error. Used as bound while iterating. QUIC_LAST_ERROR, }; @@ -232,10 +251,9 @@ struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { }; // Header for Data or FEC packets. -struct QuicPacketHeader { - QuicPacketHeader() {} - explicit QuicPacketHeader(const QuicPacketPublicHeader& header) - : public_header(header) {} +struct NET_EXPORT_PRIVATE QuicPacketHeader { + QuicPacketHeader(); + explicit QuicPacketHeader(const QuicPacketPublicHeader& header); NET_EXPORT_PRIVATE friend std::ostream& operator<<( std::ostream& os, const QuicPacketHeader& s); @@ -249,7 +267,7 @@ struct QuicPacketHeader { QuicFecGroupNumber fec_group; }; -struct QuicPublicResetPacket { +struct NET_EXPORT_PRIVATE QuicPublicResetPacket { QuicPublicResetPacket() {} explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header) : public_header(header) {} @@ -405,13 +423,13 @@ struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame { struct NET_EXPORT_PRIVATE QuicRstStreamFrame { QuicRstStreamFrame() {} - QuicRstStreamFrame(QuicStreamId stream_id, QuicErrorCode error_code) + QuicRstStreamFrame(QuicStreamId stream_id, QuicRstStreamErrorCode error_code) : stream_id(stream_id), error_code(error_code) { DCHECK_LE(error_code, std::numeric_limits<uint8>::max()); } QuicStreamId stream_id; - QuicErrorCode error_code; + QuicRstStreamErrorCode error_code; std::string error_details; }; diff --git a/net/quic/quic_reliable_client_stream.cc b/net/quic/quic_reliable_client_stream.cc index 676db77..1951ae8 100644 --- a/net/quic/quic_reliable_client_stream.cc +++ b/net/quic/quic_reliable_client_stream.cc @@ -19,7 +19,7 @@ QuicReliableClientStream::QuicReliableClientStream(QuicStreamId id, QuicReliableClientStream::~QuicReliableClientStream() { if (delegate_) - delegate_->OnClose(error()); + delegate_->OnClose(connection_error()); } uint32 QuicReliableClientStream::ProcessData(const char* data, @@ -39,7 +39,7 @@ uint32 QuicReliableClientStream::ProcessData(const char* data, void QuicReliableClientStream::TerminateFromPeer(bool half_close) { if (delegate_) { - delegate_->OnClose(error()); + delegate_->OnClose(connection_error()); delegate_ = NULL; } ReliableQuicStream::TerminateFromPeer(half_close); diff --git a/net/quic/quic_reliable_client_stream_test.cc b/net/quic/quic_reliable_client_stream_test.cc index 3da022b..b5f57e5 100644 --- a/net/quic/quic_reliable_client_stream_test.cc +++ b/net/quic/quic_reliable_client_stream_test.cc @@ -63,7 +63,7 @@ TEST_F(QuicReliableClientStreamTest, ProcessDataWithError) { EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), arraysize(data))).WillOnce(Return(ERR_UNEXPECTED)); - EXPECT_CALL(delegate_, OnClose(QUIC_BAD_APPLICATION_PAYLOAD)); + EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); EXPECT_EQ(0u, stream_.ProcessData(data, arraysize(data))); diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 39abb68..dd90fec 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -166,7 +166,7 @@ QuicConsumedData QuicSession::WriteData(QuicStreamId id, } void QuicSession::SendRstStream(QuicStreamId id, - QuicErrorCode error) { + QuicRstStreamErrorCode error) { connection_->SendRstStream(id, error); CloseStream(id); } @@ -242,7 +242,7 @@ ReliableQuicStream* QuicSession::GetIncomingReliableStream( if (goaway_sent_) { // We've already sent a GoAway - connection()->SendRstStream(stream_id, QUIC_PEER_GOING_AWAY); + SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY); return NULL; } diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index 7152aa0..963a53a 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -57,8 +57,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { QuicStreamOffset offset, bool fin); // Called by streams when they want to close the stream in both directions. - void SendRstStream(QuicStreamId id, - QuicErrorCode error); + virtual void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error); // Called when the session wants to go away and not accept any new streams. void SendGoAway(QuicErrorCode error_code, const std::string& reason); diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index a629f20..bd18ec8 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -207,7 +207,7 @@ TEST_F(QuicSessionTest, SendGoAway) { session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); EXPECT_TRUE(session_.goaway_sent()); - EXPECT_CALL(*connection_, SendRstStream(3u, QUIC_PEER_GOING_AWAY)); + EXPECT_CALL(*connection_, SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY)); EXPECT_FALSE(session_.GetIncomingReliableStream(3u)); } } // namespace diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index e5f74b6..c9c9177 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc @@ -47,7 +47,7 @@ class QuicStreamFactoryTest : public ::testing::Test { header.fec_flag = false; header.fec_group = 0; - QuicRstStreamFrame rst(stream_id, QUIC_NO_ERROR); + QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); return scoped_ptr<QuicEncryptedPacket>( ConstructPacket(header, QuicFrame(&rst))); } @@ -74,6 +74,7 @@ class QuicStreamFactoryTest : public ::testing::Test { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false); QuicFrames frames; frames.push_back(QuicFrame(&ack)); @@ -112,6 +113,7 @@ class QuicStreamFactoryTest : public ::testing::Test { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false); QuicFrames frames; frames.push_back(frame); diff --git a/net/quic/quic_stream_sequencer_test.cc b/net/quic/quic_stream_sequencer_test.cc index 74937ae..787d8f5 100644 --- a/net/quic/quic_stream_sequencer_test.cc +++ b/net/quic/quic_stream_sequencer_test.cc @@ -63,7 +63,7 @@ class MockStream : public ReliableQuicStream { MOCK_METHOD1(TerminateFromPeer, void(bool half_close)); MOCK_METHOD2(ProcessData, uint32(const char* data, uint32 data_len)); - MOCK_METHOD1(Close, void(QuicErrorCode error)); + MOCK_METHOD1(Close, void(QuicRstStreamErrorCode error)); MOCK_METHOD0(OnCanWrite, void()); }; diff --git a/net/quic/quic_time.cc b/net/quic/quic_time.cc index dac6231..c4c80b0 100644 --- a/net/quic/quic_time.cc +++ b/net/quic/quic_time.cc @@ -72,36 +72,14 @@ bool QuicTime::Delta::IsInfinite() const { // static QuicTime QuicTime::Zero() { - return QuicTime::FromMilliseconds(0); -} - -// static -QuicTime QuicTime::FromMilliseconds(int64 time_ms) { - // DateTime use 100 ns as resolution make sure we don't pass down too high - // values. - DCHECK(time_ms < kQuicInfiniteTimeUs / 1000); - return QuicTime(base::TimeTicks() + - base::TimeDelta::FromMilliseconds(time_ms)); -} - -// static -QuicTime QuicTime::FromMicroseconds(int64 time_us) { - // DateTime use 100 ns as resolution make sure we don't pass down too high - // values. - DCHECK(time_us < kQuicInfiniteTimeUs); - return QuicTime(base::TimeTicks() + - base::TimeDelta::FromMicroseconds(time_us)); + return QuicTime(base::TimeTicks()); } QuicTime::QuicTime(base::TimeTicks ticks) : ticks_(ticks) { } -int64 QuicTime::ToMilliseconds() const { - return (ticks_ - base::TimeTicks()).InMilliseconds(); -} - -int64 QuicTime::ToMicroseconds() const { +int64 QuicTime::ToDebuggingValue() const { return (ticks_ - base::TimeTicks()).InMicroseconds(); } diff --git a/net/quic/quic_time.h b/net/quic/quic_time.h index e780ca5..bfb476d 100644 --- a/net/quic/quic_time.h +++ b/net/quic/quic_time.h @@ -70,15 +70,11 @@ class NET_EXPORT_PRIVATE QuicTime { // will return false for these times. static QuicTime Zero(); - // Create a new QuicTime holding the time_ms. - static QuicTime FromMilliseconds(int64 time_ms); - - // Create a new QuicTime holding the time_us. - static QuicTime FromMicroseconds(int64 time_us); - - int64 ToMilliseconds() const; - - int64 ToMicroseconds() const; + // Produce the internal value to be used when logging. This value + // represents the number of microseconds since some epoch. It may + // be the UNIX epoch on some platforms. On others, it may + // be a CPU ticks based value. + int64 ToDebuggingValue() const; bool IsInitialized() const; @@ -89,10 +85,13 @@ class NET_EXPORT_PRIVATE QuicTime { Delta Subtract(const QuicTime& other) const; private: - base::TimeTicks ticks_; + friend bool operator==(QuicTime lhs, QuicTime rhs); + friend bool operator<(QuicTime lhs, QuicTime rhs); friend class QuicClock; friend class QuicClockTest; + + base::TimeTicks ticks_; }; // Non-member relational operators for QuicTime::Delta. @@ -116,13 +115,13 @@ inline bool operator>=(QuicTime::Delta lhs, QuicTime::Delta rhs) { } // Non-member relational operators for QuicTime. inline bool operator==(QuicTime lhs, QuicTime rhs) { - return lhs.ToMicroseconds() == rhs.ToMicroseconds(); + return lhs.ticks_ == rhs.ticks_; } inline bool operator!=(QuicTime lhs, QuicTime rhs) { return !(lhs == rhs); } inline bool operator<(QuicTime lhs, QuicTime rhs) { - return lhs.ToMicroseconds() < rhs.ToMicroseconds(); + return lhs.ticks_ < rhs.ticks_; } inline bool operator>(QuicTime lhs, QuicTime rhs) { return rhs < lhs; diff --git a/net/quic/quic_time_test.cc b/net/quic/quic_time_test.cc index a4a6c55..c4ea0e2 100644 --- a/net/quic/quic_time_test.cc +++ b/net/quic/quic_time_test.cc @@ -59,21 +59,15 @@ class QuicTimeTest : public ::testing::Test { TEST_F(QuicTimeTest, Initialized) { EXPECT_FALSE(QuicTime::Zero().IsInitialized()); - EXPECT_TRUE(QuicTime::FromMilliseconds(1).IsInitialized()); -} - -TEST_F(QuicTimeTest, FromTo) { - EXPECT_EQ(QuicTime::FromMilliseconds(1), - QuicTime::FromMicroseconds(1000)); - EXPECT_EQ(1u, QuicTime::FromMicroseconds(1000).ToMilliseconds()); - EXPECT_EQ(1000u, QuicTime::FromMilliseconds(1).ToMicroseconds()); + EXPECT_TRUE(QuicTime::Zero().Add( + QuicTime::Delta::FromMicroseconds(1)).IsInitialized()); } TEST_F(QuicTimeTest, Add) { - QuicTime time_1 = QuicTime::FromMilliseconds(1); - QuicTime time_2 = QuicTime::FromMilliseconds(1); - - time_1 = time_1.Subtract(QuicTime::Delta::FromMilliseconds(1)); + QuicTime time_1 = QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(1)); + QuicTime time_2 = QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(2)); QuicTime::Delta diff = time_2.Subtract(time_1); @@ -83,15 +77,18 @@ TEST_F(QuicTimeTest, Add) { } TEST_F(QuicTimeTest, Subtract) { - QuicTime time_1 = QuicTime::FromMilliseconds(1); - QuicTime time_2 = QuicTime::FromMilliseconds(2); + QuicTime time_1 = QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(1)); + QuicTime time_2 = QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(2)); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), time_2.Subtract(time_1)); } TEST_F(QuicTimeTest, SubtractDelta) { - QuicTime time = QuicTime::FromMilliseconds(2); - EXPECT_EQ(QuicTime::FromMilliseconds(1), + QuicTime time = QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(2)); + EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1)), time.Subtract(QuicTime::Delta::FromMilliseconds(1))); } @@ -99,7 +96,7 @@ TEST_F(QuicTimeTest, MockClock) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); QuicTime now = clock_.ApproximateNow(); - QuicTime time = QuicTime::FromMicroseconds(1000); + QuicTime time = QuicTime::Zero().Add(QuicTime::Delta::FromMicroseconds(1000)); EXPECT_EQ(now, time); diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index d28bc63..616e809 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -36,14 +36,28 @@ case x: \ return #x; // static +const char* QuicUtils::StreamErrorToString(QuicRstStreamErrorCode error) { + switch (error) { + RETURN_STRING_LITERAL(QUIC_STREAM_NO_ERROR); + RETURN_STRING_LITERAL(QUIC_STREAM_CONNECTION_ERROR); + RETURN_STRING_LITERAL(QUIC_SERVER_ERROR_PROCESSING_STREAM); + RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS); + RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD); + RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY); + RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR); + } + // Return a default value so that we return this when |error| doesn't match + // any of the QuicRstStreamErrorCodes. This can happen when the RstStream + // frame sent by the peer (attacker) has invalid error code. + return "INVALID_RST_STREAM_ERROR_CODE"; +} + +// static const char* QuicUtils::ErrorToString(QuicErrorCode error) { switch (error) { RETURN_STRING_LITERAL(QUIC_NO_ERROR); RETURN_STRING_LITERAL(QUIC_INTERNAL_ERROR); RETURN_STRING_LITERAL(QUIC_STREAM_DATA_AFTER_TERMINATION); - RETURN_STRING_LITERAL(QUIC_SERVER_ERROR_PROCESSING_STREAM); - RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS); - RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD); RETURN_STRING_LITERAL(QUIC_INVALID_PACKET_HEADER); RETURN_STRING_LITERAL(QUIC_INVALID_FRAME_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA); @@ -78,7 +92,10 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. } - return ""; + // Return a default value so that we return this when |error| doesn't match + // any of the QuicErrorCodes. This can happen when the ConnectionClose + // frame sent by the peer (attacker) has invalid error code. + return "INVALID_ERROR_CODE"; } } // namespace net diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h index 34f749e..32dc15f 100644 --- a/net/quic/quic_utils.h +++ b/net/quic/quic_utils.h @@ -19,7 +19,10 @@ class NET_EXPORT_PRIVATE QuicUtils { // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param static uint128 FNV1a_128_Hash(const char* data, int len); - // Returns the name of the quic error code as a char* + // Returns the name of the QuicRstStreamErrorCode as a char* + static const char* StreamErrorToString(QuicRstStreamErrorCode error); + + // Returns the name of the QuicErrorCode as a char* static const char* ErrorToString(QuicErrorCode error); }; diff --git a/net/quic/quic_utils_test.cc b/net/quic/quic_utils_test.cc new file mode 100644 index 0000000..a59c1e7 --- /dev/null +++ b/net/quic/quic_utils_test.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_utils.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { +namespace { + +TEST(QuicUtilsTest, StreamErrorToString) { + EXPECT_STREQ("QUIC_BAD_APPLICATION_PAYLOAD", + QuicUtils::StreamErrorToString(QUIC_BAD_APPLICATION_PAYLOAD)); +} + +TEST(QuicUtilsTest, ErrorToString) { + EXPECT_STREQ("QUIC_NO_ERROR", + QuicUtils::ErrorToString(QUIC_NO_ERROR)); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc index 79b060e..3e1b821 100644 --- a/net/quic/reliable_quic_stream.cc +++ b/net/quic/reliable_quic_stream.cc @@ -18,7 +18,8 @@ ReliableQuicStream::ReliableQuicStream(QuicStreamId id, visitor_(NULL), stream_bytes_read_(0), stream_bytes_written_(0), - error_(QUIC_NO_ERROR), + stream_error_(QUIC_STREAM_NO_ERROR), + connection_error_(QUIC_NO_ERROR), read_side_closed_(false), write_side_closed_(false), fin_buffered_(false), @@ -59,13 +60,17 @@ bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) { return accepted; } -void ReliableQuicStream::OnStreamReset(QuicErrorCode error) { - error_ = error; +void ReliableQuicStream::OnStreamReset(QuicRstStreamErrorCode error) { + stream_error_ = error; TerminateFromPeer(false); // Full close. } void ReliableQuicStream::ConnectionClose(QuicErrorCode error, bool from_peer) { - error_ = error; + if (error != QUIC_NO_ERROR) { + stream_error_ = QUIC_STREAM_CONNECTION_ERROR; + connection_error_ = error; + } + if (from_peer) { TerminateFromPeer(false); } else { @@ -81,8 +86,8 @@ void ReliableQuicStream::TerminateFromPeer(bool half_close) { CloseReadSide(); } -void ReliableQuicStream::Close(QuicErrorCode error) { - error_ = error; +void ReliableQuicStream::Close(QuicRstStreamErrorCode error) { + stream_error_ = error; session()->SendRstStream(id(), error); } diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h index 8249c96..a26595be 100644 --- a/net/quic/reliable_quic_stream.h +++ b/net/quic/reliable_quic_stream.h @@ -54,7 +54,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { virtual void OnClose(); // Called when we get a stream reset from the client. - void OnStreamReset(QuicErrorCode error); + virtual void OnStreamReset(QuicRstStreamErrorCode error); // Called when we get or send a connection close, and should immediately // close the stream. This is not passed through the sequencer, @@ -68,7 +68,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { virtual uint32 ProcessData(const char* data, uint32 data_len) = 0; // Called to close the stream from this end. - virtual void Close(QuicErrorCode error); + virtual void Close(QuicRstStreamErrorCode error); // This block of functions wraps the sequencer's functions of the same // name. @@ -77,7 +77,8 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { QuicStreamId id() const { return id_; } - QuicErrorCode error() const { return error_; } + QuicRstStreamErrorCode stream_error() const { return stream_error_; } + QuicErrorCode connection_error() const { return connection_error_; } bool read_side_closed() const { return read_side_closed_; } bool write_side_closed() const { return write_side_closed_; } @@ -134,7 +135,15 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { // framing, encryption overhead etc. uint64 stream_bytes_read_; uint64 stream_bytes_written_; - QuicErrorCode error_; + + // Stream error code received from a RstStreamFrame or error code sent by the + // visitor or sequencer in the RstStreamFrame. + QuicRstStreamErrorCode stream_error_; + // Connection error code due to which the stream was closed. |stream_error_| + // is set to |QUIC_STREAM_CONNECTION_ERROR| when this happens and consumers + // should check |connection_error_|. + QuicErrorCode connection_error_; + // True if the read side is closed and further frames should be rejected. bool read_side_closed_; // True if the write side is closed, and further writes should fail. diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index fcb04ab..7afad69 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -120,21 +120,21 @@ void CryptoTestUtils::CompareClientAndServerKeys( QuicCryptoClientStream* client, QuicCryptoServerStream* server) { StringPiece client_encrypter_key = - client->crypto_negotiated_params_.encrypter->GetKey(); + client->session()->connection()->encrypter()->GetKey(); StringPiece client_encrypter_iv = - client->crypto_negotiated_params_.encrypter->GetNoncePrefix(); + client->session()->connection()->encrypter()->GetNoncePrefix(); StringPiece client_decrypter_key = - client->crypto_negotiated_params_.decrypter->GetKey(); + client->session()->connection()->decrypter()->GetKey(); StringPiece client_decrypter_iv = - client->crypto_negotiated_params_.decrypter->GetNoncePrefix(); + client->session()->connection()->decrypter()->GetNoncePrefix(); StringPiece server_encrypter_key = - server->crypto_negotiated_params_.encrypter->GetKey(); + server->session()->connection()->encrypter()->GetKey(); StringPiece server_encrypter_iv = - server->crypto_negotiated_params_.encrypter->GetNoncePrefix(); + server->session()->connection()->encrypter()->GetNoncePrefix(); StringPiece server_decrypter_key = - server->crypto_negotiated_params_.decrypter->GetKey(); + server->session()->connection()->decrypter()->GetKey(); StringPiece server_decrypter_iv = - server->crypto_negotiated_params_.decrypter->GetNoncePrefix(); + server->session()->connection()->decrypter()->GetNoncePrefix(); CompareCharArraysWithHexError("client write key", client_encrypter_key.data(), client_encrypter_key.length(), diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index ce726e1..1fb7f4f 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -157,7 +157,7 @@ bool PacketSavingConnection::SendOrQueuePacket( QuicPacketSequenceNumber sequence_number, QuicPacket* packet, QuicPacketEntropyHash entropy_hash, - bool has_retransmittable_data) { + HasRetransmittableData retransmittable) { packets_.push_back(packet); return true; } @@ -268,6 +268,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( QuicFramer quic_framer(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), false); QuicPacketHeader header; diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 523daaf..2f1f382 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -217,7 +217,7 @@ class MockConnection : public QuicConnection { MOCK_METHOD2(SendConnectionCloseWithDetails, void(QuicErrorCode error, const string& details)); MOCK_METHOD2(SendRstStream, void(QuicStreamId id, - QuicErrorCode error)); + QuicRstStreamErrorCode error)); MOCK_METHOD3(SendGoAway, void(QuicErrorCode error, QuicStreamId last_good_stream_id, const string& reason)); @@ -242,10 +242,11 @@ class PacketSavingConnection : public MockConnection { PacketSavingConnection(QuicGuid guid, IPEndPoint address, bool is_server); virtual ~PacketSavingConnection(); - virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, - QuicPacket* packet, - QuicPacketEntropyHash entropy_hash, - bool has_retransmittable_data) OVERRIDE; + virtual bool SendOrQueuePacket( + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + QuicPacketEntropyHash entropy_hash, + HasRetransmittableData has_retransmittable_data) OVERRIDE; std::vector<QuicPacket*> packets_; @@ -291,10 +292,11 @@ class MockSendAlgorithm : public SendAlgorithmInterface { void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta)); MOCK_METHOD1(OnIncomingLoss, void(QuicTime)); MOCK_METHOD4(SentPacket, void(QuicTime sent_time, QuicPacketSequenceNumber, - QuicByteCount, bool)); + QuicByteCount, Retransmission)); MOCK_METHOD2(AbandoningPacket, void(QuicPacketSequenceNumber sequence_number, QuicByteCount abandoned_bytes)); - MOCK_METHOD3(TimeUntilSend, QuicTime::Delta(QuicTime now, bool, bool)); + MOCK_METHOD3(TimeUntilSend, QuicTime::Delta(QuicTime now, Retransmission, + HasRetransmittableData)); MOCK_METHOD0(BandwidthEstimate, QuicBandwidth(void)); MOCK_METHOD0(SmoothedRtt, QuicTime::Delta(void)); diff --git a/net/quic/test_tools/simple_quic_framer.cc b/net/quic/test_tools/simple_quic_framer.cc index b9b690d..5d21b73 100644 --- a/net/quic/test_tools/simple_quic_framer.cc +++ b/net/quic/test_tools/simple_quic_framer.cc @@ -125,6 +125,7 @@ SimpleQuicFramer::SimpleQuicFramer() : framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), true), visitor_(NULL) { } diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index 68968ca..70b9a76 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -11,6 +11,8 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/simple_thread.h" #include "net/base/ip_endpoint.h" +// TODO(rtenneti): Delete this when NSS is supported. +#include "net/quic/crypto/aes_128_gcm_encrypter.h" #include "net/quic/crypto/null_encrypter.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" @@ -99,7 +101,8 @@ class ServerThread : public base::SimpleThread { class EndToEndTest : public ::testing::Test { protected: EndToEndTest() - : server_hostname_("localhost") { + : server_hostname_("localhost"), + server_started_(false) { net::IPAddressNumber ip; CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip)); server_address_ = IPEndPoint(ip, 0); @@ -136,9 +139,12 @@ class EndToEndTest : public ::testing::Test { server_thread_->listening()->Wait(); server_address_ = IPEndPoint(server_address_.address(), server_thread_->GetPort()); + server_started_ = true; } void StopServer() { + if (!server_started_) + return; server_thread_->quit()->Signal(); server_thread_->Join(); } @@ -178,9 +184,16 @@ class EndToEndTest : public ::testing::Test { string server_hostname_; scoped_ptr<ServerThread> server_thread_; scoped_ptr<QuicTestClient> client_; + bool server_started_; }; TEST_F(EndToEndTest, SimpleRequestResponse) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -188,6 +201,12 @@ TEST_F(EndToEndTest, SimpleRequestResponse) { } TEST_F(EndToEndTest, SimpleRequestResponsev6) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + IPAddressNumber ip; CHECK(net::ParseIPLiteralToNumber("::", &ip)); server_address_ = IPEndPoint(ip, server_address_.port()); @@ -198,6 +217,12 @@ TEST_F(EndToEndTest, SimpleRequestResponsev6) { } TEST_F(EndToEndTest, SeparateFinPacket) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); HTTPMessage request(HttpConstants::HTTP_1_1, @@ -222,6 +247,12 @@ TEST_F(EndToEndTest, SeparateFinPacket) { } TEST_F(EndToEndTest, MultipleRequestResponse) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -231,6 +262,12 @@ TEST_F(EndToEndTest, MultipleRequestResponse) { } TEST_F(EndToEndTest, MultipleClients) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); scoped_ptr<QuicTestClient> client2(CreateQuicClient()); @@ -254,6 +291,12 @@ TEST_F(EndToEndTest, MultipleClients) { } TEST_F(EndToEndTest, RequestOverMultiplePackets) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); // Set things up so we have a small payload, to guarantee fragmentation. // A congestion feedback frame can't be split into multiple packets, make sure @@ -278,6 +321,12 @@ TEST_F(EndToEndTest, RequestOverMultiplePackets) { } TEST_F(EndToEndTest, MultipleFramesRandomOrder) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); // Set things up so we have a small payload, to guarantee fragmentation. // A congestion feedback frame can't be split into multiple packets, make sure @@ -303,6 +352,12 @@ TEST_F(EndToEndTest, MultipleFramesRandomOrder) { } TEST_F(EndToEndTest, PostMissingBytes) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); // Add a content length header with no body. @@ -319,6 +374,12 @@ TEST_F(EndToEndTest, PostMissingBytes) { } TEST_F(EndToEndTest, LargePost) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + // FLAGS_fake_packet_loss_percentage = 30; ASSERT_TRUE(Initialize()); @@ -333,6 +394,12 @@ TEST_F(EndToEndTest, LargePost) { } TEST_F(EndToEndTest, LargePostFEC) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + // FLAGS_fake_packet_loss_percentage = 30; ASSERT_TRUE(Initialize()); client_->options()->max_packets_per_fec_group = 6; @@ -365,6 +432,12 @@ TEST_F(EndToEndTest, LargePostFEC) { }*/ TEST_F(EndToEndTest, InvalidStream) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); string body; @@ -383,6 +456,12 @@ TEST_F(EndToEndTest, InvalidStream) { } TEST_F(EndToEndTest, MultipleTermination) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); scoped_ptr<QuicTestClient> client2(CreateQuicClient()); @@ -424,6 +503,12 @@ TEST_F(EndToEndTest, MultipleTermination) { }*/ TEST_F(EndToEndTest, ResetConnection) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); diff --git a/net/tools/quic/quic_time_wait_list_manager.cc b/net/tools/quic/quic_time_wait_list_manager.cc index 04fd922..02dbb63 100644 --- a/net/tools/quic/quic_time_wait_list_manager.cc +++ b/net/tools/quic/quic_time_wait_list_manager.cc @@ -93,6 +93,7 @@ QuicTimeWaitListManager::QuicTimeWaitListManager( : framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL), + QuicTime::Zero(), true), epoll_server_(epoll_server), kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)), diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc index 22e3827..43ac914 100644 --- a/net/tools/quic/test_tools/quic_test_client.cc +++ b/net/tools/quic/test_tools/quic_test_client.cc @@ -37,7 +37,7 @@ QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname) : server_address_(address), client_(address, hostname), stream_(NULL), - stream_error_(QUIC_NO_ERROR), + stream_error_(QUIC_STREAM_NO_ERROR), connection_error_(QUIC_NO_ERROR), bytes_read_(0), bytes_written_(0), @@ -135,7 +135,7 @@ IPEndPoint QuicTestClient::LocalSocketAddress() const { } void QuicTestClient::ClearPerRequestState() { - stream_error_ = QUIC_NO_ERROR; + stream_error_ = QUIC_STREAM_NO_ERROR; connection_error_ = QUIC_NO_ERROR; stream_ = NULL; response_ = ""; @@ -170,8 +170,8 @@ size_t QuicTestClient::bytes_written() const { void QuicTestClient::OnClose(ReliableQuicStream* stream) { response_ = stream_->data(); headers_.CopyFrom(stream_->headers()); - stream_error_ = stream_->error(); - connection_error_ = stream_->error(); + stream_error_ = stream_->stream_error(); + connection_error_ = stream_->connection_error(); bytes_read_ = stream_->stream_bytes_read(); bytes_written_ = stream_->stream_bytes_written(); stream_ = NULL; diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h index d02905a..1bb4f07 100644 --- a/net/tools/quic/test_tools/quic_test_client.h +++ b/net/tools/quic/test_tools/quic_test_client.h @@ -61,7 +61,7 @@ class QuicTestClient : public ReliableQuicStream::Visitor { // Returns NULL if the maximum number of streams have already been created. QuicReliableClientStream* GetOrCreateStream(); - QuicErrorCode stream_error() { return stream_error_; } + QuicRstStreamErrorCode stream_error() { return stream_error_; } QuicErrorCode connection_error() { return connection_error_; } QuicClient* client() { return &client_; } @@ -75,7 +75,7 @@ class QuicTestClient : public ReliableQuicStream::Visitor { QuicClient client_; // The actual client QuicReliableClientStream* stream_; - QuicErrorCode stream_error_; + QuicRstStreamErrorCode stream_error_; QuicErrorCode connection_error_; BalsaHeaders headers_; |