From 8e01c06dc909bf509e3cddaa6d1daa5712834d5c Mon Sep 17 00:00:00 2001 From: "rtenneti@chromium.org" Date: Thu, 31 Oct 2013 07:35:31 +0000 Subject: Land Recent QUIC changes. Fix a bug in the QuicFramer where the entropy genererator was not being consulted when truncated acks were sent, causing an incorrect entropy to be sent for truncated acks. Merge internal change: 55879563 Add an optional initial RTT to the negotiation parameters to allow the QUIC client to suggest an expected RTT to the server. Merge internal change: 55792505 Changing DCHECKs to LOG(DFATAL) so we'll notice if they're occurring in the running server/client. Merge internal change: 55790448 Added a blank line per the change in the following CL which deleted default-true flags. Merge internal change: 55738366 Allow a REJ message to be twice as large as a CHLO message that doesn't contain a valid source-address token. message that doesn't contain a valid source-address token could be twice as large as before. Merge internal change: 55736193 Log OpenSSL errors when QUIC encryption fails. Merge internal change: 55718465 Second version with improved tests and keeping the client and server packet sizes identical to ensure the truncated ack detection works correctly. Merge internal change: 55651642 Hidden bug that uses ack_frame instead of stream_frame. Merge internal change: 55648483 Move QuicCryptoServerConfig from crypto_server_config.{h,cc} to quic_crypto_server_config.{h,cc} and move QuicCryptoClientConfig from crypt_handshake.{h,cc} to quic_crypto_client_config.{h,cc} to be consistent. Merge internal change: 55644306 Ported IOVector unit tests (as part of porting the following CL). Makes GFE's IOVector class copy-able. I am working on using IOVector through QUIC's read and write paths, and it would be tremendously helpful to have value-semantics for an IOVector object. Making it copy-able was noted in joechan@'s TODO in the original IOVector code. Merge internal change: 55509822 Cancel all pending alarms when the QUIC connection is closed. Merge internal change: 55640997 Removed version 10 from supported versions. Adding QUIC_VERSION_12 to optimize the Quic ack framing format to reduce the size and better handle ranges of nacks, which should make truncated acks virtually impossible. Also adding an explicit flag for truncated acks and moving the ack outside of the connection close frame. Merge internal change: 55594574 R=rch@chromium.org Review URL: https://codereview.chromium.org/51313002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232035 0039d316-1c4b-4281-b951-d872f2087c98 --- net/net.gyp | 9 +- net/quic/congestion_control/fix_rate_sender.cc | 15 +- net/quic/congestion_control/fix_rate_sender.h | 3 + net/quic/congestion_control/fix_rate_test.cc | 12 +- net/quic/congestion_control/inter_arrival_probe.cc | 11 +- net/quic/congestion_control/inter_arrival_probe.h | 5 +- .../congestion_control/inter_arrival_probe_test.cc | 23 +- .../congestion_control/inter_arrival_sender.cc | 14 +- net/quic/congestion_control/inter_arrival_sender.h | 3 + .../inter_arrival_sender_test.cc | 19 +- net/quic/congestion_control/paced_sender.cc | 11 +- net/quic/congestion_control/paced_sender.h | 5 +- net/quic/congestion_control/paced_sender_test.cc | 19 +- .../quic_congestion_control_test.cc | 25 +- .../congestion_control/quic_congestion_manager.cc | 13 + .../congestion_control/quic_congestion_manager.h | 2 + .../congestion_control/send_algorithm_interface.h | 3 + net/quic/congestion_control/tcp_cubic_sender.cc | 15 +- net/quic/congestion_control/tcp_cubic_sender.h | 2 + .../congestion_control/tcp_cubic_sender_test.cc | 17 + .../crypto/aes_128_gcm_12_encrypter_openssl.cc | 20 + net/quic/crypto/crypto_handshake.cc | 614 ----------- net/quic/crypto/crypto_handshake.h | 186 ---- net/quic/crypto/crypto_handshake_test.cc | 355 ------- net/quic/crypto/crypto_protocol.h | 4 + net/quic/crypto/crypto_server_config.cc | 1108 ------------------- net/quic/crypto/crypto_server_config.h | 370 ------- net/quic/crypto/crypto_server_test.cc | 2 +- net/quic/crypto/quic_crypto_client_config.cc | 633 +++++++++++ net/quic/crypto/quic_crypto_client_config.h | 208 ++++ net/quic/crypto/quic_crypto_server_config.cc | 1118 ++++++++++++++++++++ net/quic/crypto/quic_crypto_server_config.h | 370 +++++++ net/quic/crypto/quic_crypto_server_config_test.cc | 355 +++++++ net/quic/iovector_test.cc | 283 +++++ net/quic/quic_config.cc | 83 +- net/quic/quic_config.h | 24 + net/quic/quic_config_test.cc | 18 + net/quic/quic_connection.cc | 62 +- net/quic/quic_connection.h | 9 +- net/quic/quic_connection_test.cc | 79 +- net/quic/quic_crypto_client_stream.cc | 2 + net/quic/quic_crypto_client_stream.h | 2 +- net/quic/quic_crypto_server_stream.cc | 3 +- net/quic/quic_crypto_server_stream_test.cc | 2 +- net/quic/quic_framer.cc | 563 ++++++++-- net/quic/quic_framer.h | 84 +- net/quic/quic_framer_test.cc | 477 ++++++++- net/quic/quic_packet_creator.cc | 32 +- net/quic/quic_packet_creator.h | 5 +- net/quic/quic_packet_creator_test.cc | 53 +- net/quic/quic_packet_generator.cc | 4 +- net/quic/quic_packet_generator_test.cc | 6 +- net/quic/quic_protocol.cc | 6 +- net/quic/quic_protocol.h | 37 +- net/quic/quic_session.cc | 13 +- net/quic/quic_session.h | 1 + net/quic/quic_session_test.cc | 1 + net/quic/reliable_quic_stream.cc | 1 + net/quic/test_tools/crypto_test_utils.cc | 2 +- net/quic/test_tools/quic_test_utils.cc | 11 +- net/quic/test_tools/quic_test_utils.h | 16 +- net/tools/quic/end_to_end_test.cc | 104 ++ net/tools/quic/quic_client.cc | 6 + net/tools/quic/quic_dispatcher_test.cc | 2 +- net/tools/quic/quic_server.cc | 1 + net/tools/quic/quic_server.h | 2 +- net/tools/quic/quic_server_session_test.cc | 2 +- net/tools/quic/test_tools/mock_quic_dispatcher.h | 2 +- 68 files changed, 4629 insertions(+), 2938 deletions(-) delete mode 100644 net/quic/crypto/crypto_handshake_test.cc delete mode 100644 net/quic/crypto/crypto_server_config.cc delete mode 100644 net/quic/crypto/crypto_server_config.h create mode 100644 net/quic/crypto/quic_crypto_client_config.cc create mode 100644 net/quic/crypto/quic_crypto_client_config.h create mode 100644 net/quic/crypto/quic_crypto_server_config.cc create mode 100644 net/quic/crypto/quic_crypto_server_config.h create mode 100644 net/quic/crypto/quic_crypto_server_config_test.cc create mode 100644 net/quic/iovector_test.cc (limited to 'net') diff --git a/net/net.gyp b/net/net.gyp index 114f8f1..9d511d1 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -762,8 +762,6 @@ 'quic/crypto/crypto_protocol.h', 'quic/crypto/crypto_secret_boxer.cc', 'quic/crypto/crypto_secret_boxer.h', - 'quic/crypto/crypto_server_config.cc', - 'quic/crypto/crypto_server_config.h', 'quic/crypto/crypto_server_config_protobuf.cc', 'quic/crypto/crypto_server_config_protobuf.h', 'quic/crypto/crypto_utils.cc', @@ -785,6 +783,10 @@ 'quic/crypto/proof_verifier.cc', 'quic/crypto/proof_verifier_chromium.cc', 'quic/crypto/proof_verifier_chromium.h', + 'quic/crypto/quic_crypto_client_config.cc', + 'quic/crypto/quic_crypto_client_config.h', + 'quic/crypto/quic_crypto_server_config.cc', + 'quic/crypto/quic_crypto_server_config.h', 'quic/crypto/quic_decrypter.cc', 'quic/crypto/quic_decrypter.h', 'quic/crypto/quic_encrypter.cc', @@ -1736,7 +1738,6 @@ 'quic/crypto/channel_id_test.cc', 'quic/crypto/common_cert_set_test.cc', 'quic/crypto/crypto_framer_test.cc', - 'quic/crypto/crypto_handshake_test.cc', 'quic/crypto/crypto_secret_boxer_test.cc', 'quic/crypto/crypto_server_test.cc', 'quic/crypto/crypto_utils_test.cc', @@ -1745,8 +1746,10 @@ 'quic/crypto/null_encrypter_test.cc', 'quic/crypto/p256_key_exchange_test.cc', 'quic/crypto/proof_test.cc', + 'quic/crypto/quic_crypto_server_config_test.cc', 'quic/crypto/quic_random_test.cc', 'quic/crypto/strike_register_test.cc', + 'quic/iovector_test.cc', 'quic/test_tools/crypto_test_utils.cc', 'quic/test_tools/crypto_test_utils.h', 'quic/test_tools/crypto_test_utils_chromium.cc', diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc index 05fcb35..7aab1e5 100644 --- a/net/quic/congestion_control/fix_rate_sender.cc +++ b/net/quic/congestion_control/fix_rate_sender.cc @@ -20,8 +20,9 @@ namespace net { FixRateSender::FixRateSender(const QuicClock* clock) : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)), + max_segment_size_(kDefaultMaxPacketSize), fix_rate_leaky_bucket_(bitrate_), - paced_sender_(bitrate_), + paced_sender_(bitrate_, max_segment_size_), data_in_flight_(0), latest_rtt_(QuicTime::Delta::Zero()) { DLOG(INFO) << "FixRateSender"; @@ -30,12 +31,18 @@ FixRateSender::FixRateSender(const QuicClock* clock) FixRateSender::~FixRateSender() { } +void FixRateSender::SetFromConfig(const QuicConfig& config, bool is_server) { + max_segment_size_ = config.server_max_packet_size(); + paced_sender_.set_max_segment_size(max_segment_size_); +} + void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, const SentPacketsMap& /*sent_packets*/) { - DCHECK(feedback.type == kFixRate) << - "Invalid incoming CongestionFeedbackType:" << feedback.type; + if (feedback.type != kFixRate) { + LOG(DFATAL) << "Invalid incoming CongestionFeedbackType:" << feedback.type; + } if (feedback.type == kFixRate) { bitrate_ = feedback.fix_rate.bitrate; fix_rate_leaky_bucket_.SetDrainingRate(feedback_receive_time, bitrate_); @@ -105,7 +112,7 @@ QuicByteCount FixRateSender::CongestionWindow() { QuicByteCount window_size_bytes = bitrate_.ToBytesPerPeriod( QuicTime::Delta::FromMicroseconds(kWindowSizeUs)); // Make sure window size is not less than a packet. - return std::max(kMaxPacketSize, window_size_bytes); + return std::max(kDefaultMaxPacketSize, window_size_bytes); } QuicBandwidth FixRateSender::BandwidthEstimate() { diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h index bbbee68..f69fd08 100644 --- a/net/quic/congestion_control/fix_rate_sender.h +++ b/net/quic/congestion_control/fix_rate_sender.h @@ -23,6 +23,8 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { explicit FixRateSender(const QuicClock* clock); virtual ~FixRateSender(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; + // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, @@ -56,6 +58,7 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { QuicByteCount CongestionWindow(); QuicBandwidth bitrate_; + QuicByteCount max_segment_size_; LeakyBucket fix_rate_leaky_bucket_; PacedSender paced_sender_; QuicByteCount data_in_flight_; diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc index 6cb8c9c..7f186d1 100644 --- a/net/quic/congestion_control/fix_rate_test.cc +++ b/net/quic/congestion_control/fix_rate_test.cc @@ -63,12 +63,12 @@ TEST_F(FixRateTest, SenderAPI) { EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), 1, kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + sender_->OnPacketSent(clock_.Now(), 1, kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), 2, kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + sender_->OnPacketSent(clock_.Now(), 2, kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); sender_->OnPacketSent(clock_.Now(), 3, 600, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), @@ -78,8 +78,8 @@ TEST_F(FixRateTest, SenderAPI) { EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); - sender_->OnIncomingAck(1, kMaxPacketSize, rtt_); - sender_->OnIncomingAck(2, kMaxPacketSize, rtt_); + sender_->OnIncomingAck(1, kDefaultMaxPacketSize, rtt_); + sender_->OnIncomingAck(2, kDefaultMaxPacketSize, rtt_); sender_->OnIncomingAck(3, 600, rtt_); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); diff --git a/net/quic/congestion_control/inter_arrival_probe.cc b/net/quic/congestion_control/inter_arrival_probe.cc index 7d6abdc..d8fa2db 100644 --- a/net/quic/congestion_control/inter_arrival_probe.cc +++ b/net/quic/congestion_control/inter_arrival_probe.cc @@ -16,8 +16,9 @@ const float kUncertainScaleFactor = 0.5; // TODO(pwestin): revisit this factor. namespace net { -InterArrivalProbe::InterArrivalProbe() - : estimate_available_(false), +InterArrivalProbe::InterArrivalProbe(QuicByteCount max_segment_size) + : max_segment_size_(max_segment_size), + estimate_available_(false), available_channel_estimate_(QuicBandwidth::Zero()), unacked_data_(0) { } @@ -25,6 +26,10 @@ InterArrivalProbe::InterArrivalProbe() InterArrivalProbe::~InterArrivalProbe() { } +void InterArrivalProbe::set_max_segment_size(QuicByteCount max_segment_size) { + max_segment_size_ = max_segment_size; +} + bool InterArrivalProbe::GetEstimate(QuicBandwidth* available_channel_estimate) { if (!estimate_available_) { return false; @@ -50,7 +55,7 @@ QuicByteCount InterArrivalProbe::GetAvailableCongestionWindow() { if (estimate_available_) { return 0; } - return (kProbeSizePackets * kMaxPacketSize) - unacked_data_; + return (kProbeSizePackets * max_segment_size_) - unacked_data_; } void InterArrivalProbe::OnIncomingFeedback( diff --git a/net/quic/congestion_control/inter_arrival_probe.h b/net/quic/congestion_control/inter_arrival_probe.h index e4c0331..0cba7ee 100644 --- a/net/quic/congestion_control/inter_arrival_probe.h +++ b/net/quic/congestion_control/inter_arrival_probe.h @@ -17,9 +17,11 @@ namespace net { class NET_EXPORT_PRIVATE InterArrivalProbe { public: - InterArrivalProbe(); + explicit InterArrivalProbe(QuicByteCount max_segment_size); ~InterArrivalProbe(); + void set_max_segment_size(QuicByteCount max_segment_size); + // Call every time a packet is sent to the network. void OnPacketSent(QuicByteCount bytes); @@ -45,6 +47,7 @@ class NET_EXPORT_PRIVATE InterArrivalProbe { bool GetEstimate(QuicBandwidth* available_channel_estimate); private: + QuicByteCount max_segment_size_; scoped_ptr available_channel_estimator_; QuicPacketSequenceNumber first_sequence_number_; bool estimate_available_; diff --git a/net/quic/congestion_control/inter_arrival_probe_test.cc b/net/quic/congestion_control/inter_arrival_probe_test.cc index c311da0..d242a6c 100644 --- a/net/quic/congestion_control/inter_arrival_probe_test.cc +++ b/net/quic/congestion_control/inter_arrival_probe_test.cc @@ -13,7 +13,9 @@ namespace test { class InterArrivalProbeTest : public ::testing::Test { protected: - InterArrivalProbeTest() : start_(QuicTime::Zero()) { + InterArrivalProbeTest() + : probe_(kDefaultMaxPacketSize), + start_(QuicTime::Zero()) { } InterArrivalProbe probe_; @@ -22,19 +24,20 @@ class InterArrivalProbeTest : public ::testing::Test { TEST_F(InterArrivalProbeTest, CongestionWindow) { for (size_t i = 0; i < 10; i++) { - probe_.OnPacketSent(kMaxPacketSize); - EXPECT_EQ((9 - i) * kMaxPacketSize, probe_.GetAvailableCongestionWindow()); + probe_.OnPacketSent(kDefaultMaxPacketSize); + EXPECT_EQ((9 - i) * kDefaultMaxPacketSize, + probe_.GetAvailableCongestionWindow()); } - probe_.OnAcknowledgedPacket(kMaxPacketSize); - EXPECT_EQ(kMaxPacketSize, probe_.GetAvailableCongestionWindow()); + probe_.OnAcknowledgedPacket(kDefaultMaxPacketSize); + EXPECT_EQ(kDefaultMaxPacketSize, probe_.GetAvailableCongestionWindow()); - probe_.OnPacketSent(kMaxPacketSize); + probe_.OnPacketSent(kDefaultMaxPacketSize); EXPECT_EQ(0u, probe_.GetAvailableCongestionWindow()); } TEST_F(InterArrivalProbeTest, Estimate) { QuicPacketSequenceNumber sequence_number = 1; - QuicByteCount bytes_sent = kMaxPacketSize; + QuicByteCount bytes_sent = kDefaultMaxPacketSize; QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); @@ -50,13 +53,13 @@ TEST_F(InterArrivalProbeTest, Estimate) { time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); } EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); - EXPECT_EQ(kMaxPacketSize * 100, + EXPECT_EQ(kDefaultMaxPacketSize * 100, static_cast(available_channel_estimate.ToBytesPerSecond())); } TEST_F(InterArrivalProbeTest, EstimateWithLoss) { QuicPacketSequenceNumber sequence_number = 1; - QuicByteCount bytes_sent = kMaxPacketSize; + QuicByteCount bytes_sent = kDefaultMaxPacketSize; QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); @@ -73,7 +76,7 @@ TEST_F(InterArrivalProbeTest, EstimateWithLoss) { time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); } EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); - EXPECT_EQ(kMaxPacketSize * 50, + EXPECT_EQ(kDefaultMaxPacketSize * 50, static_cast(available_channel_estimate.ToBytesPerSecond())); } diff --git a/net/quic/congestion_control/inter_arrival_sender.cc b/net/quic/congestion_control/inter_arrival_sender.cc index 2740f1e..94df198 100644 --- a/net/quic/congestion_control/inter_arrival_sender.cc +++ b/net/quic/congestion_control/inter_arrival_sender.cc @@ -24,15 +24,16 @@ static const int kMinBitrateSmoothingPeriodMs = 500; InterArrivalSender::InterArrivalSender(const QuicClock* clock) : probing_(true), + max_segment_size_(kDefaultMaxPacketSize), current_bandwidth_(QuicBandwidth::Zero()), smoothed_rtt_(QuicTime::Delta::Zero()), channel_estimator_(new ChannelEstimator()), bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)), overuse_detector_(new InterArrivalOveruseDetector()), - probe_(new InterArrivalProbe()), + probe_(new InterArrivalProbe(max_segment_size_)), state_machine_(new InterArrivalStateMachine(clock)), paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond( - kProbeBitrateKBytesPerSecond))), + kProbeBitrateKBytesPerSecond), max_segment_size_)), accumulated_number_of_lost_packets_(0), bandwidth_usage_state_(kBandwidthSteady), back_down_time_(QuicTime::Zero()), @@ -43,6 +44,13 @@ InterArrivalSender::InterArrivalSender(const QuicClock* clock) InterArrivalSender::~InterArrivalSender() { } +void InterArrivalSender::SetFromConfig(const QuicConfig& config, + bool is_server) { + max_segment_size_ = config.server_max_packet_size(); + paced_sender_->set_max_segment_size(max_segment_size_); + probe_->set_max_segment_size(max_segment_size_); +} + // TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest). // static QuicBandwidth InterArrivalSender::CalculateSentBandwidth( @@ -459,7 +467,7 @@ void InterArrivalSender::EstimateBandwidthAfterDelayEvent( // While in delay sensing mode send at least one packet per RTT. QuicBandwidth min_delay_bitrate = - QuicBandwidth::FromBytesAndTimeDelta(kMaxPacketSize, SmoothedRtt()); + QuicBandwidth::FromBytesAndTimeDelta(max_segment_size_, SmoothedRtt()); new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate); ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate); diff --git a/net/quic/congestion_control/inter_arrival_sender.h b/net/quic/congestion_control/inter_arrival_sender.h index 3961a2b..2b70e74 100644 --- a/net/quic/congestion_control/inter_arrival_sender.h +++ b/net/quic/congestion_control/inter_arrival_sender.h @@ -27,6 +27,8 @@ class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface { explicit InterArrivalSender(const QuicClock* clock); virtual ~InterArrivalSender(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; + static QuicBandwidth CalculateSentBandwidth( const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, QuicTime feedback_receive_time); @@ -83,6 +85,7 @@ class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface { bool ProbingPhase(QuicTime feedback_receive_time); bool probing_; // Are we currently in the probing phase? + QuicByteCount max_segment_size_; QuicBandwidth current_bandwidth_; QuicTime::Delta smoothed_rtt_; scoped_ptr channel_estimator_; diff --git a/net/quic/congestion_control/inter_arrival_sender_test.cc b/net/quic/congestion_control/inter_arrival_sender_test.cc index c1c848bb..65264ee6 100644 --- a/net/quic/congestion_control/inter_arrival_sender_test.cc +++ b/net/quic/congestion_control/inter_arrival_sender_test.cc @@ -37,7 +37,7 @@ class InterArrivalSenderTest : public ::testing::Test { void SendAvailableCongestionWindow() { while (sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()) { - QuicByteCount bytes_in_packet = kMaxPacketSize; + QuicByteCount bytes_in_packet = kDefaultMaxPacketSize; sent_packets_[sequence_number_] = new class SendAlgorithmInterface::SentPacket( bytes_in_packet, send_clock_.Now()); @@ -52,7 +52,8 @@ class InterArrivalSenderTest : public ::testing::Test { void AckNPackets(int n) { for (int i = 0; i < n; ++i) { - sender_.OnIncomingAck(acked_sequence_number_++, kMaxPacketSize, rtt_); + sender_.OnIncomingAck( + acked_sequence_number_++, kDefaultMaxPacketSize, rtt_); } } @@ -147,7 +148,7 @@ TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) { // We should now have our probe rate. QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -259,7 +260,7 @@ TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) { sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 3400, 100); - int64 max_rate = kMaxPacketSize * kNumMicrosPerSecond / + int64 max_rate = kDefaultMaxPacketSize * kNumMicrosPerSecond / one_ms_.ToMicroseconds(); int64 halfway_rate = probe_rate + (max_rate - probe_rate) / 2; @@ -325,7 +326,7 @@ TEST_F(InterArrivalSenderTest, DelaySpikeFollowedBySlowDrain) { // We should now have our probe rate. QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -422,7 +423,7 @@ TEST_F(InterArrivalSenderTest, DelaySpikeFollowedByImmediateDrain) { // We should now have our probe rate. QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -526,7 +527,7 @@ TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(81); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -539,7 +540,7 @@ TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); sender_.OnIncomingLoss(send_clock_.Now()); - sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_); + sender_.OnIncomingAck(acked_sequence_number_, kDefaultMaxPacketSize, rtt_); acked_sequence_number_ += 2; // Create a loss by not acking both packets. SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); } @@ -554,7 +555,7 @@ TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); sender_.OnIncomingLoss(send_clock_.Now()); - sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_); + sender_.OnIncomingAck(acked_sequence_number_, kDefaultMaxPacketSize, rtt_); acked_sequence_number_ += 2; // Create a loss by not acking both packets. SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); diff --git a/net/quic/congestion_control/paced_sender.cc b/net/quic/congestion_control/paced_sender.cc index 8de5437..9a5cc44 100644 --- a/net/quic/congestion_control/paced_sender.cc +++ b/net/quic/congestion_control/paced_sender.cc @@ -16,9 +16,14 @@ const int64 kMinPacketBurstSize = 2; // AvailableCongestionWindow. const int64 kMaxSchedulingDelayUs = 2000; -PacedSender::PacedSender(QuicBandwidth estimate) +PacedSender::PacedSender(QuicBandwidth estimate, QuicByteCount max_segment_size) : leaky_bucket_(estimate), - pace_(estimate) { + pace_(estimate), + max_segment_size_(kDefaultMaxPacketSize) { +} + +void PacedSender::set_max_segment_size(QuicByteCount max_segment_size) { + max_segment_size_ = max_segment_size; } void PacedSender::UpdateBandwidthEstimate(QuicTime now, @@ -39,7 +44,7 @@ QuicTime::Delta PacedSender::TimeUntilSend(QuicTime now, // Pace the data. QuicByteCount pacing_window = pace_.ToBytesPerPeriod( QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs)); - QuicByteCount min_window_size = kMinPacketBurstSize * kMaxPacketSize; + QuicByteCount min_window_size = kMinPacketBurstSize * max_segment_size_; pacing_window = std::max(pacing_window, min_window_size); if (pacing_window > leaky_bucket_.BytesPending(now)) { diff --git a/net/quic/congestion_control/paced_sender.h b/net/quic/congestion_control/paced_sender.h index 1f61585..6d7c429 100644 --- a/net/quic/congestion_control/paced_sender.h +++ b/net/quic/congestion_control/paced_sender.h @@ -17,7 +17,9 @@ namespace net { class NET_EXPORT_PRIVATE PacedSender { public: - explicit PacedSender(QuicBandwidth bandwidth_estimate); + PacedSender(QuicBandwidth bandwidth_estimate, QuicByteCount max_segment_size); + + void set_max_segment_size(QuicByteCount max_segment_size); // The estimated bandidth from the congestion algorithm changed. void UpdateBandwidthEstimate(QuicTime now, QuicBandwidth bandwidth_estimate); @@ -32,6 +34,7 @@ class NET_EXPORT_PRIVATE PacedSender { // Helper object to track the rate data can leave the buffer for pacing. LeakyBucket leaky_bucket_; QuicBandwidth pace_; + QuicByteCount max_segment_size_; DISALLOW_COPY_AND_ASSIGN(PacedSender); }; diff --git a/net/quic/congestion_control/paced_sender_test.cc b/net/quic/congestion_control/paced_sender_test.cc index b583315..fa42c2c 100644 --- a/net/quic/congestion_control/paced_sender_test.cc +++ b/net/quic/congestion_control/paced_sender_test.cc @@ -20,7 +20,8 @@ class PacedSenderTest : public ::testing::Test { PacedSenderTest() : zero_time_(QuicTime::Delta::Zero()), paced_sender_(new PacedSender( - QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS))) { + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS), + kDefaultMaxPacketSize)) { } const QuicTime::Delta zero_time_; @@ -32,10 +33,10 @@ TEST_F(PacedSenderTest, Basic) { paced_sender_->UpdateBandwidthEstimate(clock_.Now(), QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10)); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); - EXPECT_EQ(static_cast(kMaxPacketSize * 2), + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); + EXPECT_EQ(static_cast(kDefaultMaxPacketSize * 2), paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).ToMicroseconds()); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); @@ -46,10 +47,10 @@ TEST_F(PacedSenderTest, LowRate) { paced_sender_->UpdateBandwidthEstimate(clock_.Now(), QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS)); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); - EXPECT_EQ(static_cast(kMaxPacketSize * 20), + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); + EXPECT_EQ(static_cast(kDefaultMaxPacketSize * 20), paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).ToMicroseconds()); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); @@ -62,11 +63,11 @@ TEST_F(PacedSenderTest, HighRate) { paced_sender_->UpdateBandwidthEstimate(clock_.Now(), bandwidth_estimate); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); for (int i = 0; i < 16; ++i) { - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_TRUE(paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).IsZero()); } - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_EQ(2040, paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).ToMicroseconds()); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400)); diff --git a/net/quic/congestion_control/quic_congestion_control_test.cc b/net/quic/congestion_control/quic_congestion_control_test.cc index b4f33d2..a93263a 100644 --- a/net/quic/congestion_control/quic_congestion_control_test.cc +++ b/net/quic/congestion_control/quic_congestion_control_test.cc @@ -16,15 +16,6 @@ using std::max; namespace net { namespace test { -class QuicCongestionManagerPeer : public QuicCongestionManager { - public: - explicit QuicCongestionManagerPeer(const QuicClock* clock, - CongestionFeedbackType congestion_type) - : QuicCongestionManager(clock, congestion_type) { - } - using QuicCongestionManager::BandwidthEstimate; -}; - class QuicCongestionControlTest : public ::testing::Test { protected: QuicCongestionControlTest() @@ -32,12 +23,12 @@ class QuicCongestionControlTest : public ::testing::Test { } void SetUpCongestionType(CongestionFeedbackType congestion_type) { - manager_.reset(new QuicCongestionManagerPeer(&clock_, congestion_type)); + manager_.reset(new QuicCongestionManager(&clock_, congestion_type)); } MockClock clock_; QuicTime start_; - scoped_ptr manager_; + scoped_ptr manager_; }; TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) { @@ -49,8 +40,8 @@ TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) { clock_.Now()); EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(1, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + manager_->OnPacketSent(1, clock_.Now(), kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40), manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); @@ -79,8 +70,8 @@ TEST_F(QuicCongestionControlTest, FixedRatePacing) { for (QuicPacketSequenceNumber i = 1; i <= 100; ++i) { EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(i, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + manager_->OnPacketSent(i, clock_.Now(), kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); clock_.AdvanceTime(advance_time); @@ -110,11 +101,11 @@ TEST_F(QuicCongestionControlTest, Pacing) { for (QuicPacketSequenceNumber i = 1; i <= 100;) { EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(i++, clock_.Now(), kMaxPacketSize, + manager_->OnPacketSent(i++, clock_.Now(), kDefaultMaxPacketSize, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(i++, clock_.Now(), kMaxPacketSize, + manager_->OnPacketSent(i++, clock_.Now(), kDefaultMaxPacketSize, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc index ed133fc..d93d1ac 100644 --- a/net/quic/congestion_control/quic_congestion_manager.cc +++ b/net/quic/congestion_control/quic_congestion_manager.cc @@ -48,6 +48,19 @@ QuicCongestionManager::~QuicCongestionManager() { STLDeleteValues(&packet_history_map_); } +void QuicCongestionManager::SetFromConfig(const QuicConfig& config, + bool is_server) { + if (config.initial_round_trip_time_us() > 0 && + current_rtt_.IsInfinite()) { + // The initial rtt should already be set on the client side. + DLOG_IF(INFO, !is_server) + << "Client did not set an initial RTT, but did negotiate one."; + current_rtt_ = + QuicTime::Delta::FromMicroseconds(config.initial_round_trip_time_us()); + } + send_algorithm_->SetFromConfig(config, is_server); +} + void QuicCongestionManager::OnPacketSent( QuicPacketSequenceNumber sequence_number, QuicTime sent_time, diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h index 6d15719..27d6392 100644 --- a/net/quic/congestion_control/quic_congestion_manager.h +++ b/net/quic/congestion_control/quic_congestion_manager.h @@ -32,6 +32,8 @@ class NET_EXPORT_PRIVATE QuicCongestionManager { CongestionFeedbackType congestion_type); virtual ~QuicCongestionManager(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server); + // Called when we have received an ack frame from peer. virtual void OnIncomingAckFrame(const QuicAckFrame& frame, QuicTime ack_receive_time); diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h index 8ff3961..59f6ad0 100644 --- a/net/quic/congestion_control/send_algorithm_interface.h +++ b/net/quic/congestion_control/send_algorithm_interface.h @@ -13,6 +13,7 @@ #include "net/base/net_export.h" #include "net/quic/quic_bandwidth.h" #include "net/quic/quic_clock.h" +#include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" @@ -41,6 +42,8 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { virtual ~SendAlgorithmInterface() {} + virtual void SetFromConfig(const QuicConfig& config, bool is_server) = 0; + // Called when we receive congestion feedback from remote peer. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 0a7a897..6ff93a7 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -51,6 +51,14 @@ TcpCubicSender::~TcpCubicSender() { UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_); } +void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) { + if (is_server) { + // Set the initial window size. + // Ignoring the max packet size and always using TCP's default MSS. + congestion_window_ = config.server_initial_congestion_window(); + } +} + void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, @@ -206,7 +214,7 @@ bool TcpCubicSender::IsCwndLimited() const { return left <= tcp_max_burst; } -// Called when we receive and ack. Normal TCP tracks how many packets one ack +// Called when we receive an ack. Normal TCP tracks how many packets one ack // represents, but quic has a separate ack for each packet. void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) { if (!IsCwndLimited()) { @@ -254,6 +262,8 @@ void TcpCubicSender::OnTimeOut() { void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { if (rtt.IsInfinite() || rtt.IsZero()) { + DLOG(INFO) << "Ignoring rtt, because it's " + << (rtt.IsZero() ? "Zero" : "Infinite"); return; } // RTT can't be negative. @@ -278,7 +288,8 @@ void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { smoothed_rtt_ = QuicTime::Delta::FromMicroseconds( kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() + kAlpha * rtt.ToMicroseconds()); - DLOG(INFO) << "Cubic; mean_deviation_:" << mean_deviation_.ToMicroseconds(); + DLOG(INFO) << "Cubic; smoothed_rtt_:" << smoothed_rtt_.ToMicroseconds() + << " mean_deviation_:" << mean_deviation_.ToMicroseconds(); } // Hybrid start triggers when cwnd is larger than some threshold. diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h index cf5f2ff..2c2e698 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.h +++ b/net/quic/congestion_control/tcp_cubic_sender.h @@ -35,6 +35,8 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { QuicTcpCongestionWindow max_tcp_congestion_window); virtual ~TcpCubicSender(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; + // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index 15845cd..2d495d1 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -13,6 +13,7 @@ namespace net { namespace test { const uint32 kDefaultWindowTCP = 10 * kDefaultTCPMSS; + // TODO(ianswett): Remove 10000 once b/10075719 is fixed. const QuicTcpCongestionWindow kDefaultMaxCongestionWindowTCP = 10000; @@ -23,6 +24,11 @@ class TcpCubicSenderPeer : public TcpCubicSender { QuicTcpCongestionWindow max_tcp_congestion_window) : TcpCubicSender(clock, reno, max_tcp_congestion_window) { } + + QuicTcpCongestionWindow congestion_window() { + return congestion_window_; + } + using TcpCubicSender::AvailableSendWindow; using TcpCubicSender::SendWindow; using TcpCubicSender::AckAccounting; @@ -371,5 +377,16 @@ TEST_F(TcpCubicSenderTest, SendWindowNotAffectedByAcks) { EXPECT_GT(send_window, sender_->AvailableSendWindow()); } +TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) { + QuicTcpCongestionWindow congestion_window = sender_->congestion_window(); + QuicConfig config; + config.set_server_initial_congestion_window(2 * congestion_window, + 2 * congestion_window); + EXPECT_EQ(2 * congestion_window, config.server_initial_congestion_window()); + + sender_->SetFromConfig(config, true); + EXPECT_EQ(2 * congestion_window, sender_->congestion_window()); +} + } // namespace test } // namespace net diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc index cc9bf35..394971a7 100644 --- a/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc +++ b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc @@ -4,6 +4,7 @@ #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" +#include #include #include @@ -19,6 +20,18 @@ const size_t kKeySize = 16; const size_t kNoncePrefixSize = 4; const size_t kAESNonceSize = 12; +void ClearOpenSslErrors() { +#ifdef NDEBUG + while (ERR_get_error()) {} +#else + while (long error = ERR_get_error()) { + char buf[120]; + ERR_error_string_n(error, buf, arraysize(buf)); + DLOG(ERROR) << "OpenSSL error: " << buf; + } +#endif +} + } // namespace Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {} @@ -35,12 +48,14 @@ bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { // Set the cipher type and the key. if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_, NULL) == 0) { + ClearOpenSslErrors(); return false; } // Set the IV (nonce) length. if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize, NULL) == 0) { + ClearOpenSslErrors(); return false; } @@ -69,6 +84,7 @@ bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, if (EVP_EncryptInit_ex( ctx_.get(), NULL, NULL, NULL, reinterpret_cast(nonce.data())) == 0) { + ClearOpenSslErrors(); return false; } @@ -82,6 +98,7 @@ bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, ctx_.get(), NULL, &unused_len, reinterpret_cast(associated_data.data()), associated_data.size()) == 0) { + ClearOpenSslErrors(); return false; } } @@ -91,17 +108,20 @@ bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, ctx_.get(), output, &len, reinterpret_cast(plaintext.data()), plaintext.size()) == 0) { + ClearOpenSslErrors(); return false; } output += len; if (EVP_EncryptFinal_ex(ctx_.get(), output, &len) == 0) { + ClearOpenSslErrors(); return false; } output += len; if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize, output) == 0) { + ClearOpenSslErrors(); return false; } diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 0f3bb78..c869557 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -7,34 +7,22 @@ #include #include "base/memory/scoped_ptr.h" -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "crypto/secure_hash.h" #include "net/base/net_util.h" -#include "net/quic/crypto/cert_compressor.h" -#include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/common_cert_set.h" #include "net/quic/crypto/crypto_framer.h" -#include "net/quic/crypto/crypto_utils.h" -#include "net/quic/crypto/curve25519_key_exchange.h" #include "net/quic/crypto/key_exchange.h" -#include "net/quic/crypto/p256_key_exchange.h" -#include "net/quic/crypto/proof_verifier.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_utils.h" -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif - using base::StringPiece; using base::StringPrintf; -using std::map; using std::string; using std::vector; @@ -348,606 +336,4 @@ QuicCryptoConfig::QuicCryptoConfig() QuicCryptoConfig::~QuicCryptoConfig() {} -QuicCryptoClientConfig::QuicCryptoClientConfig() {} - -QuicCryptoClientConfig::~QuicCryptoClientConfig() { - STLDeleteValues(&cached_states_); -} - -QuicCryptoClientConfig::CachedState::CachedState() - : server_config_valid_(false), - generation_counter_(0) {} - -QuicCryptoClientConfig::CachedState::~CachedState() {} - -bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { - if (server_config_.empty() || !server_config_valid_) { - return false; - } - - const CryptoHandshakeMessage* scfg = GetServerConfig(); - if (!scfg) { - // Should be impossible short of cache corruption. - DCHECK(false); - return false; - } - - uint64 expiry_seconds; - if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR || - now.ToUNIXSeconds() >= expiry_seconds) { - return false; - } - - return true; -} - -const CryptoHandshakeMessage* -QuicCryptoClientConfig::CachedState::GetServerConfig() const { - if (server_config_.empty()) { - return NULL; - } - - if (!scfg_.get()) { - scfg_.reset(CryptoFramer::ParseMessage(server_config_)); - DCHECK(scfg_.get()); - } - return scfg_.get(); -} - -QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig( - StringPiece server_config, QuicWallTime now, string* error_details) { - const bool matches_existing = server_config == server_config_; - - // Even if the new server config matches the existing one, we still wish to - // reject it if it has expired. - scoped_ptr new_scfg_storage; - const CryptoHandshakeMessage* new_scfg; - - if (!matches_existing) { - new_scfg_storage.reset(CryptoFramer::ParseMessage(server_config)); - new_scfg = new_scfg_storage.get(); - } else { - new_scfg = GetServerConfig(); - } - - if (!new_scfg) { - *error_details = "SCFG invalid"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - uint64 expiry_seconds; - if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { - *error_details = "SCFG missing EXPY"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (now.ToUNIXSeconds() >= expiry_seconds) { - *error_details = "SCFG has expired"; - return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; - } - - if (!matches_existing) { - server_config_ = server_config.as_string(); - SetProofInvalid(); - scfg_.reset(new_scfg_storage.release()); - } - return QUIC_NO_ERROR; -} - -void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { - server_config_.clear(); - scfg_.reset(); - SetProofInvalid(); -} - -void QuicCryptoClientConfig::CachedState::SetProof(const vector& certs, - StringPiece signature) { - bool has_changed = - signature != server_config_sig_ || certs_.size() != certs.size(); - - if (!has_changed) { - for (size_t i = 0; i < certs_.size(); i++) { - if (certs_[i] != certs[i]) { - has_changed = true; - break; - } - } - } - - if (!has_changed) { - return; - } - - // If the proof has changed then it needs to be revalidated. - SetProofInvalid(); - certs_ = certs; - server_config_sig_ = signature.as_string(); -} - -void QuicCryptoClientConfig::CachedState::ClearProof() { - SetProofInvalid(); - certs_.clear(); - server_config_sig_.clear(); -} - -void QuicCryptoClientConfig::CachedState::SetProofValid() { - server_config_valid_ = true; -} - -void QuicCryptoClientConfig::CachedState::SetProofInvalid() { - server_config_valid_ = false; - ++generation_counter_; -} - -const string& QuicCryptoClientConfig::CachedState::server_config() const { - return server_config_; -} - -const string& -QuicCryptoClientConfig::CachedState::source_address_token() const { - return source_address_token_; -} - -const vector& QuicCryptoClientConfig::CachedState::certs() const { - return certs_; -} - -const string& QuicCryptoClientConfig::CachedState::signature() const { - return server_config_sig_; -} - -bool QuicCryptoClientConfig::CachedState::proof_valid() const { - return server_config_valid_; -} - -uint64 QuicCryptoClientConfig::CachedState::generation_counter() const { - return generation_counter_; -} - -const ProofVerifyDetails* -QuicCryptoClientConfig::CachedState::proof_verify_details() const { - return proof_verify_details_.get(); -} - -void QuicCryptoClientConfig::CachedState::set_source_address_token( - StringPiece token) { - source_address_token_ = token.as_string(); -} - -void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails( - ProofVerifyDetails* details) { - proof_verify_details_.reset(details); -} - -void QuicCryptoClientConfig::CachedState::InitializeFrom( - const QuicCryptoClientConfig::CachedState& other) { - DCHECK(server_config_.empty()); - DCHECK(!server_config_valid_); - server_config_ = other.server_config_; - source_address_token_ = other.source_address_token_; - certs_ = other.certs_; - server_config_sig_ = other.server_config_sig_; - server_config_valid_ = other.server_config_valid_; -} - -void QuicCryptoClientConfig::SetDefaults() { - // Version must be 0. - // TODO(agl): this version stuff is obsolete now. - version = QuicCryptoConfig::CONFIG_VERSION; - - // Key exchange methods. - kexs.resize(2); - kexs[0] = kC255; - kexs[1] = kP256; - - // Authenticated encryption algorithms. - aead.resize(1); - aead[0] = kAESG; -} - -QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( - const string& server_hostname) { - map::const_iterator it = - cached_states_.find(server_hostname); - if (it != cached_states_.end()) { - return it->second; - } - - CachedState* cached = new CachedState; - cached_states_.insert(make_pair(server_hostname, cached)); - return cached; -} - -void QuicCryptoClientConfig::FillInchoateClientHello( - const string& server_hostname, - const CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out) const { - out->set_tag(kCHLO); - out->set_minimum_size(kClientHelloMinimumSize); - - // Server name indication. We only send SNI if it's a valid domain name, as - // per the spec. - if (CryptoUtils::IsValidSNI(server_hostname)) { - out->SetStringPiece(kSNI, server_hostname); - } - out->SetValue(kVERS, version); - - if (!cached->source_address_token().empty()) { - out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); - } - - if (proof_verifier_.get()) { - // Don't request ECDSA proofs on platforms that do not support ECDSA - // certificates. - bool disableECDSA = false; -#if defined(OS_WIN) - if (base::win::GetVersion() < base::win::VERSION_VISTA) - disableECDSA = true; -#endif - if (disableECDSA) { - out->SetTaglist(kPDMD, kX59R, 0); - } else { - out->SetTaglist(kPDMD, kX509, 0); - } - - if (!cached->proof_valid()) { - // If we are expecting a certificate chain, double the size of the client - // hello so that the response from the server can be larger - hopefully - // including the whole certificate chain. - out->set_minimum_size(kClientHelloMinimumSize * 2); - } - } - - if (common_cert_sets) { - out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); - } - - const vector& certs = cached->certs(); - // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the - // client config is being used for multiple connections, another connection - // doesn't update the cached certificates and cause us to be unable to - // process the server's compressed certificate chain. - out_params->cached_certs = certs; - if (!certs.empty()) { - vector hashes; - hashes.reserve(certs.size()); - for (vector::const_iterator i = certs.begin(); - i != certs.end(); ++i) { - hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); - } - out->SetVector(kCCRT, hashes); - } -} - -QuicErrorCode QuicCryptoClientConfig::FillClientHello( - const string& server_hostname, - QuicGuid guid, - const CachedState* cached, - QuicWallTime now, - QuicRandom* rand, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out, - string* error_details) const { - DCHECK(error_details != NULL); - - FillInchoateClientHello(server_hostname, cached, out_params, out); - - const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); - if (!scfg) { - // This should never happen as our caller should have checked - // cached->IsComplete() before calling this function. - *error_details = "Handshake not ready"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - - StringPiece scid; - if (!scfg->GetStringPiece(kSCID, &scid)) { - *error_details = "SCFG missing SCID"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - out->SetStringPiece(kSCID, scid); - - const QuicTag* their_aeads; - const QuicTag* their_key_exchanges; - size_t num_their_aeads, num_their_key_exchanges; - if (scfg->GetTaglist(kAEAD, &their_aeads, - &num_their_aeads) != QUIC_NO_ERROR || - scfg->GetTaglist(kKEXS, &their_key_exchanges, - &num_their_key_exchanges) != QUIC_NO_ERROR) { - *error_details = "Missing AEAD or KEXS"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - size_t key_exchange_index; - if (!QuicUtils::FindMutualTag( - aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY, - &out_params->aead, NULL) || - !QuicUtils::FindMutualTag( - kexs, their_key_exchanges, num_their_key_exchanges, - QuicUtils::PEER_PRIORITY, &out_params->key_exchange, - &key_exchange_index)) { - *error_details = "Unsupported AEAD or KEXS"; - return QUIC_CRYPTO_NO_SUPPORT; - } - out->SetTaglist(kAEAD, out_params->aead, 0); - out->SetTaglist(kKEXS, out_params->key_exchange, 0); - - StringPiece public_value; - if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) != - QUIC_NO_ERROR) { - *error_details = "Missing public value"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - StringPiece orbit; - if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) { - *error_details = "SCFG missing OBIT"; - return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } - - CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce); - out->SetStringPiece(kNONC, out_params->client_nonce); - if (!out_params->server_nonce.empty()) { - out->SetStringPiece(kServerNonceTag, out_params->server_nonce); - } - - switch (out_params->key_exchange) { - case kC255: - out_params->client_key_exchange.reset(Curve25519KeyExchange::New( - Curve25519KeyExchange::NewPrivateKey(rand))); - break; - case kP256: - out_params->client_key_exchange.reset(P256KeyExchange::New( - P256KeyExchange::NewPrivateKey())); - break; - default: - DCHECK(false); - *error_details = "Configured to support an unknown key exchange"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - - if (!out_params->client_key_exchange->CalculateSharedKey( - public_value, &out_params->initial_premaster_secret)) { - *error_details = "Key exchange failure"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); - - bool do_channel_id = false; - if (channel_id_signer_.get()) { - const QuicTag* their_proof_demands; - size_t num_their_proof_demands; - if (scfg->GetTaglist(kPDMD, &their_proof_demands, - &num_their_proof_demands) == QUIC_NO_ERROR) { - for (size_t i = 0; i < num_their_proof_demands; i++) { - if (their_proof_demands[i] == kCHID) { - do_channel_id = true; - break; - } - } - } - } - - if (do_channel_id) { - // In order to calculate the encryption key for the CETV block we need to - // serialise the client hello as it currently is (i.e. without the CETV - // block). For this, the client hello is serialized without padding. - const size_t orig_min_size = out->minimum_size(); - out->set_minimum_size(0); - - CryptoHandshakeMessage cetv; - cetv.set_tag(kCETV); - - string hkdf_input; - const QuicData& client_hello_serialized = out->GetSerialized(); - hkdf_input.append(QuicCryptoConfig::kCETVLabel, - strlen(QuicCryptoConfig::kCETVLabel) + 1); - hkdf_input.append(reinterpret_cast(&guid), sizeof(guid)); - hkdf_input.append(client_hello_serialized.data(), - client_hello_serialized.length()); - hkdf_input.append(cached->server_config()); - - string key, signature; - if (!channel_id_signer_->Sign(server_hostname, hkdf_input, - &key, &signature)) { - *error_details = "Channel ID signature failed"; - return QUIC_INVALID_CHANNEL_ID_SIGNATURE; - } - - cetv.SetStringPiece(kCIDK, key); - cetv.SetStringPiece(kCIDS, signature); - - CrypterPair crypters; - if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, - out_params->aead, out_params->client_nonce, - out_params->server_nonce, hkdf_input, - CryptoUtils::CLIENT, &crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - const QuicData& cetv_plaintext = cetv.GetSerialized(); - scoped_ptr cetv_ciphertext(crypters.encrypter->EncryptPacket( - 0 /* sequence number */, - StringPiece() /* associated data */, - cetv_plaintext.AsStringPiece())); - if (!cetv_ciphertext.get()) { - *error_details = "Packet encryption failed"; - return QUIC_ENCRYPTION_FAILURE; - } - - out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece()); - out->MarkDirty(); - - out->set_minimum_size(orig_min_size); - } - - out_params->hkdf_input_suffix.clear(); - out_params->hkdf_input_suffix.append(reinterpret_cast(&guid), - sizeof(guid)); - const QuicData& client_hello_serialized = out->GetSerialized(); - out_params->hkdf_input_suffix.append(client_hello_serialized.data(), - client_hello_serialized.length()); - out_params->hkdf_input_suffix.append(cached->server_config()); - - string hkdf_input; - const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; - hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); - hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); - hkdf_input.append(out_params->hkdf_input_suffix); - - if (!CryptoUtils::DeriveKeys( - out_params->initial_premaster_secret, out_params->aead, - out_params->client_nonce, out_params->server_nonce, hkdf_input, - CryptoUtils::CLIENT, &out_params->initial_crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - return QUIC_NO_ERROR; -} - -QuicErrorCode QuicCryptoClientConfig::ProcessRejection( - const CryptoHandshakeMessage& rej, - QuicWallTime now, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - string* error_details) { - DCHECK(error_details != NULL); - - if (rej.tag() != kREJ) { - *error_details = "Message is not REJ"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - - StringPiece scfg; - if (!rej.GetStringPiece(kSCFG, &scfg)) { - *error_details = "Missing SCFG"; - return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } - - QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details); - if (error != QUIC_NO_ERROR) { - return error; - } - - StringPiece token; - if (rej.GetStringPiece(kSourceAddressTokenTag, &token)) { - cached->set_source_address_token(token); - } - - StringPiece nonce; - if (rej.GetStringPiece(kServerNonceTag, &nonce)) { - out_params->server_nonce = nonce.as_string(); - } - - StringPiece proof, cert_bytes; - bool has_proof = rej.GetStringPiece(kPROF, &proof); - bool has_cert = rej.GetStringPiece(kCertificateTag, &cert_bytes); - if (has_proof && has_cert) { - vector certs; - if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs, - common_cert_sets, &certs)) { - *error_details = "Certificate data invalid"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - cached->SetProof(certs, proof); - } else { - cached->ClearProof(); - if (has_proof && !has_cert) { - *error_details = "Certificate missing"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (!has_proof && has_cert) { - *error_details = "Proof missing"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - } - - return QUIC_NO_ERROR; -} - -QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( - const CryptoHandshakeMessage& server_hello, - QuicGuid guid, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - string* error_details) { - DCHECK(error_details != NULL); - - if (server_hello.tag() != kSHLO) { - *error_details = "Bad tag"; - return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; - } - - // Learn about updated source address tokens. - StringPiece token; - if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) { - cached->set_source_address_token(token); - } - - // TODO(agl): - // learn about updated SCFGs. - - StringPiece public_value; - if (!server_hello.GetStringPiece(kPUBS, &public_value)) { - *error_details = "server hello missing forward secure public value"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (!out_params->client_key_exchange->CalculateSharedKey( - public_value, &out_params->forward_secure_premaster_secret)) { - *error_details = "Key exchange failure"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - string hkdf_input; - const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; - hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); - hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len); - hkdf_input.append(out_params->hkdf_input_suffix); - - if (!CryptoUtils::DeriveKeys( - out_params->forward_secure_premaster_secret, out_params->aead, - out_params->client_nonce, out_params->server_nonce, hkdf_input, - CryptoUtils::CLIENT, &out_params->forward_secure_crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - return QUIC_NO_ERROR; -} - -ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { - return proof_verifier_.get(); -} - -void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { - proof_verifier_.reset(verifier); -} - -ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const { - return channel_id_signer_.get(); -} - -void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) { - channel_id_signer_.reset(signer); -} - -void QuicCryptoClientConfig::InitializeFrom( - const std::string& server_hostname, - const std::string& canonical_server_hostname, - QuicCryptoClientConfig* canonical_crypto_config) { - CachedState* canonical_cached = - canonical_crypto_config->LookupOrCreate(canonical_server_hostname); - if (!canonical_cached->proof_valid()) { - return; - } - CachedState* cached = LookupOrCreate(server_hostname); - cached->InitializeFrom(*canonical_cached); -} - } // namespace net diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index 4fefba5..c520294 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -225,192 +225,6 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { DISALLOW_COPY_AND_ASSIGN(QuicCryptoConfig); }; -// QuicCryptoClientConfig contains crypto-related configuration settings for a -// client. Note that this object isn't thread-safe. It's designed to be used on -// a single thread at a time. -class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { - public: - // A CachedState contains the information that the client needs in order to - // perform a 0-RTT handshake with a server. This information can be reused - // over several connections to the same server. - class NET_EXPORT_PRIVATE CachedState { - public: - CachedState(); - ~CachedState(); - - // IsComplete returns true if this object contains enough information to - // perform a handshake with the server. |now| is used to judge whether any - // cached server config has expired. - bool IsComplete(QuicWallTime now) const; - - // GetServerConfig returns the parsed contents of |server_config|, or NULL - // if |server_config| is empty. The return value is owned by this object - // and is destroyed when this object is. - const CryptoHandshakeMessage* GetServerConfig() const; - - // SetServerConfig checks that |server_config| parses correctly and stores - // it in |server_config_|. |now| is used to judge whether |server_config| - // has expired. - QuicErrorCode SetServerConfig(base::StringPiece server_config, - QuicWallTime now, - std::string* error_details); - - // InvalidateServerConfig clears the cached server config (if any). - void InvalidateServerConfig(); - - // SetProof stores a certificate chain and signature. - void SetProof(const std::vector& certs, - base::StringPiece signature); - - // Clears the certificate chain and signature and invalidates the proof. - void ClearProof(); - - // SetProofValid records that the certificate chain and signature have been - // validated and that it's safe to assume that the server is legitimate. - // (Note: this does not check the chain or signature.) - void SetProofValid(); - - // If the server config or the proof has changed then it needs to be - // revalidated. Helper function to keep server_config_valid_ and - // generation_counter_ in sync. - void SetProofInvalid(); - - const std::string& server_config() const; - const std::string& source_address_token() const; - const std::vector& certs() const; - const std::string& signature() const; - bool proof_valid() const; - uint64 generation_counter() const; - const ProofVerifyDetails* proof_verify_details() const; - - void set_source_address_token(base::StringPiece token); - - // SetProofVerifyDetails takes ownership of |details|. - void SetProofVerifyDetails(ProofVerifyDetails* details); - - // Copy the |server_config_|, |source_address_token_|, |certs_| and - // |server_config_sig_| from the |other|. The remaining fields, - // |generation_counter_|, |proof_verify_details_|, and |scfg_| remain - // unchanged. - void InitializeFrom(const CachedState& other); - - private: - std::string server_config_id_; // An opaque id from the server. - std::string server_config_; // A serialized handshake message. - std::string source_address_token_; // An opaque proof of IP ownership. - std::vector certs_; // A list of certificates in leaf-first - // order. - std::string server_config_sig_; // A signature of |server_config_|. - bool server_config_valid_; // True if |server_config_| is correctly - // signed and |certs_| has been - // validated. - // Generation counter associated with the |server_config_|, |certs_| and - // |server_config_sig_| combination. It is incremented whenever we set - // server_config_valid_ to false. - uint64 generation_counter_; - - scoped_ptr proof_verify_details_; - - // scfg contains the cached, parsed value of |server_config|. - mutable scoped_ptr scfg_; - - DISALLOW_COPY_AND_ASSIGN(CachedState); - }; - - QuicCryptoClientConfig(); - ~QuicCryptoClientConfig(); - - // Sets the members to reasonable, default values. - void SetDefaults(); - - // LookupOrCreate returns a CachedState for the given hostname. If no such - // CachedState currently exists, it will be created and cached. - CachedState* LookupOrCreate(const std::string& server_hostname); - - // FillInchoateClientHello sets |out| to be a CHLO message that elicits a - // source-address token or SCFG from a server. If |cached| is non-NULL, the - // source-address token will be taken from it. |out_params| is used in order - // to store the cached certs that were sent as hints to the server in - // |out_params->cached_certs|. - void FillInchoateClientHello(const std::string& server_hostname, - const CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out) const; - - // FillClientHello sets |out| to be a CHLO message based on the configuration - // of this object. This object must have cached enough information about - // |server_hostname| in order to perform a handshake. This can be checked - // with the |IsComplete| member of |CachedState|. - // - // |clock| and |rand| are used to generate the nonce and |out_params| is - // filled with the results of the handshake that the server is expected to - // accept. - QuicErrorCode FillClientHello(const std::string& server_hostname, - QuicGuid guid, - const CachedState* cached, - QuicWallTime now, - QuicRandom* rand, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out, - std::string* error_details) const; - - // ProcessRejection processes a REJ message from a server and updates the - // cached information about that server. After this, |IsComplete| may return - // true for that server's CachedState. If the rejection message contains - // state about a future handshake (i.e. an nonce value from the server), then - // it will be saved in |out_params|. |now| is used to judge whether the - // server config in the rejection message has expired. - QuicErrorCode ProcessRejection(const CryptoHandshakeMessage& rej, - QuicWallTime now, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - std::string* error_details); - - // ProcessServerHello processes the message in |server_hello|, updates the - // cached information about that server, writes the negotiated parameters to - // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable - // then it puts an error message in |error_details| and returns an error - // code. - QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, - QuicGuid guid, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - std::string* error_details); - - ProofVerifier* proof_verifier() const; - - // SetProofVerifier takes ownership of a |ProofVerifier| that clients are - // free to use in order to verify certificate chains from servers. If a - // ProofVerifier is set then the client will request a certificate chain from - // the server. - void SetProofVerifier(ProofVerifier* verifier); - - ChannelIDSigner* channel_id_signer() const; - - // SetChannelIDSigner sets a ChannelIDSigner that will be called when the - // server supports channel IDs to sign a message proving possession of the - // given ChannelID. This object takes ownership of |signer|. - void SetChannelIDSigner(ChannelIDSigner* signer); - - // Initialize the CachedState from |canonical_crypto_config| for the - // |canonical_server_hostname| as the initial CachedState for - // |server_hostname|. We will copy config data only if - // |canonical_crypto_config| has valid proof. - void InitializeFrom(const std::string& server_hostname, - const std::string& canonical_server_hostname, - QuicCryptoClientConfig* canonical_crypto_config); - - private: - // cached_states_ maps from the server hostname to the cached information - // about that server. - std::map cached_states_; - - scoped_ptr proof_verifier_; - scoped_ptr channel_id_signer_; - - DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig); -}; - } // namespace net #endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc deleted file mode 100644 index dcde964..0000000 --- a/net/quic/crypto/crypto_handshake_test.cc +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/crypto/crypto_handshake.h" - -#include - -#include "base/stl_util.h" -#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include "net/quic/crypto/crypto_server_config.h" -#include "net/quic/crypto/crypto_server_config_protobuf.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::make_pair; -using std::map; -using std::pair; -using std::string; -using std::vector; - -namespace net { -namespace test { - -class QuicCryptoServerConfigPeer { - public: - explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config) - : server_config_(server_config) {} - - string NewSourceAddressToken(IPEndPoint ip, - QuicRandom* rand, - QuicWallTime now) { - return server_config_->NewSourceAddressToken(ip, rand, now); - } - - bool ValidateSourceAddressToken(StringPiece srct, - IPEndPoint ip, - QuicWallTime now) { - return server_config_->ValidateSourceAddressToken(srct, ip, now); - } - - // CheckConfigs compares the state of the Configs in |server_config_| to the - // description given as arguments. The arguments are given as NULL-terminated - // pairs. The first of each pair is the server config ID of a Config. The - // second is a boolean describing whether the config is the primary. For - // example: - // CheckConfigs(NULL); // checks that no Configs are loaded. - // - // // Checks that exactly three Configs are loaded with the given IDs and - // // status. - // CheckConfigs( - // "id1", false, - // "id2", true, - // "id3", false, - // NULL); - void CheckConfigs(const char* server_config_id1, ...) { - va_list ap; - va_start(ap, server_config_id1); - - vector > expected; - bool first = true; - for (;;) { - const char* server_config_id; - if (first) { - server_config_id = server_config_id1; - first = false; - } else { - server_config_id = va_arg(ap, const char*); - } - - if (!server_config_id) { - break; - } - - // varargs will promote the value to an int so we have to read that from - // the stack and cast down. - const bool is_primary = static_cast(va_arg(ap, int)); - expected.push_back(make_pair(server_config_id, is_primary)); - } - - va_end(ap); - - base::AutoLock locked(server_config_->configs_lock_); - - ASSERT_EQ(expected.size(), server_config_->configs_.size()) - << ConfigsDebug(); - - for (QuicCryptoServerConfig::ConfigMap::const_iterator - i = server_config_->configs_.begin(); - i != server_config_->configs_.end(); ++i) { - bool found = false; - for (vector >::iterator j = expected.begin(); - j != expected.end(); ++j) { - if (i->first == j->first && i->second->is_primary == j->second) { - found = true; - j->first.clear(); - break; - } - } - - ASSERT_TRUE(found) << "Failed to find match for " << i->first - << " in configs:\n" << ConfigsDebug(); - } - } - - // ConfigsDebug returns a string that contains debugging information about - // the set of Configs loaded in |server_config_| and their status. - // ConfigsDebug() should be called after acquiring - // server_config_->configs_lock_. - string ConfigsDebug() { - if (server_config_->configs_.empty()) { - return "No Configs in QuicCryptoServerConfig"; - } - - string s; - - for (QuicCryptoServerConfig::ConfigMap::const_iterator - i = server_config_->configs_.begin(); - i != server_config_->configs_.end(); ++i) { - const scoped_refptr config = i->second; - if (config->is_primary) { - s += "(primary) "; - } else { - s += " "; - } - s += config->id; - s += "\n"; - } - - return s; - } - - void SelectNewPrimaryConfig(int seconds) { - base::AutoLock locked(server_config_->configs_lock_); - server_config_->SelectNewPrimaryConfig( - QuicWallTime::FromUNIXSeconds(seconds)); - } - - private: - const QuicCryptoServerConfig* server_config_; -}; - -TEST(QuicCryptoServerConfigTest, ServerConfig) { - QuicRandom* rand = QuicRandom::GetInstance(); - QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); - MockClock clock; - - scoped_ptr( - server.AddDefaultConfig(rand, &clock, - QuicCryptoServerConfig::ConfigOptions())); -} - -TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { - QuicRandom* rand = QuicRandom::GetInstance(); - QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); - 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); - MockClock clock; - clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); - QuicCryptoServerConfigPeer peer(&server); - - QuicWallTime now = clock.WallNow(); - const QuicWallTime original_time = now; - - const string token4 = peer.NewSourceAddressToken(ip4, rand, now); - const string token6 = peer.NewSourceAddressToken(ip6, rand, now); - 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)); -} - -class CryptoServerConfigsTest : public ::testing::Test { - public: - CryptoServerConfigsTest() - : rand_(QuicRandom::GetInstance()), - config_(QuicCryptoServerConfig::TESTING, rand_), - test_peer_(&config_) {} - - virtual void SetUp() { - clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000)); - } - - // SetConfigs constructs suitable config protobufs and calls SetConfigs on - // |config_|. The arguments are given as NULL-terminated pairs. The first of - // each pair is the server config ID of a Config. The second is the - // |primary_time| of that Config, given in epoch seconds. (Although note - // that, in these tests, time is set to 1000 seconds since the epoch.) For - // example: - // SetConfigs(NULL); // calls |config_.SetConfigs| with no protobufs. - // - // // Calls |config_.SetConfigs| with two protobufs: one for a Config with - // // a |primary_time| of 900, and another with a |primary_time| of 1000. - // CheckConfigs( - // "id1", 900, - // "id2", 1000, - // NULL); - // - // If the server config id starts with "INVALID" then the generated protobuf - // will be invalid. - void SetConfigs(const char* server_config_id1, ...) { - va_list ap; - va_start(ap, server_config_id1); - bool has_invalid = false; - - vector protobufs; - bool first = true; - for (;;) { - const char* server_config_id; - if (first) { - server_config_id = server_config_id1; - first = false; - } else { - server_config_id = va_arg(ap, const char*); - } - - if (!server_config_id) { - break; - } - - int primary_time = va_arg(ap, int); - - QuicCryptoServerConfig::ConfigOptions options; - options.id = server_config_id; - QuicServerConfigProtobuf* protobuf( - QuicCryptoServerConfig::DefaultConfig(rand_, &clock_, options)); - protobuf->set_primary_time(primary_time); - if (string(server_config_id).find("INVALID") == 0) { - protobuf->clear_key(); - has_invalid = true; - } - protobufs.push_back(protobuf); - } - - ASSERT_EQ(!has_invalid, config_.SetConfigs(protobufs, clock_.WallNow())); - STLDeleteElements(&protobufs); - } - - protected: - QuicRandom* const rand_; - MockClock clock_; - QuicCryptoServerConfig config_; - QuicCryptoServerConfigPeer test_peer_; -}; - -TEST_F(CryptoServerConfigsTest, NoConfigs) { - test_peer_.CheckConfigs(NULL); -} - -TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) { - // Make sure that "b" is primary even though "a" comes first. - SetConfigs("a", 1100, - "b", 900, - NULL); - test_peer_.CheckConfigs( - "a", false, - "b", true, - NULL); -} - -TEST_F(CryptoServerConfigsTest, MakePrimarySecond) { - // Make sure that a remains primary after b is added. - SetConfigs("a", 900, - "b", 1100, - NULL); - test_peer_.CheckConfigs( - "a", true, - "b", false, - NULL); -} - -TEST_F(CryptoServerConfigsTest, Delete) { - // Ensure that configs get deleted when removed. - SetConfigs("a", 800, - "b", 900, - "c", 1100, - NULL); - SetConfigs("b", 900, - "c", 1100, - NULL); - test_peer_.CheckConfigs( - "b", true, - "c", false, - NULL); -} - -TEST_F(CryptoServerConfigsTest, DontDeletePrimary) { - // Ensure that the primary config isn't deleted when removed. - SetConfigs("a", 800, - "b", 900, - "c", 1100, - NULL); - SetConfigs("a", 800, - "c", 1100, - NULL); - test_peer_.CheckConfigs( - "a", false, - "b", true, - "c", false, - NULL); -} - -TEST_F(CryptoServerConfigsTest, AdvancePrimary) { - // Check that a new primary config is enabled at the right time. - SetConfigs("a", 900, - "b", 1100, - NULL); - test_peer_.SelectNewPrimaryConfig(1000); - test_peer_.CheckConfigs( - "a", true, - "b", false, - NULL); - test_peer_.SelectNewPrimaryConfig(1101); - test_peer_.CheckConfigs( - "a", false, - "b", true, - NULL); -} - -TEST_F(CryptoServerConfigsTest, InvalidConfigs) { - // Ensure that invalid configs don't change anything. - SetConfigs("a", 800, - "b", 900, - "c", 1100, - NULL); - test_peer_.CheckConfigs( - "a", false, - "b", true, - "c", false, - NULL); - SetConfigs("a", 800, - "c", 1100, - "INVALID1", 1000, - NULL); - test_peer_.CheckConfigs( - "a", false, - "b", true, - "c", false, - NULL); -} - -} // namespace test -} // namespace net diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 0dcf016..42a056a 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -69,6 +69,10 @@ const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state // lifetime const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection. +const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us. +const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion + // window. +const QuicTag kSMSS = TAG('S', 'M', 'S', 'S'); // Server's maximum packet size. const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name // indication const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc deleted file mode 100644 index 7fb4fa9..0000000 --- a/net/quic/crypto/crypto_server_config.cc +++ /dev/null @@ -1,1108 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/crypto/crypto_server_config.h" - -#include -#include - -#include "base/stl_util.h" -#include "base/strings/string_number_conversions.h" -#include "crypto/hkdf.h" -#include "crypto/secure_hash.h" -#include "net/base/net_util.h" -#include "net/quic/crypto/aes_128_gcm_12_decrypter.h" -#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include "net/quic/crypto/cert_compressor.h" -#include "net/quic/crypto/channel_id.h" -#include "net/quic/crypto/crypto_framer.h" -#include "net/quic/crypto/crypto_server_config_protobuf.h" -#include "net/quic/crypto/crypto_utils.h" -#include "net/quic/crypto/curve25519_key_exchange.h" -#include "net/quic/crypto/ephemeral_key_source.h" -#include "net/quic/crypto/key_exchange.h" -#include "net/quic/crypto/p256_key_exchange.h" -#include "net/quic/crypto/proof_source.h" -#include "net/quic/crypto/quic_decrypter.h" -#include "net/quic/crypto/quic_encrypter.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/crypto/source_address_token.h" -#include "net/quic/crypto/strike_register.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_utils.h" - -using base::StringPiece; -using crypto::SecureHash; -using std::map; -using std::string; -using std::vector; - -namespace net { - -// static -const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; - -QuicCryptoServerConfig::ConfigOptions::ConfigOptions() - : expiry_time(QuicWallTime::Zero()), - channel_id_enabled(false), - p256(false) {} - -QuicCryptoServerConfig::QuicCryptoServerConfig( - StringPiece source_address_token_secret, - QuicRandom* rand) - : replay_protection_(true), - configs_lock_(), - primary_config_(NULL), - next_config_promotion_time_(QuicWallTime::Zero()), - strike_register_lock_(), - server_nonce_strike_register_lock_(), - strike_register_no_startup_period_(false), - strike_register_max_entries_(1 << 10), - strike_register_window_secs_(600), - source_address_token_future_secs_(3600), - source_address_token_lifetime_secs_(86400), - server_nonce_strike_register_max_entries_(1 << 10), - server_nonce_strike_register_window_secs_(120) { - crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, - "QUIC source address token key", - CryptoSecretBoxer::GetKeySize(), - 0 /* no fixed IV needed */); - source_address_token_boxer_.SetKey(hkdf.server_write_key()); - - // Generate a random key and orbit for server nonces. - rand->RandBytes(server_nonce_orbit_, sizeof(server_nonce_orbit_)); - const size_t key_size = server_nonce_boxer_.GetKeySize(); - scoped_ptr key_bytes(new uint8[key_size]); - rand->RandBytes(key_bytes.get(), key_size); - - server_nonce_boxer_.SetKey( - StringPiece(reinterpret_cast(key_bytes.get()), key_size)); -} - -QuicCryptoServerConfig::~QuicCryptoServerConfig() { - primary_config_ = NULL; -} - -// static -QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const ConfigOptions& options) { - CryptoHandshakeMessage msg; - - const string curve25519_private_key = - Curve25519KeyExchange::NewPrivateKey(rand); - scoped_ptr curve25519( - Curve25519KeyExchange::New(curve25519_private_key)); - StringPiece curve25519_public_value = curve25519->public_value(); - - string encoded_public_values; - // First three bytes encode the length of the public value. - encoded_public_values.push_back(curve25519_public_value.size()); - encoded_public_values.push_back(curve25519_public_value.size() >> 8); - encoded_public_values.push_back(curve25519_public_value.size() >> 16); - encoded_public_values.append(curve25519_public_value.data(), - curve25519_public_value.size()); - - string p256_private_key; - if (options.p256) { - p256_private_key = P256KeyExchange::NewPrivateKey(); - scoped_ptr p256(P256KeyExchange::New(p256_private_key)); - StringPiece p256_public_value = p256->public_value(); - - encoded_public_values.push_back(p256_public_value.size()); - encoded_public_values.push_back(p256_public_value.size() >> 8); - encoded_public_values.push_back(p256_public_value.size() >> 16); - encoded_public_values.append(p256_public_value.data(), - p256_public_value.size()); - } - - msg.set_tag(kSCFG); - if (options.p256) { - msg.SetTaglist(kKEXS, kC255, kP256, 0); - } else { - msg.SetTaglist(kKEXS, kC255, 0); - } - msg.SetTaglist(kAEAD, kAESG, 0); - msg.SetValue(kVERS, static_cast(0)); - msg.SetStringPiece(kPUBS, encoded_public_values); - - if (options.expiry_time.IsZero()) { - const QuicWallTime now = clock->WallNow(); - const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds( - 60 * 60 * 24 * 180 /* 180 days, ~six months */)); - const uint64 expiry_seconds = expiry.ToUNIXSeconds(); - msg.SetValue(kEXPY, expiry_seconds); - } else { - msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds()); - } - - char orbit_bytes[kOrbitSize]; - if (options.orbit.size() == sizeof(orbit_bytes)) { - memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes)); - } else { - DCHECK(options.orbit.empty()); - rand->RandBytes(orbit_bytes, sizeof(orbit_bytes)); - } - msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes))); - - if (options.channel_id_enabled) { - msg.SetTaglist(kPDMD, kCHID, 0); - } - - if (options.id.empty()) { - // We need to ensure that the SCID changes whenever the server config does - // thus we make it a hash of the rest of the server config. - scoped_ptr serialized( - CryptoFramer::ConstructHandshakeMessage(msg)); - scoped_ptr hash(SecureHash::Create(SecureHash::SHA256)); - hash->Update(serialized->data(), serialized->length()); - - char scid_bytes[16]; - hash->Finish(scid_bytes, sizeof(scid_bytes)); - msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes))); - } else { - msg.SetStringPiece(kSCID, options.id); - } - // Don't put new tags below this point. The SCID generation should hash over - // everything but itself and so extra tags should be added prior to the - // preceeding if block. - - scoped_ptr serialized(CryptoFramer::ConstructHandshakeMessage(msg)); - - scoped_ptr config(new QuicServerConfigProtobuf); - config->set_config(serialized->AsStringPiece()); - QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key(); - curve25519_key->set_tag(kC255); - curve25519_key->set_private_key(curve25519_private_key); - - if (options.p256) { - QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key(); - p256_key->set_tag(kP256); - p256_key->set_private_key(p256_private_key); - } - - return config.release(); -} - -CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( - QuicServerConfigProtobuf* protobuf, - const QuicWallTime now) { - scoped_ptr msg( - CryptoFramer::ParseMessage(protobuf->config())); - - if (!msg.get()) { - LOG(WARNING) << "Failed to parse server config message"; - return NULL; - } - - scoped_refptr config(ParseConfigProtobuf(protobuf)); - if (!config.get()) { - LOG(WARNING) << "Failed to parse server config message"; - return NULL; - } - - { - base::AutoLock locked(configs_lock_); - if (configs_.find(config->id) != configs_.end()) { - LOG(WARNING) << "Failed to add config because another with the same " - "server config id already exists: " - << base::HexEncode(config->id.data(), config->id.size()); - return NULL; - } - - configs_[config->id] = config; - SelectNewPrimaryConfig(now); - DCHECK(primary_config_.get()); - } - - return msg.release(); -} - -CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const ConfigOptions& options) { - scoped_ptr config( - DefaultConfig(rand, clock, options)); - return AddConfig(config.get(), clock->WallNow()); -} - -bool QuicCryptoServerConfig::SetConfigs( - const vector& protobufs, - const QuicWallTime now) { - vector > new_configs; - bool ok = true; - - for (vector::const_iterator i = protobufs.begin(); - i != protobufs.end(); ++i) { - scoped_refptr config(ParseConfigProtobuf(*i)); - if (!config.get()) { - ok = false; - break; - } - new_configs.push_back(config); - } - - if (!ok) { - LOG(WARNING) << "Rejecting QUIC configs because of above errors"; - } else { - base::AutoLock locked(configs_lock_); - typedef ConfigMap::iterator ConfigMapIterator; - vector to_delete; - - DCHECK_EQ(protobufs.size(), new_configs.size()); - - // First, look for any configs that have been removed. - for (ConfigMapIterator i = configs_.begin(); - i != configs_.end(); ++i) { - const scoped_refptr old_config = i->second; - bool found = false; - - for (vector >::const_iterator j = - new_configs.begin(); - j != new_configs.end(); ++j) { - if ((*j)->id == old_config->id) { - found = true; - break; - } - } - - if (!found) { - // We cannot remove the primary config. This has probably happened - // because our source of config information failed for a time and we're - // suddenly seeing a jump in time. No matter - we'll configure a new - // primary config and then we'll be able to delete it next time. - if (!old_config->is_primary) { - to_delete.push_back(i); - } - } - } - - for (vector::const_iterator i = to_delete.begin(); - i != to_delete.end(); ++i) { - configs_.erase(*i); - } - - // Find any configs that need to be added. - for (vector >::const_iterator i = new_configs.begin(); - i != new_configs.end(); ++i) { - const scoped_refptr new_config = *i; - if (configs_.find(new_config->id) != configs_.end()) { - continue; - } - - configs_[new_config->id] = new_config; - } - - SelectNewPrimaryConfig(now); - } - - return ok; -} - -// ClientHelloInfo contains information about a client hello message that is -// only kept for as long as it's being processed. -struct ClientHelloInfo { - ClientHelloInfo(const IPEndPoint& in_client_ip, QuicWallTime in_now) - : client_ip(in_client_ip), - now(in_now), - valid_source_address_token(false), - client_nonce_well_formed(false), - unique(false) {} - - // Inputs to EvaluateClientHello. - const IPEndPoint client_ip; - const QuicWallTime now; - - // Outputs from EvaluateClientHello. - bool valid_source_address_token; - bool client_nonce_well_formed; - bool unique; - StringPiece sni; - StringPiece client_nonce; - StringPiece server_nonce; -}; - -QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( - const CryptoHandshakeMessage& client_hello, - QuicGuid guid, - const IPEndPoint& client_ip, - const QuicClock* clock, - QuicRandom* rand, - QuicCryptoNegotiatedParameters *params, - CryptoHandshakeMessage* out, - string* error_details) const { - DCHECK(error_details); - - StringPiece requested_scid; - client_hello.GetStringPiece(kSCID, &requested_scid); - const QuicWallTime now(clock->WallNow()); - - scoped_refptr requested_config; - scoped_refptr primary_config; - { - base::AutoLock locked(configs_lock_); - - if (!primary_config_.get()) { - *error_details = "No configurations loaded"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - - if (!next_config_promotion_time_.IsZero() && - next_config_promotion_time_.IsAfter(now)) { - SelectNewPrimaryConfig(now); - } - - primary_config = primary_config_; - - if (!requested_scid.empty()) { - ConfigMap::const_iterator it = configs_.find(requested_scid.as_string()); - if (it != configs_.end()) { - // We'll use the config that the client requested in order to do - // key-agreement. Otherwise we'll give it a copy of |primary_config_| - // to use. - requested_config = it->second; - } - } - } - - ClientHelloInfo info(client_ip, now); - QuicErrorCode error = EvaluateClientHello( - client_hello, primary_config->orbit, &info, error_details); - if (error != QUIC_NO_ERROR) { - return error; - } - - out->Clear(); - - if (!info.valid_source_address_token || - !info.client_nonce_well_formed || - !info.unique || - !requested_config.get()) { - BuildRejection(primary_config.get(), client_hello, info, rand, out); - return QUIC_NO_ERROR; - } - - const QuicTag* their_aeads; - const QuicTag* their_key_exchanges; - size_t num_their_aeads, num_their_key_exchanges; - if (client_hello.GetTaglist(kAEAD, &their_aeads, - &num_their_aeads) != QUIC_NO_ERROR || - client_hello.GetTaglist(kKEXS, &their_key_exchanges, - &num_their_key_exchanges) != QUIC_NO_ERROR || - num_their_aeads != 1 || - num_their_key_exchanges != 1) { - *error_details = "Missing or invalid AEAD or KEXS"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - size_t key_exchange_index; - if (!QuicUtils::FindMutualTag(requested_config->aead, their_aeads, - num_their_aeads, QuicUtils::LOCAL_PRIORITY, - ¶ms->aead, NULL) || - !QuicUtils::FindMutualTag( - requested_config->kexs, their_key_exchanges, num_their_key_exchanges, - QuicUtils::LOCAL_PRIORITY, ¶ms->key_exchange, - &key_exchange_index)) { - *error_details = "Unsupported AEAD or KEXS"; - return QUIC_CRYPTO_NO_SUPPORT; - } - - StringPiece public_value; - if (!client_hello.GetStringPiece(kPUBS, &public_value)) { - *error_details = "Missing public value"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - const KeyExchange* key_exchange = - requested_config->key_exchanges[key_exchange_index]; - if (!key_exchange->CalculateSharedKey(public_value, - ¶ms->initial_premaster_secret)) { - *error_details = "Invalid public value"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (!info.sni.empty()) { - scoped_ptr sni_tmp(new char[info.sni.length() + 1]); - memcpy(sni_tmp.get(), info.sni.data(), info.sni.length()); - sni_tmp[info.sni.length()] = 0; - params->sni = CryptoUtils::NormalizeHostname(sni_tmp.get()); - } - - string hkdf_suffix; - const QuicData& client_hello_serialized = client_hello.GetSerialized(); - hkdf_suffix.reserve(sizeof(guid) + client_hello_serialized.length() + - requested_config->serialized.size()); - hkdf_suffix.append(reinterpret_cast(&guid), sizeof(guid)); - hkdf_suffix.append(client_hello_serialized.data(), - client_hello_serialized.length()); - hkdf_suffix.append(requested_config->serialized); - - StringPiece cetv_ciphertext; - if (requested_config->channel_id_enabled && - client_hello.GetStringPiece(kCETV, &cetv_ciphertext)) { - CryptoHandshakeMessage client_hello_copy(client_hello); - client_hello_copy.Erase(kCETV); - client_hello_copy.Erase(kPAD); - - const QuicData& client_hello_serialized = client_hello_copy.GetSerialized(); - string hkdf_input; - hkdf_input.append(QuicCryptoConfig::kCETVLabel, - strlen(QuicCryptoConfig::kCETVLabel) + 1); - hkdf_input.append(reinterpret_cast(&guid), sizeof(guid)); - hkdf_input.append(client_hello_serialized.data(), - client_hello_serialized.length()); - hkdf_input.append(requested_config->serialized); - - CrypterPair crypters; - if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, - info.client_nonce, info.server_nonce, - hkdf_input, CryptoUtils::SERVER, &crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - scoped_ptr cetv_plaintext(crypters.decrypter->DecryptPacket( - 0 /* sequence number */, StringPiece() /* associated data */, - cetv_ciphertext)); - if (!cetv_plaintext.get()) { - *error_details = "CETV decryption failure"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - scoped_ptr cetv(CryptoFramer::ParseMessage( - cetv_plaintext->AsStringPiece())); - if (!cetv.get()) { - *error_details = "CETV parse error"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - StringPiece key, signature; - if (cetv->GetStringPiece(kCIDK, &key) && - cetv->GetStringPiece(kCIDS, &signature)) { - if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) { - *error_details = "ChannelID signature failure"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - params->channel_id = key.as_string(); - } - } - - string hkdf_input; - size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; - hkdf_input.reserve(label_len + hkdf_suffix.size()); - hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); - hkdf_input.append(hkdf_suffix); - - if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, - info.client_nonce, info.server_nonce, hkdf_input, - CryptoUtils::SERVER, - ¶ms->initial_crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - string forward_secure_public_value; - if (ephemeral_key_source_.get()) { - params->forward_secure_premaster_secret = - ephemeral_key_source_->CalculateForwardSecureKey( - key_exchange, rand, clock->ApproximateNow(), public_value, - &forward_secure_public_value); - } else { - scoped_ptr forward_secure_key_exchange( - key_exchange->NewKeyPair(rand)); - forward_secure_public_value = - forward_secure_key_exchange->public_value().as_string(); - if (!forward_secure_key_exchange->CalculateSharedKey( - public_value, ¶ms->forward_secure_premaster_secret)) { - *error_details = "Invalid public value"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - } - - string forward_secure_hkdf_input; - label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; - forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size()); - forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, - label_len); - forward_secure_hkdf_input.append(hkdf_suffix); - - if (!CryptoUtils::DeriveKeys( - params->forward_secure_premaster_secret, params->aead, - info.client_nonce, info.server_nonce, forward_secure_hkdf_input, - CryptoUtils::SERVER, ¶ms->forward_secure_crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - out->set_tag(kSHLO); - out->SetStringPiece(kSourceAddressTokenTag, - NewSourceAddressToken(client_ip, rand, info.now)); - out->SetStringPiece(kPUBS, forward_secure_public_value); - return QUIC_NO_ERROR; -} - -// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for -// Config's based on their primary_time. -// static -bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan( - const scoped_refptr& a, - const scoped_refptr& b) { - return a->primary_time.IsBefore(b->primary_time); -} - -void QuicCryptoServerConfig::SelectNewPrimaryConfig( - const QuicWallTime now) const { - vector > configs; - configs.reserve(configs_.size()); - scoped_refptr first_config = NULL; - - for (ConfigMap::const_iterator it = configs_.begin(); - it != configs_.end(); ++it) { - const scoped_refptr config(it->second); - if (!first_config.get()) { - first_config = config; - } - if (config->primary_time.IsZero()) { - continue; - } - configs.push_back(it->second); - } - - if (configs.size() == 0) { - // Tests don't set |primary_time_|. For that case we promote the first - // Config and leave it as primary forever. - if (!primary_config_.get() && first_config.get()) { - primary_config_ = first_config; - primary_config_->is_primary = true; - } - return; - } - - std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan); - - for (size_t i = 0; i < configs.size(); ++i) { - const scoped_refptr config(configs[i]); - - if (!config->primary_time.IsAfter(now)) { - continue; - } - - // This is the first config with a primary_time in the future. Thus the - // previous Config should be the primary and this one should determine the - // next_config_promotion_time_. - scoped_refptr new_primary; - if (i == 0) { - // There was no previous Config, so this will have to be primary. - new_primary = config; - - // We need the primary_time of the next config. - if (configs.size() > 1) { - next_config_promotion_time_ = configs[1]->primary_time; - } else { - next_config_promotion_time_ = QuicWallTime::Zero(); - } - } else { - new_primary = configs[i - 1]; - next_config_promotion_time_ = config->primary_time; - } - - if (primary_config_.get()) { - primary_config_->is_primary = false; - } - primary_config_ = new_primary; - new_primary->is_primary = true; - - return; - } - - // All config's primary times are in the past. We should make the most recent - // primary. - scoped_refptr new_primary = configs[configs.size() - 1]; - if (primary_config_.get()) { - primary_config_->is_primary = false; - } - primary_config_ = new_primary; - new_primary->is_primary = true; - next_config_promotion_time_ = QuicWallTime::Zero(); -} - -QuicErrorCode QuicCryptoServerConfig::EvaluateClientHello( - const CryptoHandshakeMessage& client_hello, - const uint8* orbit, - ClientHelloInfo* info, - string* error_details) const { - if (client_hello.size() < kClientHelloMinimumSize) { - *error_details = "Client hello too small"; - return QUIC_CRYPTO_INVALID_VALUE_LENGTH; - } - - StringPiece srct; - if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct) && - ValidateSourceAddressToken(srct, info->client_ip, info->now)) { - info->valid_source_address_token = true; - } - - if (client_hello.GetStringPiece(kSNI, &info->sni) && - !CryptoUtils::IsValidSNI(info->sni)) { - *error_details = "Invalid SNI name"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - // The client nonce is used first to try and establish uniqueness. - bool unique_by_strike_register = false; - - if (client_hello.GetStringPiece(kNONC, &info->client_nonce) && - info->client_nonce.size() == kNonceSize) { - info->client_nonce_well_formed = true; - if (replay_protection_) { - base::AutoLock auto_lock(strike_register_lock_); - - if (strike_register_.get() == NULL) { - strike_register_.reset(new StrikeRegister( - strike_register_max_entries_, - static_cast(info->now.ToUNIXSeconds()), - strike_register_window_secs_, - orbit, - strike_register_no_startup_period_ ? - StrikeRegister::NO_STARTUP_PERIOD_NEEDED : - StrikeRegister::DENY_REQUESTS_AT_STARTUP)); - } - - unique_by_strike_register = strike_register_->Insert( - reinterpret_cast(info->client_nonce.data()), - static_cast(info->now.ToUNIXSeconds())); - } - } - - client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce); - - // If the client nonce didn't establish uniqueness then an echoed server - // nonce may. - bool unique_by_server_nonce = false; - if (replay_protection_ && - !unique_by_strike_register && - !info->server_nonce.empty()) { - unique_by_server_nonce = ValidateServerNonce(info->server_nonce, info->now); - } - - info->unique = !replay_protection_ || - unique_by_strike_register || - unique_by_server_nonce; - - return QUIC_NO_ERROR; -} - -void QuicCryptoServerConfig::BuildRejection( - const scoped_refptr& config, - const CryptoHandshakeMessage& client_hello, - const ClientHelloInfo& info, - QuicRandom* rand, - CryptoHandshakeMessage* out) const { - out->set_tag(kREJ); - out->SetStringPiece(kSCFG, config->serialized); - out->SetStringPiece(kSourceAddressTokenTag, - NewSourceAddressToken(info.client_ip, rand, info.now)); - if (replay_protection_) { - out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now)); - } - - // The client may have requested a certificate chain. - const QuicTag* their_proof_demands; - size_t num_their_proof_demands; - - if (proof_source_.get() == NULL || - client_hello.GetTaglist(kPDMD, &their_proof_demands, - &num_their_proof_demands) != - QUIC_NO_ERROR) { - return; - } - - bool x509_supported = false, x509_ecdsa_supported = false; - for (size_t i = 0; i < num_their_proof_demands; i++) { - switch (their_proof_demands[i]) { - case kX509: - x509_supported = true; - x509_ecdsa_supported = true; - break; - case kX59R: - x509_supported = true; - break; - } - } - - if (!x509_supported) { - return; - } - - const vector* certs; - string signature; - if (!proof_source_->GetProof(info.sni.as_string(), config->serialized, - x509_ecdsa_supported, &certs, &signature)) { - return; - } - - StringPiece their_common_set_hashes; - StringPiece their_cached_cert_hashes; - client_hello.GetStringPiece(kCCS, &their_common_set_hashes); - client_hello.GetStringPiece(kCCRT, &their_cached_cert_hashes); - - const string compressed = CertCompressor::CompressChain( - *certs, their_common_set_hashes, their_cached_cert_hashes, - config->common_cert_sets); - - // kREJOverheadBytes is a very rough estimate of how much of a REJ - // message is taken up by things other than the certificates. - const size_t kREJOverheadBytes = 112; - // max_unverified_size is the number of bytes that the certificate chain - // and signature can consume before we will demand a valid source-address - // token. - const size_t max_unverified_size = client_hello.size() - kREJOverheadBytes; - COMPILE_ASSERT(kClientHelloMinimumSize >= kREJOverheadBytes, - overhead_calculation_may_underflow); - if (info.valid_source_address_token || - signature.size() + compressed.size() < max_unverified_size) { - out->SetStringPiece(kCertificateTag, compressed); - out->SetStringPiece(kPROF, signature); - } -} - -scoped_refptr -QuicCryptoServerConfig::ParseConfigProtobuf( - QuicServerConfigProtobuf* protobuf) { - scoped_ptr msg( - CryptoFramer::ParseMessage(protobuf->config())); - - if (msg->tag() != kSCFG) { - LOG(WARNING) << "Server config message has tag " << msg->tag() - << " expected " << kSCFG; - return NULL; - } - - scoped_refptr config(new Config); - config->serialized = protobuf->config(); - - if (protobuf->has_primary_time()) { - config->primary_time = - QuicWallTime::FromUNIXSeconds(protobuf->primary_time()); - } - - StringPiece scid; - if (!msg->GetStringPiece(kSCID, &scid)) { - LOG(WARNING) << "Server config message is missing SCID"; - return NULL; - } - config->id = scid.as_string(); - - const QuicTag* aead_tags; - size_t aead_len; - if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) { - LOG(WARNING) << "Server config message is missing AEAD"; - return NULL; - } - config->aead = vector(aead_tags, aead_tags + aead_len); - - const QuicTag* kexs_tags; - size_t kexs_len; - if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) { - LOG(WARNING) << "Server config message is missing KEXS"; - return NULL; - } - - StringPiece orbit; - if (!msg->GetStringPiece(kORBT, &orbit)) { - LOG(WARNING) << "Server config message is missing OBIT"; - return NULL; - } - - if (orbit.size() != kOrbitSize) { - LOG(WARNING) << "Orbit value in server config is the wrong length." - " Got " << orbit.size() << " want " << kOrbitSize; - return NULL; - } - COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size); - memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); - - { - base::AutoLock locked(strike_register_lock_); - if (strike_register_.get()) { - const uint8* orbit = strike_register_->orbit(); - if (0 != memcmp(orbit, config->orbit, kOrbitSize)) { - LOG(WARNING) - << "Server config has different orbit than current config. " - "Switching orbits at run-time is not supported."; - return NULL; - } - } - } - - if (kexs_len != protobuf->key_size()) { - LOG(WARNING) << "Server config has " << kexs_len - << " key exchange methods configured, but " - << protobuf->key_size() << " private keys"; - return NULL; - } - - const QuicTag* proof_demand_tags; - size_t num_proof_demand_tags; - if (msg->GetTaglist(kPDMD, &proof_demand_tags, &num_proof_demand_tags) == - QUIC_NO_ERROR) { - for (size_t i = 0; i < num_proof_demand_tags; i++) { - if (proof_demand_tags[i] == kCHID) { - config->channel_id_enabled = true; - break; - } - } - } - - for (size_t i = 0; i < kexs_len; i++) { - const QuicTag tag = kexs_tags[i]; - string private_key; - - config->kexs.push_back(tag); - - for (size_t j = 0; j < protobuf->key_size(); j++) { - const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i); - if (key.tag() == tag) { - private_key = key.private_key(); - break; - } - } - - if (private_key.empty()) { - LOG(WARNING) << "Server config contains key exchange method without " - "corresponding private key: " << tag; - return NULL; - } - - scoped_ptr ka; - switch (tag) { - case kC255: - ka.reset(Curve25519KeyExchange::New(private_key)); - if (!ka.get()) { - LOG(WARNING) << "Server config contained an invalid curve25519" - " private key."; - 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: " << tag; - return NULL; - } - - for (vector::const_iterator i = config->key_exchanges.begin(); - i != config->key_exchanges.end(); ++i) { - if ((*i)->tag() == tag) { - LOG(WARNING) << "Duplicate key exchange in config: " << tag; - return NULL; - } - } - - config->key_exchanges.push_back(ka.release()); - } - - if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) { - LOG(WARNING) << "Server config message is missing version"; - return NULL; - } - - if (config->version != QuicCryptoConfig::CONFIG_VERSION) { - LOG(WARNING) << "Server config specifies an unsupported version"; - return NULL; - } - - return config; -} - -void QuicCryptoServerConfig::SetProofSource(ProofSource* proof_source) { - proof_source_.reset(proof_source); -} - -void QuicCryptoServerConfig::SetEphemeralKeySource( - EphemeralKeySource* ephemeral_key_source) { - ephemeral_key_source_.reset(ephemeral_key_source); -} - -void QuicCryptoServerConfig::set_replay_protection(bool on) { - replay_protection_ = on; -} - -void QuicCryptoServerConfig::set_strike_register_no_startup_period() { - base::AutoLock auto_lock(strike_register_lock_); - DCHECK(!strike_register_.get()); - strike_register_no_startup_period_ = true; -} - -void QuicCryptoServerConfig::set_strike_register_max_entries( - uint32 max_entries) { - base::AutoLock locker(strike_register_lock_); - DCHECK(!strike_register_.get()); - strike_register_max_entries_ = max_entries; -} - -void QuicCryptoServerConfig::set_strike_register_window_secs( - uint32 window_secs) { - base::AutoLock locker(strike_register_lock_); - DCHECK(!strike_register_.get()); - strike_register_window_secs_ = window_secs; -} - -void QuicCryptoServerConfig::set_source_address_token_future_secs( - uint32 future_secs) { - source_address_token_future_secs_ = future_secs; -} - -void QuicCryptoServerConfig::set_source_address_token_lifetime_secs( - uint32 lifetime_secs) { - source_address_token_lifetime_secs_ = lifetime_secs; -} - -void QuicCryptoServerConfig::set_server_nonce_strike_register_max_entries( - uint32 max_entries) { - DCHECK(!server_nonce_strike_register_.get()); - server_nonce_strike_register_max_entries_ = max_entries; -} - -void QuicCryptoServerConfig::set_server_nonce_strike_register_window_secs( - uint32 window_secs) { - DCHECK(!server_nonce_strike_register_.get()); - server_nonce_strike_register_window_secs_ = window_secs; -} - -string QuicCryptoServerConfig::NewSourceAddressToken( - const IPEndPoint& ip, - QuicRandom* rand, - QuicWallTime now) const { - SourceAddressToken source_address_token; - source_address_token.set_ip(IPAddressToPackedString(ip.address())); - source_address_token.set_timestamp(now.ToUNIXSeconds()); - - return source_address_token_boxer_.Box( - rand, source_address_token.SerializeAsString()); -} - -bool QuicCryptoServerConfig::ValidateSourceAddressToken( - StringPiece token, - const IPEndPoint& ip, - QuicWallTime now) const { - string storage; - StringPiece plaintext; - if (!source_address_token_boxer_.Unbox(token, &storage, &plaintext)) { - return false; - } - - SourceAddressToken source_address_token; - if (!source_address_token.ParseFromArray(plaintext.data(), - plaintext.size())) { - return false; - } - - if (source_address_token.ip() != IPAddressToPackedString(ip.address())) { - // It's for a different IP address. - return false; - } - - const QuicWallTime timestamp( - QuicWallTime::FromUNIXSeconds(source_address_token.timestamp())); - const QuicTime::Delta delta(now.AbsoluteDifference(timestamp)); - - if (now.IsBefore(timestamp) && - delta.ToSeconds() > source_address_token_future_secs_) { - return false; - } - - if (now.IsAfter(timestamp) && - delta.ToSeconds() > source_address_token_lifetime_secs_) { - return false; - } - - return true; -} - -// kServerNoncePlaintextSize is the number of bytes in an unencrypted server -// nonce. -static const size_t kServerNoncePlaintextSize = - 4 /* timestamp */ + 20 /* random bytes */; - -string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand, - QuicWallTime now) const { - const uint32 timestamp = static_cast(now.ToUNIXSeconds()); - - uint8 server_nonce[kServerNoncePlaintextSize]; - COMPILE_ASSERT(sizeof(server_nonce) > sizeof(timestamp), nonce_too_small); - server_nonce[0] = static_cast(timestamp >> 24); - server_nonce[1] = static_cast(timestamp >> 16); - server_nonce[2] = static_cast(timestamp >> 8); - server_nonce[3] = static_cast(timestamp); - rand->RandBytes(&server_nonce[sizeof(timestamp)], - sizeof(server_nonce) - sizeof(timestamp)); - - return server_nonce_boxer_.Box( - rand, - StringPiece(reinterpret_cast(server_nonce), sizeof(server_nonce))); -} - -bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token, - QuicWallTime now) const { - string storage; - StringPiece plaintext; - if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) { - return false; - } - - // plaintext contains: - // uint32 timestamp - // uint8[20] random bytes - - if (plaintext.size() != kServerNoncePlaintextSize) { - // This should never happen because the value decrypted correctly. - LOG(DFATAL) << "Seemingly valid server nonce had incorrect length."; - return false; - } - - uint8 server_nonce[32]; - memcpy(server_nonce, plaintext.data(), 4); - memcpy(server_nonce + 4, server_nonce_orbit_, sizeof(server_nonce_orbit_)); - memcpy(server_nonce + 4 + sizeof(server_nonce_orbit_), plaintext.data() + 4, - 20); - COMPILE_ASSERT(4 + sizeof(server_nonce_orbit_) + 20 == sizeof(server_nonce), - bad_nonce_buffer_length); - - bool is_unique; - { - base::AutoLock auto_lock(server_nonce_strike_register_lock_); - if (server_nonce_strike_register_.get() == NULL) { - server_nonce_strike_register_.reset(new StrikeRegister( - server_nonce_strike_register_max_entries_, - static_cast(now.ToUNIXSeconds()), - server_nonce_strike_register_window_secs_, server_nonce_orbit_, - StrikeRegister::NO_STARTUP_PERIOD_NEEDED)); - } - is_unique = server_nonce_strike_register_->Insert( - server_nonce, static_cast(now.ToUNIXSeconds())); - } - - return is_unique; -} - -QuicCryptoServerConfig::Config::Config() - : channel_id_enabled(false), - is_primary(false), - primary_time(QuicWallTime::Zero()) {} - -QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); } - -} // namespace net diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/crypto_server_config.h deleted file mode 100644 index 4551425..0000000 --- a/net/quic/crypto/crypto_server_config.h +++ /dev/null @@ -1,370 +0,0 @@ -// 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. - -#ifndef NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ -#define NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ - -#include -#include -#include - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string_piece.h" -#include "base/synchronization/lock.h" -#include "net/base/ip_endpoint.h" -#include "net/base/net_export.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/crypto_secret_boxer.h" -#include "net/quic/quic_time.h" - -namespace net { - -class EphemeralKeySource; -class KeyExchange; -class ProofSource; -class QuicClock; -class QuicDecrypter; -class QuicEncrypter; -class QuicRandom; -class QuicServerConfigProtobuf; -class StrikeRegister; - -struct ClientHelloInfo; - -namespace test { -class QuicCryptoServerConfigPeer; -} // namespace test - -// QuicCryptoServerConfig contains the crypto configuration of a QUIC server. -// Unlike a client, a QUIC server can have multiple configurations active in -// order to support clients resuming with a previous configuration. -// TODO(agl): when adding configurations at runtime is added, this object will -// need to consider locking. -class NET_EXPORT_PRIVATE QuicCryptoServerConfig { - public: - // ConfigOptions contains options for generating server configs. - struct NET_EXPORT_PRIVATE ConfigOptions { - ConfigOptions(); - - // expiry_time is the time, in UNIX seconds, when the server config will - // expire. If unset, it defaults to the current time plus six months. - QuicWallTime expiry_time; - // channel_id_enabled controls whether the server config will indicate - // support for ChannelIDs. - bool channel_id_enabled; - // id contains the server config id for the resulting config. If empty, a - // random id is generated. - std::string id; - // orbit contains the kOrbitSize bytes of the orbit value for the server - // config. If |orbit| is empty then a random orbit is generated. - std::string orbit; - // p256 determines whether a P-256 public key will be included in the - // server config. Note that this breaks deterministic server-config - // generation since P-256 key generation doesn't use the QuicRandom given - // to DefaultConfig(). - bool p256; - }; - - // |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. In tests, use TESTING. - // |server_nonce_entropy|: an entropy source used to generate the orbit and - // key for server nonces, which are always local to a given instance of a - // server. - QuicCryptoServerConfig(base::StringPiece source_address_token_secret, - QuicRandom* server_nonce_entropy); - ~QuicCryptoServerConfig(); - - // TESTING is a magic parameter for passing to the constructor in tests. - static const char TESTING[]; - - // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for - // using in tests. - static QuicServerConfigProtobuf* DefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const ConfigOptions& options); - - // AddConfig adds a QuicServerConfigProtobuf to the availible configurations. - // It returns the SCFG message from the config if successful. The caller - // takes ownership of the CryptoHandshakeMessage. |now| is used in - // conjunction with |protobuf->primary_time()| to determine whether the - // config should be made primary. - CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf, - QuicWallTime now); - - // AddDefaultConfig calls DefaultConfig to create a config and then calls - // AddConfig to add it. See the comment for |DefaultConfig| for details of - // the arguments. - CryptoHandshakeMessage* AddDefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const ConfigOptions& options); - - // SetConfigs takes a vector of config protobufs and the current time. - // Configs are assumed to be uniquely identified by their server config ID. - // Previously unknown configs are added and possibly made the primary config - // depending on their |primary_time| and the value of |now|. Configs that are - // known, but are missing from the protobufs are deleted, unless they are - // currently the primary config. SetConfigs returns false if any errors were - // encountered and no changes to the QuicCryptoServerConfig will occur. - bool SetConfigs(const std::vector& protobufs, - QuicWallTime now); - - // 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. 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. - // clock: used to validate client nonces and ephemeral keys. - // rand: an entropy source - // params: the state of the handshake. This may be updated with a server - // nonce when we send a rejection. After a successful handshake, this will - // contain the state of the connection. - // out: the resulting handshake message (either REJ or SHLO) - // error_details: used to store a string describing any error. - QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, - QuicGuid guid, - const IPEndPoint& client_ip, - const QuicClock* clock, - QuicRandom* rand, - QuicCryptoNegotiatedParameters* params, - CryptoHandshakeMessage* out, - std::string* error_details) const; - - // SetProofSource installs |proof_source| as the ProofSource for handshakes. - // This object takes ownership of |proof_source|. - void SetProofSource(ProofSource* proof_source); - - // SetEphemeralKeySource installs an object that can cache ephemeral keys for - // a short period of time. This object takes ownership of - // |ephemeral_key_source|. If not set then ephemeral keys will be generated - // per-connection. - void SetEphemeralKeySource(EphemeralKeySource* ephemeral_key_source); - - // set_replay_protection controls whether replay protection is enabled. If - // replay protection is disabled then no strike registers are needed and - // frontends can share an orbit value without a shared strike-register. - // However, an attacker can duplicate a handshake and cause a client's - // request to be processed twice. - void set_replay_protection(bool on); - - // set_strike_register_no_startup_period configures the strike register to - // not have a startup period. - void set_strike_register_no_startup_period(); - - // set_strike_register_max_entries sets the maximum number of entries that - // the internal strike register will hold. If the strike register fills up - // then the oldest entries (by the client's clock) will be dropped. - void set_strike_register_max_entries(uint32 max_entries); - - // set_strike_register_window_secs sets the number of seconds around the - // current time that the strike register will attempt to be authoritative - // for. Setting a larger value allows for greater client clock-skew, but - // means that the quiescent startup period must be longer. - void set_strike_register_window_secs(uint32 window_secs); - - // set_source_address_token_future_secs sets the number of seconds into the - // future that source-address tokens will be accepted from. Since - // source-address tokens are authenticated, this should only happen if - // another, valid server has clock-skew. - void set_source_address_token_future_secs(uint32 future_secs); - - // set_source_address_token_lifetime_secs sets the number of seconds that a - // source-address token will be valid for. - void set_source_address_token_lifetime_secs(uint32 lifetime_secs); - - // set_server_nonce_strike_register_max_entries sets the number of entries in - // the server-nonce strike-register. This is used to record that server nonce - // values have been used. If the number of entries is too small then clients - // which are depending on server nonces may fail to handshake because their - // nonce has expired in the amount of time it took to go from the server to - // the client and back. - void set_server_nonce_strike_register_max_entries(uint32 max_entries); - - // set_server_nonce_strike_register_window_secs sets the number of seconds - // around the current time that the server-nonce strike-register will accept - // nonces from. Setting a larger value allows for clients to delay follow-up - // client hellos for longer and still use server nonces as proofs of - // uniqueness. - void set_server_nonce_strike_register_window_secs(uint32 window_secs); - - private: - friend class test::QuicCryptoServerConfigPeer; - - // Config represents a server config: a collection of preferences and - // Diffie-Hellman public values. - class NET_EXPORT_PRIVATE Config : public QuicCryptoConfig, - public base::RefCounted { - public: - Config(); - - // TODO(rtenneti): since this is a class, we should probably do - // getters/setters here. - // |serialized| contains the bytes of this server config, suitable for - // sending on the wire. - std::string serialized; - // id contains the SCID of this server config. - std::string id; - // orbit contains the orbit value for this config: an opaque identifier - // used to identify clusters of server frontends. - unsigned char orbit[kOrbitSize]; - - // key_exchanges contains key exchange objects with the private keys - // already loaded. The values correspond, one-to-one, with the tags in - // |kexs| from the parent class. - std::vector key_exchanges; - - // tag_value_map contains the raw key/value pairs for the config. - QuicTagValueMap tag_value_map; - - // channel_id_enabled is true if the config in |serialized| specifies that - // ChannelIDs are supported. - bool channel_id_enabled; - - // is_primary is true if this config is the one that we'll give out to - // clients as the current one. - bool is_primary; - - // primary_time contains the timestamp when this config should become the - // primary config. A value of QuicWallTime::Zero() means that this config - // will not be promoted at a specific time. - QuicWallTime primary_time; - - private: - friend class base::RefCounted; - virtual ~Config(); - - DISALLOW_COPY_AND_ASSIGN(Config); - }; - - typedef std::map > ConfigMap; - - // ConfigPrimaryTimeLessThan returns true if a->primary_time < - // b->primary_time. - static bool ConfigPrimaryTimeLessThan(const scoped_refptr& a, - const scoped_refptr& b); - - // SelectNewPrimaryConfig reevaluates the primary config based on the - // "primary_time" deadlines contained in each. - void SelectNewPrimaryConfig(QuicWallTime now) const; - - // EvaluateClientHello checks |client_hello| for gross errors and determines - // whether it can be shown to be fresh (i.e. not a replay). The results are - // written to |info|. - QuicErrorCode EvaluateClientHello( - const CryptoHandshakeMessage& client_hello, - const uint8* orbit, - ClientHelloInfo* info, - std::string* error_details) const; - - // BuildRejection sets |out| to be a REJ message in reply to |client_hello|. - void BuildRejection( - const scoped_refptr& config, - const CryptoHandshakeMessage& client_hello, - const ClientHelloInfo& info, - QuicRandom* rand, - CryptoHandshakeMessage* out) const; - - // ParseConfigProtobuf parses the given config protobuf and returns a - // scoped_refptr if successful. The caller adopts the reference to the - // Config. On error, ParseConfigProtobuf returns NULL. - scoped_refptr ParseConfigProtobuf(QuicServerConfigProtobuf* protobuf); - - // NewSourceAddressToken returns a fresh source address token for the given - // IP address. - std::string NewSourceAddressToken(const IPEndPoint& ip, - QuicRandom* rand, - QuicWallTime now) const; - - // 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, - QuicWallTime now) const; - - // NewServerNonce generates and encrypts a random nonce. - std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const; - - // ValidateServerNonce decrypts |token| and verifies that it hasn't been - // previously used and is recent enough that it is plausible that it was part - // of a very recently provided rejection ("recent" will be on the order of - // 10-30 seconds). If so, it records that it has been used and returns true. - // Otherwise it returns false. - bool ValidateServerNonce(base::StringPiece echoed_server_nonce, - QuicWallTime now) const; - - // replay_protection_ controls whether the server enforces that handshakes - // aren't replays. - bool replay_protection_; - - // configs_ satisfies the following invariants: - // 1) configs_.empty() <-> primary_config_ == NULL - // 2) primary_config_ != NULL -> primary_config_->is_primary - // 3) ∀ c∈configs_, c->is_primary <-> c == primary_config_ - mutable base::Lock configs_lock_; - // configs_ contains all active server configs. It's expected that there are - // about half-a-dozen configs active at any one time. - ConfigMap configs_; - // primary_config_ points to a Config (which is also in |configs_|) which is - // the primary config - i.e. the one that we'll give out to new clients. - mutable scoped_refptr primary_config_; - // next_config_promotion_time_ contains the nearest, future time when an - // active config will be promoted to primary. - mutable QuicWallTime next_config_promotion_time_; - - mutable base::Lock strike_register_lock_; - // strike_register_ contains a data structure that keeps track of previously - // observed client nonces in order to prevent replay attacks. - mutable scoped_ptr strike_register_; - - // source_address_token_boxer_ is used to protect the source-address tokens - // that are given to clients. - CryptoSecretBoxer source_address_token_boxer_; - - // server_nonce_boxer_ is used to encrypt and validate suggested server - // nonces. - CryptoSecretBoxer server_nonce_boxer_; - - // server_nonce_orbit_ contains the random, per-server orbit values that this - // server will use to generate server nonces (the moral equivalent of a SYN - // cookies). - uint8 server_nonce_orbit_[8]; - - mutable base::Lock server_nonce_strike_register_lock_; - // server_nonce_strike_register_ contains a data structure that keeps track of - // previously observed server nonces from this server, in order to prevent - // replay attacks. - mutable scoped_ptr server_nonce_strike_register_; - - // proof_source_ contains an object that can provide certificate chains and - // signatures. - scoped_ptr proof_source_; - - // ephemeral_key_source_ contains an object that caches ephemeral keys for a - // short period of time. - scoped_ptr ephemeral_key_source_; - - // These fields store configuration values. See the comments for their - // respective setter functions. - bool strike_register_no_startup_period_; - uint32 strike_register_max_entries_; - uint32 strike_register_window_secs_; - uint32 source_address_token_future_secs_; - uint32 source_address_token_lifetime_secs_; - uint32 server_nonce_strike_register_max_entries_; - uint32 server_nonce_strike_register_window_secs_; -}; - -} // namespace net - -#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ diff --git a/net/quic/crypto/crypto_server_test.cc b/net/quic/crypto/crypto_server_test.cc index 258afd5..1d9664d 100644 --- a/net/quic/crypto/crypto_server_test.cc +++ b/net/quic/crypto/crypto_server_test.cc @@ -4,8 +4,8 @@ #include "base/strings/string_number_conversions.h" #include "crypto/secure_hash.h" -#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/mock_clock.h" diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc new file mode 100644 index 0000000..3dafdb8 --- /dev/null +++ b/net/quic/crypto/quic_crypto_client_config.cc @@ -0,0 +1,633 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/quic_crypto_client_config.h" + +#include "base/stl_util.h" +#include "net/quic/crypto/cert_compressor.h" +#include "net/quic/crypto/channel_id.h" +#include "net/quic/crypto/common_cert_set.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/curve25519_key_exchange.h" +#include "net/quic/crypto/key_exchange.h" +#include "net/quic/crypto/p256_key_exchange.h" +#include "net/quic/crypto/proof_verifier.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_utils.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +using base::StringPiece; +using std::map; +using std::string; +using std::vector; + +namespace net { + +QuicCryptoClientConfig::QuicCryptoClientConfig() {} + +QuicCryptoClientConfig::~QuicCryptoClientConfig() { + STLDeleteValues(&cached_states_); +} + +QuicCryptoClientConfig::CachedState::CachedState() + : server_config_valid_(false), + generation_counter_(0) {} + +QuicCryptoClientConfig::CachedState::~CachedState() {} + +bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { + if (server_config_.empty() || !server_config_valid_) { + return false; + } + + const CryptoHandshakeMessage* scfg = GetServerConfig(); + if (!scfg) { + // Should be impossible short of cache corruption. + DCHECK(false); + return false; + } + + uint64 expiry_seconds; + if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR || + now.ToUNIXSeconds() >= expiry_seconds) { + return false; + } + + return true; +} + +const CryptoHandshakeMessage* +QuicCryptoClientConfig::CachedState::GetServerConfig() const { + if (server_config_.empty()) { + return NULL; + } + + if (!scfg_.get()) { + scfg_.reset(CryptoFramer::ParseMessage(server_config_)); + DCHECK(scfg_.get()); + } + return scfg_.get(); +} + +QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig( + StringPiece server_config, QuicWallTime now, string* error_details) { + const bool matches_existing = server_config == server_config_; + + // Even if the new server config matches the existing one, we still wish to + // reject it if it has expired. + scoped_ptr new_scfg_storage; + const CryptoHandshakeMessage* new_scfg; + + if (!matches_existing) { + new_scfg_storage.reset(CryptoFramer::ParseMessage(server_config)); + new_scfg = new_scfg_storage.get(); + } else { + new_scfg = GetServerConfig(); + } + + if (!new_scfg) { + *error_details = "SCFG invalid"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + uint64 expiry_seconds; + if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { + *error_details = "SCFG missing EXPY"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (now.ToUNIXSeconds() >= expiry_seconds) { + *error_details = "SCFG has expired"; + return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; + } + + if (!matches_existing) { + server_config_ = server_config.as_string(); + SetProofInvalid(); + scfg_.reset(new_scfg_storage.release()); + } + return QUIC_NO_ERROR; +} + +void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { + server_config_.clear(); + scfg_.reset(); + SetProofInvalid(); +} + +void QuicCryptoClientConfig::CachedState::SetProof(const vector& certs, + StringPiece signature) { + bool has_changed = + signature != server_config_sig_ || certs_.size() != certs.size(); + + if (!has_changed) { + for (size_t i = 0; i < certs_.size(); i++) { + if (certs_[i] != certs[i]) { + has_changed = true; + break; + } + } + } + + if (!has_changed) { + return; + } + + // If the proof has changed then it needs to be revalidated. + SetProofInvalid(); + certs_ = certs; + server_config_sig_ = signature.as_string(); +} + +void QuicCryptoClientConfig::CachedState::ClearProof() { + SetProofInvalid(); + certs_.clear(); + server_config_sig_.clear(); +} + +void QuicCryptoClientConfig::CachedState::SetProofValid() { + server_config_valid_ = true; +} + +void QuicCryptoClientConfig::CachedState::SetProofInvalid() { + server_config_valid_ = false; + ++generation_counter_; +} + +const string& QuicCryptoClientConfig::CachedState::server_config() const { + return server_config_; +} + +const string& +QuicCryptoClientConfig::CachedState::source_address_token() const { + return source_address_token_; +} + +const vector& QuicCryptoClientConfig::CachedState::certs() const { + return certs_; +} + +const string& QuicCryptoClientConfig::CachedState::signature() const { + return server_config_sig_; +} + +bool QuicCryptoClientConfig::CachedState::proof_valid() const { + return server_config_valid_; +} + +uint64 QuicCryptoClientConfig::CachedState::generation_counter() const { + return generation_counter_; +} + +const ProofVerifyDetails* +QuicCryptoClientConfig::CachedState::proof_verify_details() const { + return proof_verify_details_.get(); +} + +void QuicCryptoClientConfig::CachedState::set_source_address_token( + StringPiece token) { + source_address_token_ = token.as_string(); +} + +void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails( + ProofVerifyDetails* details) { + proof_verify_details_.reset(details); +} + +void QuicCryptoClientConfig::CachedState::InitializeFrom( + const QuicCryptoClientConfig::CachedState& other) { + DCHECK(server_config_.empty()); + DCHECK(!server_config_valid_); + server_config_ = other.server_config_; + source_address_token_ = other.source_address_token_; + certs_ = other.certs_; + server_config_sig_ = other.server_config_sig_; + server_config_valid_ = other.server_config_valid_; +} + +void QuicCryptoClientConfig::SetDefaults() { + // Version must be 0. + // TODO(agl): this version stuff is obsolete now. + version = QuicCryptoConfig::CONFIG_VERSION; + + // Key exchange methods. + kexs.resize(2); + kexs[0] = kC255; + kexs[1] = kP256; + + // Authenticated encryption algorithms. + aead.resize(1); + aead[0] = kAESG; +} + +QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( + const string& server_hostname) { + map::const_iterator it = + cached_states_.find(server_hostname); + if (it != cached_states_.end()) { + return it->second; + } + + CachedState* cached = new CachedState; + cached_states_.insert(make_pair(server_hostname, cached)); + return cached; +} + +void QuicCryptoClientConfig::FillInchoateClientHello( + const string& server_hostname, + const CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out) const { + out->set_tag(kCHLO); + out->set_minimum_size(kClientHelloMinimumSize); + + // Server name indication. We only send SNI if it's a valid domain name, as + // per the spec. + if (CryptoUtils::IsValidSNI(server_hostname)) { + out->SetStringPiece(kSNI, server_hostname); + } + out->SetValue(kVERS, version); + + if (!cached->source_address_token().empty()) { + out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); + } + + if (proof_verifier_.get()) { + // Don't request ECDSA proofs on platforms that do not support ECDSA + // certificates. + bool disableECDSA = false; +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) + disableECDSA = true; +#endif + if (disableECDSA) { + out->SetTaglist(kPDMD, kX59R, 0); + } else { + out->SetTaglist(kPDMD, kX509, 0); + } + + if (!cached->proof_valid()) { + // If we are expecting a certificate chain, double the size of the client + // hello so that the response from the server can be larger - hopefully + // including the whole certificate chain. + out->set_minimum_size(kClientHelloMinimumSize * 2); + } + } + + if (common_cert_sets) { + out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); + } + + const vector& certs = cached->certs(); + // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the + // client config is being used for multiple connections, another connection + // doesn't update the cached certificates and cause us to be unable to + // process the server's compressed certificate chain. + out_params->cached_certs = certs; + if (!certs.empty()) { + vector hashes; + hashes.reserve(certs.size()); + for (vector::const_iterator i = certs.begin(); + i != certs.end(); ++i) { + hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); + } + out->SetVector(kCCRT, hashes); + } +} + +QuicErrorCode QuicCryptoClientConfig::FillClientHello( + const string& server_hostname, + QuicGuid guid, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out, + string* error_details) const { + DCHECK(error_details != NULL); + + FillInchoateClientHello(server_hostname, cached, out_params, out); + + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + if (!scfg) { + // This should never happen as our caller should have checked + // cached->IsComplete() before calling this function. + *error_details = "Handshake not ready"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + StringPiece scid; + if (!scfg->GetStringPiece(kSCID, &scid)) { + *error_details = "SCFG missing SCID"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + out->SetStringPiece(kSCID, scid); + + const QuicTag* their_aeads; + const QuicTag* their_key_exchanges; + size_t num_their_aeads, num_their_key_exchanges; + if (scfg->GetTaglist(kAEAD, &their_aeads, + &num_their_aeads) != QUIC_NO_ERROR || + scfg->GetTaglist(kKEXS, &their_key_exchanges, + &num_their_key_exchanges) != QUIC_NO_ERROR) { + *error_details = "Missing AEAD or KEXS"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + size_t key_exchange_index; + if (!QuicUtils::FindMutualTag( + aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY, + &out_params->aead, NULL) || + !QuicUtils::FindMutualTag( + kexs, their_key_exchanges, num_their_key_exchanges, + QuicUtils::PEER_PRIORITY, &out_params->key_exchange, + &key_exchange_index)) { + *error_details = "Unsupported AEAD or KEXS"; + return QUIC_CRYPTO_NO_SUPPORT; + } + out->SetTaglist(kAEAD, out_params->aead, 0); + out->SetTaglist(kKEXS, out_params->key_exchange, 0); + + StringPiece public_value; + if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) != + QUIC_NO_ERROR) { + *error_details = "Missing public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + StringPiece orbit; + if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) { + *error_details = "SCFG missing OBIT"; + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce); + out->SetStringPiece(kNONC, out_params->client_nonce); + if (!out_params->server_nonce.empty()) { + out->SetStringPiece(kServerNonceTag, out_params->server_nonce); + } + + switch (out_params->key_exchange) { + case kC255: + out_params->client_key_exchange.reset(Curve25519KeyExchange::New( + Curve25519KeyExchange::NewPrivateKey(rand))); + break; + case kP256: + out_params->client_key_exchange.reset(P256KeyExchange::New( + P256KeyExchange::NewPrivateKey())); + break; + default: + DCHECK(false); + *error_details = "Configured to support an unknown key exchange"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + if (!out_params->client_key_exchange->CalculateSharedKey( + public_value, &out_params->initial_premaster_secret)) { + *error_details = "Key exchange failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); + + bool do_channel_id = false; + if (channel_id_signer_.get()) { + const QuicTag* their_proof_demands; + size_t num_their_proof_demands; + if (scfg->GetTaglist(kPDMD, &their_proof_demands, + &num_their_proof_demands) == QUIC_NO_ERROR) { + for (size_t i = 0; i < num_their_proof_demands; i++) { + if (their_proof_demands[i] == kCHID) { + do_channel_id = true; + break; + } + } + } + } + + if (do_channel_id) { + // In order to calculate the encryption key for the CETV block we need to + // serialise the client hello as it currently is (i.e. without the CETV + // block). For this, the client hello is serialized without padding. + const size_t orig_min_size = out->minimum_size(); + out->set_minimum_size(0); + + CryptoHandshakeMessage cetv; + cetv.set_tag(kCETV); + + string hkdf_input; + const QuicData& client_hello_serialized = out->GetSerialized(); + hkdf_input.append(QuicCryptoConfig::kCETVLabel, + strlen(QuicCryptoConfig::kCETVLabel) + 1); + hkdf_input.append(reinterpret_cast(&guid), sizeof(guid)); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(cached->server_config()); + + string key, signature; + if (!channel_id_signer_->Sign(server_hostname, hkdf_input, + &key, &signature)) { + *error_details = "Channel ID signature failed"; + return QUIC_INVALID_CHANNEL_ID_SIGNATURE; + } + + cetv.SetStringPiece(kCIDK, key); + cetv.SetStringPiece(kCIDS, signature); + + CrypterPair crypters; + if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, + out_params->aead, out_params->client_nonce, + out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + const QuicData& cetv_plaintext = cetv.GetSerialized(); + scoped_ptr cetv_ciphertext(crypters.encrypter->EncryptPacket( + 0 /* sequence number */, + StringPiece() /* associated data */, + cetv_plaintext.AsStringPiece())); + if (!cetv_ciphertext.get()) { + *error_details = "Packet encryption failed"; + return QUIC_ENCRYPTION_FAILURE; + } + + out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece()); + out->MarkDirty(); + + out->set_minimum_size(orig_min_size); + } + + out_params->hkdf_input_suffix.clear(); + out_params->hkdf_input_suffix.append(reinterpret_cast(&guid), + sizeof(guid)); + const QuicData& client_hello_serialized = out->GetSerialized(); + out_params->hkdf_input_suffix.append(client_hello_serialized.data(), + client_hello_serialized.length()); + out_params->hkdf_input_suffix.append(cached->server_config()); + + string hkdf_input; + const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; + hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); + hkdf_input.append(out_params->hkdf_input_suffix); + + if (!CryptoUtils::DeriveKeys( + out_params->initial_premaster_secret, out_params->aead, + out_params->client_nonce, out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &out_params->initial_crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessRejection( + const CryptoHandshakeMessage& rej, + QuicWallTime now, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + string* error_details) { + DCHECK(error_details != NULL); + + if (rej.tag() != kREJ) { + *error_details = "Message is not REJ"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + StringPiece scfg; + if (!rej.GetStringPiece(kSCFG, &scfg)) { + *error_details = "Missing SCFG"; + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + StringPiece token; + if (rej.GetStringPiece(kSourceAddressTokenTag, &token)) { + cached->set_source_address_token(token); + } + + StringPiece nonce; + if (rej.GetStringPiece(kServerNonceTag, &nonce)) { + out_params->server_nonce = nonce.as_string(); + } + + StringPiece proof, cert_bytes; + bool has_proof = rej.GetStringPiece(kPROF, &proof); + bool has_cert = rej.GetStringPiece(kCertificateTag, &cert_bytes); + if (has_proof && has_cert) { + vector certs; + if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs, + common_cert_sets, &certs)) { + *error_details = "Certificate data invalid"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + cached->SetProof(certs, proof); + } else { + cached->ClearProof(); + if (has_proof && !has_cert) { + *error_details = "Certificate missing"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!has_proof && has_cert) { + *error_details = "Proof missing"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + QuicGuid guid, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + string* error_details) { + DCHECK(error_details != NULL); + + if (server_hello.tag() != kSHLO) { + *error_details = "Bad tag"; + return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; + } + + // Learn about updated source address tokens. + StringPiece token; + if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) { + cached->set_source_address_token(token); + } + + // TODO(agl): + // learn about updated SCFGs. + + StringPiece public_value; + if (!server_hello.GetStringPiece(kPUBS, &public_value)) { + *error_details = "server hello missing forward secure public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!out_params->client_key_exchange->CalculateSharedKey( + public_value, &out_params->forward_secure_premaster_secret)) { + *error_details = "Key exchange failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + string hkdf_input; + const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; + hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len); + hkdf_input.append(out_params->hkdf_input_suffix); + + if (!CryptoUtils::DeriveKeys( + out_params->forward_secure_premaster_secret, out_params->aead, + out_params->client_nonce, out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &out_params->forward_secure_crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + return QUIC_NO_ERROR; +} + +ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { + return proof_verifier_.get(); +} + +void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { + proof_verifier_.reset(verifier); +} + +ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const { + return channel_id_signer_.get(); +} + +void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) { + channel_id_signer_.reset(signer); +} + +void QuicCryptoClientConfig::InitializeFrom( + const std::string& server_hostname, + const std::string& canonical_server_hostname, + QuicCryptoClientConfig* canonical_crypto_config) { + CachedState* canonical_cached = + canonical_crypto_config->LookupOrCreate(canonical_server_hostname); + if (!canonical_cached->proof_valid()) { + return; + } + CachedState* cached = LookupOrCreate(server_hostname); + cached->InitializeFrom(*canonical_cached); +} + +} // namespace net diff --git a/net/quic/crypto/quic_crypto_client_config.h b/net/quic/crypto/quic_crypto_client_config.h new file mode 100644 index 0000000..8164b20 --- /dev/null +++ b/net/quic/crypto/quic_crypto_client_config.h @@ -0,0 +1,208 @@ +// Copyright 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. + +#ifndef NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ +#define NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ + +#include +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +// QuicCryptoClientConfig contains crypto-related configuration settings for a +// client. Note that this object isn't thread-safe. It's designed to be used on +// a single thread at a time. +class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { + public: + // A CachedState contains the information that the client needs in order to + // perform a 0-RTT handshake with a server. This information can be reused + // over several connections to the same server. + class NET_EXPORT_PRIVATE CachedState { + public: + CachedState(); + ~CachedState(); + + // IsComplete returns true if this object contains enough information to + // perform a handshake with the server. |now| is used to judge whether any + // cached server config has expired. + bool IsComplete(QuicWallTime now) const; + + // GetServerConfig returns the parsed contents of |server_config|, or NULL + // if |server_config| is empty. The return value is owned by this object + // and is destroyed when this object is. + const CryptoHandshakeMessage* GetServerConfig() const; + + // SetServerConfig checks that |server_config| parses correctly and stores + // it in |server_config_|. |now| is used to judge whether |server_config| + // has expired. + QuicErrorCode SetServerConfig(base::StringPiece server_config, + QuicWallTime now, + std::string* error_details); + + // InvalidateServerConfig clears the cached server config (if any). + void InvalidateServerConfig(); + + // SetProof stores a certificate chain and signature. + void SetProof(const std::vector& certs, + base::StringPiece signature); + + // Clears the certificate chain and signature and invalidates the proof. + void ClearProof(); + + // SetProofValid records that the certificate chain and signature have been + // validated and that it's safe to assume that the server is legitimate. + // (Note: this does not check the chain or signature.) + void SetProofValid(); + + // If the server config or the proof has changed then it needs to be + // revalidated. Helper function to keep server_config_valid_ and + // generation_counter_ in sync. + void SetProofInvalid(); + + const std::string& server_config() const; + const std::string& source_address_token() const; + const std::vector& certs() const; + const std::string& signature() const; + bool proof_valid() const; + uint64 generation_counter() const; + const ProofVerifyDetails* proof_verify_details() const; + + void set_source_address_token(base::StringPiece token); + + // SetProofVerifyDetails takes ownership of |details|. + void SetProofVerifyDetails(ProofVerifyDetails* details); + + // Copy the |server_config_|, |source_address_token_|, |certs_| and + // |server_config_sig_| from the |other|. The remaining fields, + // |generation_counter_|, |proof_verify_details_|, and |scfg_| remain + // unchanged. + void InitializeFrom(const CachedState& other); + + private: + std::string server_config_id_; // An opaque id from the server. + std::string server_config_; // A serialized handshake message. + std::string source_address_token_; // An opaque proof of IP ownership. + std::vector certs_; // A list of certificates in leaf-first + // order. + std::string server_config_sig_; // A signature of |server_config_|. + bool server_config_valid_; // True if |server_config_| is correctly + // signed and |certs_| has been + // validated. + // Generation counter associated with the |server_config_|, |certs_| and + // |server_config_sig_| combination. It is incremented whenever we set + // server_config_valid_ to false. + uint64 generation_counter_; + + scoped_ptr proof_verify_details_; + + // scfg contains the cached, parsed value of |server_config|. + mutable scoped_ptr scfg_; + + DISALLOW_COPY_AND_ASSIGN(CachedState); + }; + + QuicCryptoClientConfig(); + ~QuicCryptoClientConfig(); + + // Sets the members to reasonable, default values. + void SetDefaults(); + + // LookupOrCreate returns a CachedState for the given hostname. If no such + // CachedState currently exists, it will be created and cached. + CachedState* LookupOrCreate(const std::string& server_hostname); + + // FillInchoateClientHello sets |out| to be a CHLO message that elicits a + // source-address token or SCFG from a server. If |cached| is non-NULL, the + // source-address token will be taken from it. |out_params| is used in order + // to store the cached certs that were sent as hints to the server in + // |out_params->cached_certs|. + void FillInchoateClientHello(const std::string& server_hostname, + const CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out) const; + + // FillClientHello sets |out| to be a CHLO message based on the configuration + // of this object. This object must have cached enough information about + // |server_hostname| in order to perform a handshake. This can be checked + // with the |IsComplete| member of |CachedState|. + // + // |clock| and |rand| are used to generate the nonce and |out_params| is + // filled with the results of the handshake that the server is expected to + // accept. + QuicErrorCode FillClientHello(const std::string& server_hostname, + QuicGuid guid, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out, + std::string* error_details) const; + + // ProcessRejection processes a REJ message from a server and updates the + // cached information about that server. After this, |IsComplete| may return + // true for that server's CachedState. If the rejection message contains + // state about a future handshake (i.e. an nonce value from the server), then + // it will be saved in |out_params|. |now| is used to judge whether the + // server config in the rejection message has expired. + QuicErrorCode ProcessRejection(const CryptoHandshakeMessage& rej, + QuicWallTime now, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + std::string* error_details); + + // ProcessServerHello processes the message in |server_hello|, updates the + // cached information about that server, writes the negotiated parameters to + // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable + // then it puts an error message in |error_details| and returns an error + // code. + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + QuicGuid guid, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + std::string* error_details); + + ProofVerifier* proof_verifier() const; + + // SetProofVerifier takes ownership of a |ProofVerifier| that clients are + // free to use in order to verify certificate chains from servers. If a + // ProofVerifier is set then the client will request a certificate chain from + // the server. + void SetProofVerifier(ProofVerifier* verifier); + + ChannelIDSigner* channel_id_signer() const; + + // SetChannelIDSigner sets a ChannelIDSigner that will be called when the + // server supports channel IDs to sign a message proving possession of the + // given ChannelID. This object takes ownership of |signer|. + void SetChannelIDSigner(ChannelIDSigner* signer); + + // Initialize the CachedState from |canonical_crypto_config| for the + // |canonical_server_hostname| as the initial CachedState for + // |server_hostname|. We will copy config data only if + // |canonical_crypto_config| has valid proof. + void InitializeFrom(const std::string& server_hostname, + const std::string& canonical_server_hostname, + QuicCryptoClientConfig* canonical_crypto_config); + + private: + // cached_states_ maps from the server hostname to the cached information + // about that server. + std::map cached_states_; + + scoped_ptr proof_verifier_; + scoped_ptr channel_id_signer_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc new file mode 100644 index 0000000..2380058 --- /dev/null +++ b/net/quic/crypto/quic_crypto_server_config.cc @@ -0,0 +1,1118 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/quic_crypto_server_config.h" + +#include +#include + +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "crypto/hkdf.h" +#include "crypto/secure_hash.h" +#include "net/base/net_util.h" +#include "net/quic/crypto/aes_128_gcm_12_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" +#include "net/quic/crypto/cert_compressor.h" +#include "net/quic/crypto/channel_id.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_server_config_protobuf.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/curve25519_key_exchange.h" +#include "net/quic/crypto/ephemeral_key_source.h" +#include "net/quic/crypto/key_exchange.h" +#include "net/quic/crypto/p256_key_exchange.h" +#include "net/quic/crypto/proof_source.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/crypto/source_address_token.h" +#include "net/quic/crypto/strike_register.h" +#include "net/quic/quic_clock.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using crypto::SecureHash; +using std::map; +using std::string; +using std::vector; + +namespace net { + +// static +const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; + +QuicCryptoServerConfig::ConfigOptions::ConfigOptions() + : expiry_time(QuicWallTime::Zero()), + channel_id_enabled(false), + p256(false) {} + +QuicCryptoServerConfig::QuicCryptoServerConfig( + StringPiece source_address_token_secret, + QuicRandom* rand) + : replay_protection_(true), + configs_lock_(), + primary_config_(NULL), + next_config_promotion_time_(QuicWallTime::Zero()), + strike_register_lock_(), + server_nonce_strike_register_lock_(), + strike_register_no_startup_period_(false), + strike_register_max_entries_(1 << 10), + strike_register_window_secs_(600), + source_address_token_future_secs_(3600), + source_address_token_lifetime_secs_(86400), + server_nonce_strike_register_max_entries_(1 << 10), + server_nonce_strike_register_window_secs_(120) { + crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, + "QUIC source address token key", + CryptoSecretBoxer::GetKeySize(), + 0 /* no fixed IV needed */); + source_address_token_boxer_.SetKey(hkdf.server_write_key()); + + // Generate a random key and orbit for server nonces. + rand->RandBytes(server_nonce_orbit_, sizeof(server_nonce_orbit_)); + const size_t key_size = server_nonce_boxer_.GetKeySize(); + scoped_ptr key_bytes(new uint8[key_size]); + rand->RandBytes(key_bytes.get(), key_size); + + server_nonce_boxer_.SetKey( + StringPiece(reinterpret_cast(key_bytes.get()), key_size)); +} + +QuicCryptoServerConfig::~QuicCryptoServerConfig() { + primary_config_ = NULL; +} + +// static +QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options) { + CryptoHandshakeMessage msg; + + const string curve25519_private_key = + Curve25519KeyExchange::NewPrivateKey(rand); + scoped_ptr curve25519( + Curve25519KeyExchange::New(curve25519_private_key)); + StringPiece curve25519_public_value = curve25519->public_value(); + + string encoded_public_values; + // First three bytes encode the length of the public value. + encoded_public_values.push_back(curve25519_public_value.size()); + encoded_public_values.push_back(curve25519_public_value.size() >> 8); + encoded_public_values.push_back(curve25519_public_value.size() >> 16); + encoded_public_values.append(curve25519_public_value.data(), + curve25519_public_value.size()); + + string p256_private_key; + if (options.p256) { + p256_private_key = P256KeyExchange::NewPrivateKey(); + scoped_ptr p256(P256KeyExchange::New(p256_private_key)); + StringPiece p256_public_value = p256->public_value(); + + encoded_public_values.push_back(p256_public_value.size()); + encoded_public_values.push_back(p256_public_value.size() >> 8); + encoded_public_values.push_back(p256_public_value.size() >> 16); + encoded_public_values.append(p256_public_value.data(), + p256_public_value.size()); + } + + msg.set_tag(kSCFG); + if (options.p256) { + msg.SetTaglist(kKEXS, kC255, kP256, 0); + } else { + msg.SetTaglist(kKEXS, kC255, 0); + } + msg.SetTaglist(kAEAD, kAESG, 0); + msg.SetValue(kVERS, static_cast(0)); + msg.SetStringPiece(kPUBS, encoded_public_values); + + if (options.expiry_time.IsZero()) { + const QuicWallTime now = clock->WallNow(); + const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds( + 60 * 60 * 24 * 180 /* 180 days, ~six months */)); + const uint64 expiry_seconds = expiry.ToUNIXSeconds(); + msg.SetValue(kEXPY, expiry_seconds); + } else { + msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds()); + } + + char orbit_bytes[kOrbitSize]; + if (options.orbit.size() == sizeof(orbit_bytes)) { + memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes)); + } else { + DCHECK(options.orbit.empty()); + rand->RandBytes(orbit_bytes, sizeof(orbit_bytes)); + } + msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes))); + + if (options.channel_id_enabled) { + msg.SetTaglist(kPDMD, kCHID, 0); + } + + if (options.id.empty()) { + // We need to ensure that the SCID changes whenever the server config does + // thus we make it a hash of the rest of the server config. + scoped_ptr serialized( + CryptoFramer::ConstructHandshakeMessage(msg)); + scoped_ptr hash(SecureHash::Create(SecureHash::SHA256)); + hash->Update(serialized->data(), serialized->length()); + + char scid_bytes[16]; + hash->Finish(scid_bytes, sizeof(scid_bytes)); + msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes))); + } else { + msg.SetStringPiece(kSCID, options.id); + } + // Don't put new tags below this point. The SCID generation should hash over + // everything but itself and so extra tags should be added prior to the + // preceeding if block. + + scoped_ptr serialized(CryptoFramer::ConstructHandshakeMessage(msg)); + + scoped_ptr config(new QuicServerConfigProtobuf); + config->set_config(serialized->AsStringPiece()); + QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key(); + curve25519_key->set_tag(kC255); + curve25519_key->set_private_key(curve25519_private_key); + + if (options.p256) { + QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key(); + p256_key->set_tag(kP256); + p256_key->set_private_key(p256_private_key); + } + + return config.release(); +} + +CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( + QuicServerConfigProtobuf* protobuf, + const QuicWallTime now) { + scoped_ptr msg( + CryptoFramer::ParseMessage(protobuf->config())); + + if (!msg.get()) { + LOG(WARNING) << "Failed to parse server config message"; + return NULL; + } + + scoped_refptr config(ParseConfigProtobuf(protobuf)); + if (!config.get()) { + LOG(WARNING) << "Failed to parse server config message"; + return NULL; + } + + { + base::AutoLock locked(configs_lock_); + if (configs_.find(config->id) != configs_.end()) { + LOG(WARNING) << "Failed to add config because another with the same " + "server config id already exists: " + << base::HexEncode(config->id.data(), config->id.size()); + return NULL; + } + + configs_[config->id] = config; + SelectNewPrimaryConfig(now); + DCHECK(primary_config_.get()); + } + + return msg.release(); +} + +CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options) { + scoped_ptr config( + DefaultConfig(rand, clock, options)); + return AddConfig(config.get(), clock->WallNow()); +} + +bool QuicCryptoServerConfig::SetConfigs( + const vector& protobufs, + const QuicWallTime now) { + vector > new_configs; + bool ok = true; + + for (vector::const_iterator i = protobufs.begin(); + i != protobufs.end(); ++i) { + scoped_refptr config(ParseConfigProtobuf(*i)); + if (!config.get()) { + ok = false; + break; + } + new_configs.push_back(config); + } + + if (!ok) { + LOG(WARNING) << "Rejecting QUIC configs because of above errors"; + } else { + base::AutoLock locked(configs_lock_); + typedef ConfigMap::iterator ConfigMapIterator; + vector to_delete; + + DCHECK_EQ(protobufs.size(), new_configs.size()); + + // First, look for any configs that have been removed. + for (ConfigMapIterator i = configs_.begin(); + i != configs_.end(); ++i) { + const scoped_refptr old_config = i->second; + bool found = false; + + for (vector >::const_iterator j = + new_configs.begin(); + j != new_configs.end(); ++j) { + if ((*j)->id == old_config->id) { + found = true; + break; + } + } + + if (!found) { + // We cannot remove the primary config. This has probably happened + // because our source of config information failed for a time and we're + // suddenly seeing a jump in time. No matter - we'll configure a new + // primary config and then we'll be able to delete it next time. + if (!old_config->is_primary) { + to_delete.push_back(i); + } + } + } + + for (vector::const_iterator i = to_delete.begin(); + i != to_delete.end(); ++i) { + configs_.erase(*i); + } + + // Find any configs that need to be added. + for (vector >::const_iterator i = new_configs.begin(); + i != new_configs.end(); ++i) { + const scoped_refptr new_config = *i; + if (configs_.find(new_config->id) != configs_.end()) { + continue; + } + + configs_[new_config->id] = new_config; + } + + SelectNewPrimaryConfig(now); + } + + return ok; +} + +// ClientHelloInfo contains information about a client hello message that is +// only kept for as long as it's being processed. +struct ClientHelloInfo { + ClientHelloInfo(const IPEndPoint& in_client_ip, QuicWallTime in_now) + : client_ip(in_client_ip), + now(in_now), + valid_source_address_token(false), + client_nonce_well_formed(false), + unique(false) {} + + // Inputs to EvaluateClientHello. + const IPEndPoint client_ip; + const QuicWallTime now; + + // Outputs from EvaluateClientHello. + bool valid_source_address_token; + bool client_nonce_well_formed; + bool unique; + StringPiece sni; + StringPiece client_nonce; + StringPiece server_nonce; +}; + +QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( + const CryptoHandshakeMessage& client_hello, + QuicGuid guid, + const IPEndPoint& client_ip, + const QuicClock* clock, + QuicRandom* rand, + QuicCryptoNegotiatedParameters *params, + CryptoHandshakeMessage* out, + string* error_details) const { + DCHECK(error_details); + + StringPiece requested_scid; + client_hello.GetStringPiece(kSCID, &requested_scid); + const QuicWallTime now(clock->WallNow()); + + scoped_refptr requested_config; + scoped_refptr primary_config; + { + base::AutoLock locked(configs_lock_); + + if (!primary_config_.get()) { + *error_details = "No configurations loaded"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + if (!next_config_promotion_time_.IsZero() && + next_config_promotion_time_.IsAfter(now)) { + SelectNewPrimaryConfig(now); + } + + primary_config = primary_config_; + + if (!requested_scid.empty()) { + ConfigMap::const_iterator it = configs_.find(requested_scid.as_string()); + if (it != configs_.end()) { + // We'll use the config that the client requested in order to do + // key-agreement. Otherwise we'll give it a copy of |primary_config_| + // to use. + requested_config = it->second; + } + } + } + + ClientHelloInfo info(client_ip, now); + QuicErrorCode error = EvaluateClientHello( + client_hello, primary_config->orbit, &info, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + out->Clear(); + + if (!info.valid_source_address_token || + !info.client_nonce_well_formed || + !info.unique || + !requested_config.get()) { + BuildRejection(primary_config.get(), client_hello, info, rand, out); + return QUIC_NO_ERROR; + } + + const QuicTag* their_aeads; + const QuicTag* their_key_exchanges; + size_t num_their_aeads, num_their_key_exchanges; + if (client_hello.GetTaglist(kAEAD, &their_aeads, + &num_their_aeads) != QUIC_NO_ERROR || + client_hello.GetTaglist(kKEXS, &their_key_exchanges, + &num_their_key_exchanges) != QUIC_NO_ERROR || + num_their_aeads != 1 || + num_their_key_exchanges != 1) { + *error_details = "Missing or invalid AEAD or KEXS"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + size_t key_exchange_index; + if (!QuicUtils::FindMutualTag(requested_config->aead, their_aeads, + num_their_aeads, QuicUtils::LOCAL_PRIORITY, + ¶ms->aead, NULL) || + !QuicUtils::FindMutualTag( + requested_config->kexs, their_key_exchanges, num_their_key_exchanges, + QuicUtils::LOCAL_PRIORITY, ¶ms->key_exchange, + &key_exchange_index)) { + *error_details = "Unsupported AEAD or KEXS"; + return QUIC_CRYPTO_NO_SUPPORT; + } + + StringPiece public_value; + if (!client_hello.GetStringPiece(kPUBS, &public_value)) { + *error_details = "Missing public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + const KeyExchange* key_exchange = + requested_config->key_exchanges[key_exchange_index]; + if (!key_exchange->CalculateSharedKey(public_value, + ¶ms->initial_premaster_secret)) { + *error_details = "Invalid public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!info.sni.empty()) { + scoped_ptr sni_tmp(new char[info.sni.length() + 1]); + memcpy(sni_tmp.get(), info.sni.data(), info.sni.length()); + sni_tmp[info.sni.length()] = 0; + params->sni = CryptoUtils::NormalizeHostname(sni_tmp.get()); + } + + string hkdf_suffix; + const QuicData& client_hello_serialized = client_hello.GetSerialized(); + hkdf_suffix.reserve(sizeof(guid) + client_hello_serialized.length() + + requested_config->serialized.size()); + hkdf_suffix.append(reinterpret_cast(&guid), sizeof(guid)); + hkdf_suffix.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_suffix.append(requested_config->serialized); + + StringPiece cetv_ciphertext; + if (requested_config->channel_id_enabled && + client_hello.GetStringPiece(kCETV, &cetv_ciphertext)) { + CryptoHandshakeMessage client_hello_copy(client_hello); + client_hello_copy.Erase(kCETV); + client_hello_copy.Erase(kPAD); + + const QuicData& client_hello_serialized = client_hello_copy.GetSerialized(); + string hkdf_input; + hkdf_input.append(QuicCryptoConfig::kCETVLabel, + strlen(QuicCryptoConfig::kCETVLabel) + 1); + hkdf_input.append(reinterpret_cast(&guid), sizeof(guid)); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(requested_config->serialized); + + CrypterPair crypters; + if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, + info.client_nonce, info.server_nonce, + hkdf_input, CryptoUtils::SERVER, &crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + scoped_ptr cetv_plaintext(crypters.decrypter->DecryptPacket( + 0 /* sequence number */, StringPiece() /* associated data */, + cetv_ciphertext)); + if (!cetv_plaintext.get()) { + *error_details = "CETV decryption failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + scoped_ptr cetv(CryptoFramer::ParseMessage( + cetv_plaintext->AsStringPiece())); + if (!cetv.get()) { + *error_details = "CETV parse error"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + StringPiece key, signature; + if (cetv->GetStringPiece(kCIDK, &key) && + cetv->GetStringPiece(kCIDS, &signature)) { + if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) { + *error_details = "ChannelID signature failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + params->channel_id = key.as_string(); + } + } + + string hkdf_input; + size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; + hkdf_input.reserve(label_len + hkdf_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); + hkdf_input.append(hkdf_suffix); + + if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, + info.client_nonce, info.server_nonce, hkdf_input, + CryptoUtils::SERVER, + ¶ms->initial_crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + string forward_secure_public_value; + if (ephemeral_key_source_.get()) { + params->forward_secure_premaster_secret = + ephemeral_key_source_->CalculateForwardSecureKey( + key_exchange, rand, clock->ApproximateNow(), public_value, + &forward_secure_public_value); + } else { + scoped_ptr forward_secure_key_exchange( + key_exchange->NewKeyPair(rand)); + forward_secure_public_value = + forward_secure_key_exchange->public_value().as_string(); + if (!forward_secure_key_exchange->CalculateSharedKey( + public_value, ¶ms->forward_secure_premaster_secret)) { + *error_details = "Invalid public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + } + + string forward_secure_hkdf_input; + label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; + forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size()); + forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, + label_len); + forward_secure_hkdf_input.append(hkdf_suffix); + + if (!CryptoUtils::DeriveKeys( + params->forward_secure_premaster_secret, params->aead, + info.client_nonce, info.server_nonce, forward_secure_hkdf_input, + CryptoUtils::SERVER, ¶ms->forward_secure_crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + out->set_tag(kSHLO); + out->SetStringPiece(kSourceAddressTokenTag, + NewSourceAddressToken(client_ip, rand, info.now)); + out->SetStringPiece(kPUBS, forward_secure_public_value); + return QUIC_NO_ERROR; +} + +// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for +// Config's based on their primary_time. +// static +bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan( + const scoped_refptr& a, + const scoped_refptr& b) { + return a->primary_time.IsBefore(b->primary_time); +} + +void QuicCryptoServerConfig::SelectNewPrimaryConfig( + const QuicWallTime now) const { + vector > configs; + configs.reserve(configs_.size()); + scoped_refptr first_config = NULL; + + for (ConfigMap::const_iterator it = configs_.begin(); + it != configs_.end(); ++it) { + const scoped_refptr config(it->second); + if (!first_config.get()) { + first_config = config; + } + if (config->primary_time.IsZero()) { + continue; + } + configs.push_back(it->second); + } + + if (configs.size() == 0) { + // Tests don't set |primary_time_|. For that case we promote the first + // Config and leave it as primary forever. + if (!primary_config_.get() && first_config.get()) { + primary_config_ = first_config; + primary_config_->is_primary = true; + } + return; + } + + std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan); + + for (size_t i = 0; i < configs.size(); ++i) { + const scoped_refptr config(configs[i]); + + if (!config->primary_time.IsAfter(now)) { + continue; + } + + // This is the first config with a primary_time in the future. Thus the + // previous Config should be the primary and this one should determine the + // next_config_promotion_time_. + scoped_refptr new_primary; + if (i == 0) { + // There was no previous Config, so this will have to be primary. + new_primary = config; + + // We need the primary_time of the next config. + if (configs.size() > 1) { + next_config_promotion_time_ = configs[1]->primary_time; + } else { + next_config_promotion_time_ = QuicWallTime::Zero(); + } + } else { + new_primary = configs[i - 1]; + next_config_promotion_time_ = config->primary_time; + } + + if (primary_config_.get()) { + primary_config_->is_primary = false; + } + primary_config_ = new_primary; + new_primary->is_primary = true; + + return; + } + + // All config's primary times are in the past. We should make the most recent + // primary. + scoped_refptr new_primary = configs[configs.size() - 1]; + if (primary_config_.get()) { + primary_config_->is_primary = false; + } + primary_config_ = new_primary; + new_primary->is_primary = true; + next_config_promotion_time_ = QuicWallTime::Zero(); +} + +QuicErrorCode QuicCryptoServerConfig::EvaluateClientHello( + const CryptoHandshakeMessage& client_hello, + const uint8* orbit, + ClientHelloInfo* info, + string* error_details) const { + if (client_hello.size() < kClientHelloMinimumSize) { + *error_details = "Client hello too small"; + return QUIC_CRYPTO_INVALID_VALUE_LENGTH; + } + + StringPiece srct; + if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct) && + ValidateSourceAddressToken(srct, info->client_ip, info->now)) { + info->valid_source_address_token = true; + } + + if (client_hello.GetStringPiece(kSNI, &info->sni) && + !CryptoUtils::IsValidSNI(info->sni)) { + *error_details = "Invalid SNI name"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + // The client nonce is used first to try and establish uniqueness. + bool unique_by_strike_register = false; + + if (client_hello.GetStringPiece(kNONC, &info->client_nonce) && + info->client_nonce.size() == kNonceSize) { + info->client_nonce_well_formed = true; + if (replay_protection_) { + base::AutoLock auto_lock(strike_register_lock_); + + if (strike_register_.get() == NULL) { + strike_register_.reset(new StrikeRegister( + strike_register_max_entries_, + static_cast(info->now.ToUNIXSeconds()), + strike_register_window_secs_, + orbit, + strike_register_no_startup_period_ ? + StrikeRegister::NO_STARTUP_PERIOD_NEEDED : + StrikeRegister::DENY_REQUESTS_AT_STARTUP)); + } + + unique_by_strike_register = strike_register_->Insert( + reinterpret_cast(info->client_nonce.data()), + static_cast(info->now.ToUNIXSeconds())); + } + } + + client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce); + + // If the client nonce didn't establish uniqueness then an echoed server + // nonce may. + bool unique_by_server_nonce = false; + if (replay_protection_ && + !unique_by_strike_register && + !info->server_nonce.empty()) { + unique_by_server_nonce = ValidateServerNonce(info->server_nonce, info->now); + } + + info->unique = !replay_protection_ || + unique_by_strike_register || + unique_by_server_nonce; + + return QUIC_NO_ERROR; +} + +void QuicCryptoServerConfig::BuildRejection( + const scoped_refptr& config, + const CryptoHandshakeMessage& client_hello, + const ClientHelloInfo& info, + QuicRandom* rand, + CryptoHandshakeMessage* out) const { + out->set_tag(kREJ); + out->SetStringPiece(kSCFG, config->serialized); + out->SetStringPiece(kSourceAddressTokenTag, + NewSourceAddressToken(info.client_ip, rand, info.now)); + if (replay_protection_) { + out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now)); + } + + // The client may have requested a certificate chain. + const QuicTag* their_proof_demands; + size_t num_their_proof_demands; + + if (proof_source_.get() == NULL || + client_hello.GetTaglist(kPDMD, &their_proof_demands, + &num_their_proof_demands) != + QUIC_NO_ERROR) { + return; + } + + bool x509_supported = false, x509_ecdsa_supported = false; + for (size_t i = 0; i < num_their_proof_demands; i++) { + switch (their_proof_demands[i]) { + case kX509: + x509_supported = true; + x509_ecdsa_supported = true; + break; + case kX59R: + x509_supported = true; + break; + } + } + + if (!x509_supported) { + return; + } + + const vector* certs; + string signature; + if (!proof_source_->GetProof(info.sni.as_string(), config->serialized, + x509_ecdsa_supported, &certs, &signature)) { + return; + } + + StringPiece their_common_set_hashes; + StringPiece their_cached_cert_hashes; + client_hello.GetStringPiece(kCCS, &their_common_set_hashes); + client_hello.GetStringPiece(kCCRT, &their_cached_cert_hashes); + + const string compressed = CertCompressor::CompressChain( + *certs, their_common_set_hashes, their_cached_cert_hashes, + config->common_cert_sets); + + // kREJOverheadBytes is a very rough estimate of how much of a REJ + // message is taken up by things other than the certificates. + // STK: 56 bytes + // SNO: 56 bytes + // SCFG + // SCID: 16 bytes + // PUBS: 38 bytes + const size_t kREJOverheadBytes = 166; + // kMultiplier is the multiple of the CHLO message size that a REJ message + // must stay under when the client doesn't present a valid source-address + // token. + const size_t kMultiplier = 2; + // max_unverified_size is the number of bytes that the certificate chain + // and signature can consume before we will demand a valid source-address + // token. + const size_t max_unverified_size = + client_hello.size() * kMultiplier - kREJOverheadBytes; + COMPILE_ASSERT(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes, + overhead_calculation_may_underflow); + if (info.valid_source_address_token || + signature.size() + compressed.size() < max_unverified_size) { + out->SetStringPiece(kCertificateTag, compressed); + out->SetStringPiece(kPROF, signature); + } +} + +scoped_refptr +QuicCryptoServerConfig::ParseConfigProtobuf( + QuicServerConfigProtobuf* protobuf) { + scoped_ptr msg( + CryptoFramer::ParseMessage(protobuf->config())); + + if (msg->tag() != kSCFG) { + LOG(WARNING) << "Server config message has tag " << msg->tag() + << " expected " << kSCFG; + return NULL; + } + + scoped_refptr config(new Config); + config->serialized = protobuf->config(); + + if (protobuf->has_primary_time()) { + config->primary_time = + QuicWallTime::FromUNIXSeconds(protobuf->primary_time()); + } + + StringPiece scid; + if (!msg->GetStringPiece(kSCID, &scid)) { + LOG(WARNING) << "Server config message is missing SCID"; + return NULL; + } + config->id = scid.as_string(); + + const QuicTag* aead_tags; + size_t aead_len; + if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) { + LOG(WARNING) << "Server config message is missing AEAD"; + return NULL; + } + config->aead = vector(aead_tags, aead_tags + aead_len); + + const QuicTag* kexs_tags; + size_t kexs_len; + if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) { + LOG(WARNING) << "Server config message is missing KEXS"; + return NULL; + } + + StringPiece orbit; + if (!msg->GetStringPiece(kORBT, &orbit)) { + LOG(WARNING) << "Server config message is missing OBIT"; + return NULL; + } + + if (orbit.size() != kOrbitSize) { + LOG(WARNING) << "Orbit value in server config is the wrong length." + " Got " << orbit.size() << " want " << kOrbitSize; + return NULL; + } + COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size); + memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); + + { + base::AutoLock locked(strike_register_lock_); + if (strike_register_.get()) { + const uint8* orbit = strike_register_->orbit(); + if (0 != memcmp(orbit, config->orbit, kOrbitSize)) { + LOG(WARNING) + << "Server config has different orbit than current config. " + "Switching orbits at run-time is not supported."; + return NULL; + } + } + } + + if (kexs_len != protobuf->key_size()) { + LOG(WARNING) << "Server config has " << kexs_len + << " key exchange methods configured, but " + << protobuf->key_size() << " private keys"; + return NULL; + } + + const QuicTag* proof_demand_tags; + size_t num_proof_demand_tags; + if (msg->GetTaglist(kPDMD, &proof_demand_tags, &num_proof_demand_tags) == + QUIC_NO_ERROR) { + for (size_t i = 0; i < num_proof_demand_tags; i++) { + if (proof_demand_tags[i] == kCHID) { + config->channel_id_enabled = true; + break; + } + } + } + + for (size_t i = 0; i < kexs_len; i++) { + const QuicTag tag = kexs_tags[i]; + string private_key; + + config->kexs.push_back(tag); + + for (size_t j = 0; j < protobuf->key_size(); j++) { + const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i); + if (key.tag() == tag) { + private_key = key.private_key(); + break; + } + } + + if (private_key.empty()) { + LOG(WARNING) << "Server config contains key exchange method without " + "corresponding private key: " << tag; + return NULL; + } + + scoped_ptr ka; + switch (tag) { + case kC255: + ka.reset(Curve25519KeyExchange::New(private_key)); + if (!ka.get()) { + LOG(WARNING) << "Server config contained an invalid curve25519" + " private key."; + 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: " << tag; + return NULL; + } + + for (vector::const_iterator i = config->key_exchanges.begin(); + i != config->key_exchanges.end(); ++i) { + if ((*i)->tag() == tag) { + LOG(WARNING) << "Duplicate key exchange in config: " << tag; + return NULL; + } + } + + config->key_exchanges.push_back(ka.release()); + } + + if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) { + LOG(WARNING) << "Server config message is missing version"; + return NULL; + } + + if (config->version != QuicCryptoConfig::CONFIG_VERSION) { + LOG(WARNING) << "Server config specifies an unsupported version"; + return NULL; + } + + return config; +} + +void QuicCryptoServerConfig::SetProofSource(ProofSource* proof_source) { + proof_source_.reset(proof_source); +} + +void QuicCryptoServerConfig::SetEphemeralKeySource( + EphemeralKeySource* ephemeral_key_source) { + ephemeral_key_source_.reset(ephemeral_key_source); +} + +void QuicCryptoServerConfig::set_replay_protection(bool on) { + replay_protection_ = on; +} + +void QuicCryptoServerConfig::set_strike_register_no_startup_period() { + base::AutoLock auto_lock(strike_register_lock_); + DCHECK(!strike_register_.get()); + strike_register_no_startup_period_ = true; +} + +void QuicCryptoServerConfig::set_strike_register_max_entries( + uint32 max_entries) { + base::AutoLock locker(strike_register_lock_); + DCHECK(!strike_register_.get()); + strike_register_max_entries_ = max_entries; +} + +void QuicCryptoServerConfig::set_strike_register_window_secs( + uint32 window_secs) { + base::AutoLock locker(strike_register_lock_); + DCHECK(!strike_register_.get()); + strike_register_window_secs_ = window_secs; +} + +void QuicCryptoServerConfig::set_source_address_token_future_secs( + uint32 future_secs) { + source_address_token_future_secs_ = future_secs; +} + +void QuicCryptoServerConfig::set_source_address_token_lifetime_secs( + uint32 lifetime_secs) { + source_address_token_lifetime_secs_ = lifetime_secs; +} + +void QuicCryptoServerConfig::set_server_nonce_strike_register_max_entries( + uint32 max_entries) { + DCHECK(!server_nonce_strike_register_.get()); + server_nonce_strike_register_max_entries_ = max_entries; +} + +void QuicCryptoServerConfig::set_server_nonce_strike_register_window_secs( + uint32 window_secs) { + DCHECK(!server_nonce_strike_register_.get()); + server_nonce_strike_register_window_secs_ = window_secs; +} + +string QuicCryptoServerConfig::NewSourceAddressToken( + const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now) const { + SourceAddressToken source_address_token; + source_address_token.set_ip(IPAddressToPackedString(ip.address())); + source_address_token.set_timestamp(now.ToUNIXSeconds()); + + return source_address_token_boxer_.Box( + rand, source_address_token.SerializeAsString()); +} + +bool QuicCryptoServerConfig::ValidateSourceAddressToken( + StringPiece token, + const IPEndPoint& ip, + QuicWallTime now) const { + string storage; + StringPiece plaintext; + if (!source_address_token_boxer_.Unbox(token, &storage, &plaintext)) { + return false; + } + + SourceAddressToken source_address_token; + if (!source_address_token.ParseFromArray(plaintext.data(), + plaintext.size())) { + return false; + } + + if (source_address_token.ip() != IPAddressToPackedString(ip.address())) { + // It's for a different IP address. + return false; + } + + const QuicWallTime timestamp( + QuicWallTime::FromUNIXSeconds(source_address_token.timestamp())); + const QuicTime::Delta delta(now.AbsoluteDifference(timestamp)); + + if (now.IsBefore(timestamp) && + delta.ToSeconds() > source_address_token_future_secs_) { + return false; + } + + if (now.IsAfter(timestamp) && + delta.ToSeconds() > source_address_token_lifetime_secs_) { + return false; + } + + return true; +} + +// kServerNoncePlaintextSize is the number of bytes in an unencrypted server +// nonce. +static const size_t kServerNoncePlaintextSize = + 4 /* timestamp */ + 20 /* random bytes */; + +string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand, + QuicWallTime now) const { + const uint32 timestamp = static_cast(now.ToUNIXSeconds()); + + uint8 server_nonce[kServerNoncePlaintextSize]; + COMPILE_ASSERT(sizeof(server_nonce) > sizeof(timestamp), nonce_too_small); + server_nonce[0] = static_cast(timestamp >> 24); + server_nonce[1] = static_cast(timestamp >> 16); + server_nonce[2] = static_cast(timestamp >> 8); + server_nonce[3] = static_cast(timestamp); + rand->RandBytes(&server_nonce[sizeof(timestamp)], + sizeof(server_nonce) - sizeof(timestamp)); + + return server_nonce_boxer_.Box( + rand, + StringPiece(reinterpret_cast(server_nonce), sizeof(server_nonce))); +} + +bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token, + QuicWallTime now) const { + string storage; + StringPiece plaintext; + if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) { + return false; + } + + // plaintext contains: + // uint32 timestamp + // uint8[20] random bytes + + if (plaintext.size() != kServerNoncePlaintextSize) { + // This should never happen because the value decrypted correctly. + LOG(DFATAL) << "Seemingly valid server nonce had incorrect length."; + return false; + } + + uint8 server_nonce[32]; + memcpy(server_nonce, plaintext.data(), 4); + memcpy(server_nonce + 4, server_nonce_orbit_, sizeof(server_nonce_orbit_)); + memcpy(server_nonce + 4 + sizeof(server_nonce_orbit_), plaintext.data() + 4, + 20); + COMPILE_ASSERT(4 + sizeof(server_nonce_orbit_) + 20 == sizeof(server_nonce), + bad_nonce_buffer_length); + + bool is_unique; + { + base::AutoLock auto_lock(server_nonce_strike_register_lock_); + if (server_nonce_strike_register_.get() == NULL) { + server_nonce_strike_register_.reset(new StrikeRegister( + server_nonce_strike_register_max_entries_, + static_cast(now.ToUNIXSeconds()), + server_nonce_strike_register_window_secs_, server_nonce_orbit_, + StrikeRegister::NO_STARTUP_PERIOD_NEEDED)); + } + is_unique = server_nonce_strike_register_->Insert( + server_nonce, static_cast(now.ToUNIXSeconds())); + } + + return is_unique; +} + +QuicCryptoServerConfig::Config::Config() + : channel_id_enabled(false), + is_primary(false), + primary_time(QuicWallTime::Zero()) {} + +QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); } + +} // namespace net diff --git a/net/quic/crypto/quic_crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h new file mode 100644 index 0000000..80d9311 --- /dev/null +++ b/net/quic/crypto/quic_crypto_server_config.h @@ -0,0 +1,370 @@ +// Copyright 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. + +#ifndef NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ +#define NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ + +#include +#include +#include + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/crypto_secret_boxer.h" +#include "net/quic/quic_time.h" + +namespace net { + +class EphemeralKeySource; +class KeyExchange; +class ProofSource; +class QuicClock; +class QuicDecrypter; +class QuicEncrypter; +class QuicRandom; +class QuicServerConfigProtobuf; +class StrikeRegister; + +struct ClientHelloInfo; + +namespace test { +class QuicCryptoServerConfigPeer; +} // namespace test + +// QuicCryptoServerConfig contains the crypto configuration of a QUIC server. +// Unlike a client, a QUIC server can have multiple configurations active in +// order to support clients resuming with a previous configuration. +// TODO(agl): when adding configurations at runtime is added, this object will +// need to consider locking. +class NET_EXPORT_PRIVATE QuicCryptoServerConfig { + public: + // ConfigOptions contains options for generating server configs. + struct NET_EXPORT_PRIVATE ConfigOptions { + ConfigOptions(); + + // expiry_time is the time, in UNIX seconds, when the server config will + // expire. If unset, it defaults to the current time plus six months. + QuicWallTime expiry_time; + // channel_id_enabled controls whether the server config will indicate + // support for ChannelIDs. + bool channel_id_enabled; + // id contains the server config id for the resulting config. If empty, a + // random id is generated. + std::string id; + // orbit contains the kOrbitSize bytes of the orbit value for the server + // config. If |orbit| is empty then a random orbit is generated. + std::string orbit; + // p256 determines whether a P-256 public key will be included in the + // server config. Note that this breaks deterministic server-config + // generation since P-256 key generation doesn't use the QuicRandom given + // to DefaultConfig(). + bool p256; + }; + + // |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. In tests, use TESTING. + // |server_nonce_entropy|: an entropy source used to generate the orbit and + // key for server nonces, which are always local to a given instance of a + // server. + QuicCryptoServerConfig(base::StringPiece source_address_token_secret, + QuicRandom* server_nonce_entropy); + ~QuicCryptoServerConfig(); + + // TESTING is a magic parameter for passing to the constructor in tests. + static const char TESTING[]; + + // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for + // using in tests. + static QuicServerConfigProtobuf* DefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options); + + // AddConfig adds a QuicServerConfigProtobuf to the availible configurations. + // It returns the SCFG message from the config if successful. The caller + // takes ownership of the CryptoHandshakeMessage. |now| is used in + // conjunction with |protobuf->primary_time()| to determine whether the + // config should be made primary. + CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf, + QuicWallTime now); + + // AddDefaultConfig calls DefaultConfig to create a config and then calls + // AddConfig to add it. See the comment for |DefaultConfig| for details of + // the arguments. + CryptoHandshakeMessage* AddDefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options); + + // SetConfigs takes a vector of config protobufs and the current time. + // Configs are assumed to be uniquely identified by their server config ID. + // Previously unknown configs are added and possibly made the primary config + // depending on their |primary_time| and the value of |now|. Configs that are + // known, but are missing from the protobufs are deleted, unless they are + // currently the primary config. SetConfigs returns false if any errors were + // encountered and no changes to the QuicCryptoServerConfig will occur. + bool SetConfigs(const std::vector& protobufs, + QuicWallTime now); + + // 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. 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. + // clock: used to validate client nonces and ephemeral keys. + // rand: an entropy source + // params: the state of the handshake. This may be updated with a server + // nonce when we send a rejection. After a successful handshake, this will + // contain the state of the connection. + // out: the resulting handshake message (either REJ or SHLO) + // error_details: used to store a string describing any error. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + QuicGuid guid, + const IPEndPoint& client_ip, + const QuicClock* clock, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* params, + CryptoHandshakeMessage* out, + std::string* error_details) const; + + // SetProofSource installs |proof_source| as the ProofSource for handshakes. + // This object takes ownership of |proof_source|. + void SetProofSource(ProofSource* proof_source); + + // SetEphemeralKeySource installs an object that can cache ephemeral keys for + // a short period of time. This object takes ownership of + // |ephemeral_key_source|. If not set then ephemeral keys will be generated + // per-connection. + void SetEphemeralKeySource(EphemeralKeySource* ephemeral_key_source); + + // set_replay_protection controls whether replay protection is enabled. If + // replay protection is disabled then no strike registers are needed and + // frontends can share an orbit value without a shared strike-register. + // However, an attacker can duplicate a handshake and cause a client's + // request to be processed twice. + void set_replay_protection(bool on); + + // set_strike_register_no_startup_period configures the strike register to + // not have a startup period. + void set_strike_register_no_startup_period(); + + // set_strike_register_max_entries sets the maximum number of entries that + // the internal strike register will hold. If the strike register fills up + // then the oldest entries (by the client's clock) will be dropped. + void set_strike_register_max_entries(uint32 max_entries); + + // set_strike_register_window_secs sets the number of seconds around the + // current time that the strike register will attempt to be authoritative + // for. Setting a larger value allows for greater client clock-skew, but + // means that the quiescent startup period must be longer. + void set_strike_register_window_secs(uint32 window_secs); + + // set_source_address_token_future_secs sets the number of seconds into the + // future that source-address tokens will be accepted from. Since + // source-address tokens are authenticated, this should only happen if + // another, valid server has clock-skew. + void set_source_address_token_future_secs(uint32 future_secs); + + // set_source_address_token_lifetime_secs sets the number of seconds that a + // source-address token will be valid for. + void set_source_address_token_lifetime_secs(uint32 lifetime_secs); + + // set_server_nonce_strike_register_max_entries sets the number of entries in + // the server-nonce strike-register. This is used to record that server nonce + // values have been used. If the number of entries is too small then clients + // which are depending on server nonces may fail to handshake because their + // nonce has expired in the amount of time it took to go from the server to + // the client and back. + void set_server_nonce_strike_register_max_entries(uint32 max_entries); + + // set_server_nonce_strike_register_window_secs sets the number of seconds + // around the current time that the server-nonce strike-register will accept + // nonces from. Setting a larger value allows for clients to delay follow-up + // client hellos for longer and still use server nonces as proofs of + // uniqueness. + void set_server_nonce_strike_register_window_secs(uint32 window_secs); + + private: + friend class test::QuicCryptoServerConfigPeer; + + // Config represents a server config: a collection of preferences and + // Diffie-Hellman public values. + class NET_EXPORT_PRIVATE Config : public QuicCryptoConfig, + public base::RefCounted { + public: + Config(); + + // TODO(rtenneti): since this is a class, we should probably do + // getters/setters here. + // |serialized| contains the bytes of this server config, suitable for + // sending on the wire. + std::string serialized; + // id contains the SCID of this server config. + std::string id; + // orbit contains the orbit value for this config: an opaque identifier + // used to identify clusters of server frontends. + unsigned char orbit[kOrbitSize]; + + // key_exchanges contains key exchange objects with the private keys + // already loaded. The values correspond, one-to-one, with the tags in + // |kexs| from the parent class. + std::vector key_exchanges; + + // tag_value_map contains the raw key/value pairs for the config. + QuicTagValueMap tag_value_map; + + // channel_id_enabled is true if the config in |serialized| specifies that + // ChannelIDs are supported. + bool channel_id_enabled; + + // is_primary is true if this config is the one that we'll give out to + // clients as the current one. + bool is_primary; + + // primary_time contains the timestamp when this config should become the + // primary config. A value of QuicWallTime::Zero() means that this config + // will not be promoted at a specific time. + QuicWallTime primary_time; + + private: + friend class base::RefCounted; + virtual ~Config(); + + DISALLOW_COPY_AND_ASSIGN(Config); + }; + + typedef std::map > ConfigMap; + + // ConfigPrimaryTimeLessThan returns true if a->primary_time < + // b->primary_time. + static bool ConfigPrimaryTimeLessThan(const scoped_refptr& a, + const scoped_refptr& b); + + // SelectNewPrimaryConfig reevaluates the primary config based on the + // "primary_time" deadlines contained in each. + void SelectNewPrimaryConfig(QuicWallTime now) const; + + // EvaluateClientHello checks |client_hello| for gross errors and determines + // whether it can be shown to be fresh (i.e. not a replay). The results are + // written to |info|. + QuicErrorCode EvaluateClientHello( + const CryptoHandshakeMessage& client_hello, + const uint8* orbit, + ClientHelloInfo* info, + std::string* error_details) const; + + // BuildRejection sets |out| to be a REJ message in reply to |client_hello|. + void BuildRejection( + const scoped_refptr& config, + const CryptoHandshakeMessage& client_hello, + const ClientHelloInfo& info, + QuicRandom* rand, + CryptoHandshakeMessage* out) const; + + // ParseConfigProtobuf parses the given config protobuf and returns a + // scoped_refptr if successful. The caller adopts the reference to the + // Config. On error, ParseConfigProtobuf returns NULL. + scoped_refptr ParseConfigProtobuf(QuicServerConfigProtobuf* protobuf); + + // NewSourceAddressToken returns a fresh source address token for the given + // IP address. + std::string NewSourceAddressToken(const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now) const; + + // 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, + QuicWallTime now) const; + + // NewServerNonce generates and encrypts a random nonce. + std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const; + + // ValidateServerNonce decrypts |token| and verifies that it hasn't been + // previously used and is recent enough that it is plausible that it was part + // of a very recently provided rejection ("recent" will be on the order of + // 10-30 seconds). If so, it records that it has been used and returns true. + // Otherwise it returns false. + bool ValidateServerNonce(base::StringPiece echoed_server_nonce, + QuicWallTime now) const; + + // replay_protection_ controls whether the server enforces that handshakes + // aren't replays. + bool replay_protection_; + + // configs_ satisfies the following invariants: + // 1) configs_.empty() <-> primary_config_ == NULL + // 2) primary_config_ != NULL -> primary_config_->is_primary + // 3) ∀ c∈configs_, c->is_primary <-> c == primary_config_ + mutable base::Lock configs_lock_; + // configs_ contains all active server configs. It's expected that there are + // about half-a-dozen configs active at any one time. + ConfigMap configs_; + // primary_config_ points to a Config (which is also in |configs_|) which is + // the primary config - i.e. the one that we'll give out to new clients. + mutable scoped_refptr primary_config_; + // next_config_promotion_time_ contains the nearest, future time when an + // active config will be promoted to primary. + mutable QuicWallTime next_config_promotion_time_; + + mutable base::Lock strike_register_lock_; + // strike_register_ contains a data structure that keeps track of previously + // observed client nonces in order to prevent replay attacks. + mutable scoped_ptr strike_register_; + + // source_address_token_boxer_ is used to protect the source-address tokens + // that are given to clients. + CryptoSecretBoxer source_address_token_boxer_; + + // server_nonce_boxer_ is used to encrypt and validate suggested server + // nonces. + CryptoSecretBoxer server_nonce_boxer_; + + // server_nonce_orbit_ contains the random, per-server orbit values that this + // server will use to generate server nonces (the moral equivalent of a SYN + // cookies). + uint8 server_nonce_orbit_[8]; + + mutable base::Lock server_nonce_strike_register_lock_; + // server_nonce_strike_register_ contains a data structure that keeps track of + // previously observed server nonces from this server, in order to prevent + // replay attacks. + mutable scoped_ptr server_nonce_strike_register_; + + // proof_source_ contains an object that can provide certificate chains and + // signatures. + scoped_ptr proof_source_; + + // ephemeral_key_source_ contains an object that caches ephemeral keys for a + // short period of time. + scoped_ptr ephemeral_key_source_; + + // These fields store configuration values. See the comments for their + // respective setter functions. + bool strike_register_no_startup_period_; + uint32 strike_register_max_entries_; + uint32 strike_register_window_secs_; + uint32 source_address_token_future_secs_; + uint32 source_address_token_lifetime_secs_; + uint32 server_nonce_strike_register_max_entries_; + uint32 server_nonce_strike_register_window_secs_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ diff --git a/net/quic/crypto/quic_crypto_server_config_test.cc b/net/quic/crypto/quic_crypto_server_config_test.cc new file mode 100644 index 0000000..fc8882c --- /dev/null +++ b/net/quic/crypto/quic_crypto_server_config_test.cc @@ -0,0 +1,355 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/quic_crypto_server_config.h" + +#include + +#include "base/stl_util.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_server_config_protobuf.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::make_pair; +using std::map; +using std::pair; +using std::string; +using std::vector; + +namespace net { +namespace test { + +class QuicCryptoServerConfigPeer { + public: + explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config) + : server_config_(server_config) {} + + string NewSourceAddressToken(IPEndPoint ip, + QuicRandom* rand, + QuicWallTime now) { + return server_config_->NewSourceAddressToken(ip, rand, now); + } + + bool ValidateSourceAddressToken(StringPiece srct, + IPEndPoint ip, + QuicWallTime now) { + return server_config_->ValidateSourceAddressToken(srct, ip, now); + } + + // CheckConfigs compares the state of the Configs in |server_config_| to the + // description given as arguments. The arguments are given as NULL-terminated + // pairs. The first of each pair is the server config ID of a Config. The + // second is a boolean describing whether the config is the primary. For + // example: + // CheckConfigs(NULL); // checks that no Configs are loaded. + // + // // Checks that exactly three Configs are loaded with the given IDs and + // // status. + // CheckConfigs( + // "id1", false, + // "id2", true, + // "id3", false, + // NULL); + void CheckConfigs(const char* server_config_id1, ...) { + va_list ap; + va_start(ap, server_config_id1); + + vector > expected; + bool first = true; + for (;;) { + const char* server_config_id; + if (first) { + server_config_id = server_config_id1; + first = false; + } else { + server_config_id = va_arg(ap, const char*); + } + + if (!server_config_id) { + break; + } + + // varargs will promote the value to an int so we have to read that from + // the stack and cast down. + const bool is_primary = static_cast(va_arg(ap, int)); + expected.push_back(make_pair(server_config_id, is_primary)); + } + + va_end(ap); + + base::AutoLock locked(server_config_->configs_lock_); + + ASSERT_EQ(expected.size(), server_config_->configs_.size()) + << ConfigsDebug(); + + for (QuicCryptoServerConfig::ConfigMap::const_iterator + i = server_config_->configs_.begin(); + i != server_config_->configs_.end(); ++i) { + bool found = false; + for (vector >::iterator j = expected.begin(); + j != expected.end(); ++j) { + if (i->first == j->first && i->second->is_primary == j->second) { + found = true; + j->first.clear(); + break; + } + } + + ASSERT_TRUE(found) << "Failed to find match for " << i->first + << " in configs:\n" << ConfigsDebug(); + } + } + + // ConfigsDebug returns a string that contains debugging information about + // the set of Configs loaded in |server_config_| and their status. + // ConfigsDebug() should be called after acquiring + // server_config_->configs_lock_. + string ConfigsDebug() { + if (server_config_->configs_.empty()) { + return "No Configs in QuicCryptoServerConfig"; + } + + string s; + + for (QuicCryptoServerConfig::ConfigMap::const_iterator + i = server_config_->configs_.begin(); + i != server_config_->configs_.end(); ++i) { + const scoped_refptr config = i->second; + if (config->is_primary) { + s += "(primary) "; + } else { + s += " "; + } + s += config->id; + s += "\n"; + } + + return s; + } + + void SelectNewPrimaryConfig(int seconds) { + base::AutoLock locked(server_config_->configs_lock_); + server_config_->SelectNewPrimaryConfig( + QuicWallTime::FromUNIXSeconds(seconds)); + } + + private: + const QuicCryptoServerConfig* server_config_; +}; + +TEST(QuicCryptoServerConfigTest, ServerConfig) { + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); + MockClock clock; + + scoped_ptr( + server.AddDefaultConfig(rand, &clock, + QuicCryptoServerConfig::ConfigOptions())); +} + +TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); + 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); + MockClock clock; + clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); + QuicCryptoServerConfigPeer peer(&server); + + QuicWallTime now = clock.WallNow(); + const QuicWallTime original_time = now; + + const string token4 = peer.NewSourceAddressToken(ip4, rand, now); + const string token6 = peer.NewSourceAddressToken(ip6, rand, now); + 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)); +} + +class CryptoServerConfigsTest : public ::testing::Test { + public: + CryptoServerConfigsTest() + : rand_(QuicRandom::GetInstance()), + config_(QuicCryptoServerConfig::TESTING, rand_), + test_peer_(&config_) {} + + virtual void SetUp() { + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000)); + } + + // SetConfigs constructs suitable config protobufs and calls SetConfigs on + // |config_|. The arguments are given as NULL-terminated pairs. The first of + // each pair is the server config ID of a Config. The second is the + // |primary_time| of that Config, given in epoch seconds. (Although note + // that, in these tests, time is set to 1000 seconds since the epoch.) For + // example: + // SetConfigs(NULL); // calls |config_.SetConfigs| with no protobufs. + // + // // Calls |config_.SetConfigs| with two protobufs: one for a Config with + // // a |primary_time| of 900, and another with a |primary_time| of 1000. + // CheckConfigs( + // "id1", 900, + // "id2", 1000, + // NULL); + // + // If the server config id starts with "INVALID" then the generated protobuf + // will be invalid. + void SetConfigs(const char* server_config_id1, ...) { + va_list ap; + va_start(ap, server_config_id1); + bool has_invalid = false; + + vector protobufs; + bool first = true; + for (;;) { + const char* server_config_id; + if (first) { + server_config_id = server_config_id1; + first = false; + } else { + server_config_id = va_arg(ap, const char*); + } + + if (!server_config_id) { + break; + } + + int primary_time = va_arg(ap, int); + + QuicCryptoServerConfig::ConfigOptions options; + options.id = server_config_id; + QuicServerConfigProtobuf* protobuf( + QuicCryptoServerConfig::DefaultConfig(rand_, &clock_, options)); + protobuf->set_primary_time(primary_time); + if (string(server_config_id).find("INVALID") == 0) { + protobuf->clear_key(); + has_invalid = true; + } + protobufs.push_back(protobuf); + } + + ASSERT_EQ(!has_invalid, config_.SetConfigs(protobufs, clock_.WallNow())); + STLDeleteElements(&protobufs); + } + + protected: + QuicRandom* const rand_; + MockClock clock_; + QuicCryptoServerConfig config_; + QuicCryptoServerConfigPeer test_peer_; +}; + +TEST_F(CryptoServerConfigsTest, NoConfigs) { + test_peer_.CheckConfigs(NULL); +} + +TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) { + // Make sure that "b" is primary even though "a" comes first. + SetConfigs("a", 1100, + "b", 900, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + NULL); +} + +TEST_F(CryptoServerConfigsTest, MakePrimarySecond) { + // Make sure that a remains primary after b is added. + SetConfigs("a", 900, + "b", 1100, + NULL); + test_peer_.CheckConfigs( + "a", true, + "b", false, + NULL); +} + +TEST_F(CryptoServerConfigsTest, Delete) { + // Ensure that configs get deleted when removed. + SetConfigs("a", 800, + "b", 900, + "c", 1100, + NULL); + SetConfigs("b", 900, + "c", 1100, + NULL); + test_peer_.CheckConfigs( + "b", true, + "c", false, + NULL); +} + +TEST_F(CryptoServerConfigsTest, DontDeletePrimary) { + // Ensure that the primary config isn't deleted when removed. + SetConfigs("a", 800, + "b", 900, + "c", 1100, + NULL); + SetConfigs("a", 800, + "c", 1100, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); +} + +TEST_F(CryptoServerConfigsTest, AdvancePrimary) { + // Check that a new primary config is enabled at the right time. + SetConfigs("a", 900, + "b", 1100, + NULL); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs( + "a", true, + "b", false, + NULL); + test_peer_.SelectNewPrimaryConfig(1101); + test_peer_.CheckConfigs( + "a", false, + "b", true, + NULL); +} + +TEST_F(CryptoServerConfigsTest, InvalidConfigs) { + // Ensure that invalid configs don't change anything. + SetConfigs("a", 800, + "b", 900, + "c", 1100, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); + SetConfigs("a", 800, + "c", 1100, + "INVALID1", 1000, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); +} + +} // namespace test +} // namespace net diff --git a/net/quic/iovector_test.cc b/net/quic/iovector_test.cc new file mode 100644 index 0000000..b25f29c --- /dev/null +++ b/net/quic/iovector_test.cc @@ -0,0 +1,283 @@ +// Copyright 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/iovector.h" + +#include + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { +namespace { + +const char* const test_data[] = { + "test string 1, a medium size one.", + "test string2", + "test string 3, a looooooooooooong loooooooooooooooong string" +}; + +TEST(IOVectorTest, CopyConstructor) { + IOVector iov1; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + iov1.Append(const_cast(test_data[i]), strlen(test_data[i])); + } + IOVector iov2 = iov1; + EXPECT_EQ(iov2.Size(), iov1.Size()); + for (size_t i = 0; i < iov2.Size(); ++i) { + EXPECT_TRUE(iov2.iovec()[i].iov_base == iov1.iovec()[i].iov_base); + EXPECT_EQ(iov2.iovec()[i].iov_len, iov1.iovec()[i].iov_len); + } + EXPECT_EQ(iov2.TotalBufferSize(), iov1.TotalBufferSize()); +} + +TEST(IOVectorTest, AssignmentOperator) { + IOVector iov1; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + iov1.Append(const_cast(test_data[i]), strlen(test_data[i])); + } + IOVector iov2; + iov2.Append(const_cast("ephemeral string"), 16); + // The following assignment results in a shallow copy; + // both IOVectors point to the same underlying data. + iov2 = iov1; + EXPECT_EQ(iov2.Size(), iov1.Size()); + for (size_t i = 0; i < iov2.Size(); ++i) { + EXPECT_TRUE(iov2.iovec()[i].iov_base == iov1.iovec()[i].iov_base); + EXPECT_EQ(iov2.iovec()[i].iov_len, iov1.iovec()[i].iov_len); + } + EXPECT_EQ(iov2.TotalBufferSize(), iov1.TotalBufferSize()); +} + +TEST(IOVectorTest, Append) { + IOVector iov; + int length = 0; + const struct iovec* iov2 = iov.iovec(); + + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + const int append_len = str_len / 2; + // This should append a new block + iov.Append(const_cast(test_data[i]), append_len); + length += append_len; + ASSERT_EQ(i + 1, static_cast(iov.Size())); + ASSERT_TRUE(iov.LastBlockEnd() == test_data[i] + append_len); + // This should just lengthen the existing block. + iov.Append(const_cast(test_data[i] + append_len), + str_len - append_len); + length += (str_len - append_len); + ASSERT_EQ(i + 1, static_cast(iov.Size())); + ASSERT_TRUE(iov.LastBlockEnd() == test_data[i] + str_len); + } + + iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + for (size_t i = 0; i < iov.Size(); ++i) { + ASSERT_TRUE(test_data[i] == iov2[i].iov_base); + ASSERT_EQ(strlen(test_data[i]), iov2[i].iov_len); + } +} + +TEST(IOVectorTest, AppendIovec) { + IOVector iov; + const struct iovec test_iov[] = { + {const_cast("foo"), 3}, + {const_cast("bar"), 3}, + {const_cast("buzzzz"), 6} + }; + iov.AppendIovec(test_iov, ARRAYSIZE_UNSAFE(test_iov)); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_iov); ++i) { + EXPECT_EQ(test_iov[i].iov_base, iov.iovec()[i].iov_base); + EXPECT_EQ(test_iov[i].iov_len, iov.iovec()[i].iov_len); + } + + // Test AppendIovecAtMostBytes. + iov.Clear(); + // Stop in the middle of a block. + EXPECT_EQ(5u, iov.AppendIovecAtMostBytes(test_iov, ARRAYSIZE_UNSAFE(test_iov), + 5)); + EXPECT_EQ(5u, iov.TotalBufferSize()); + iov.Append(static_cast(test_iov[1].iov_base) + 2, 1); + // Make sure the boundary case, where max_bytes == size of block also works. + EXPECT_EQ(6u, iov.AppendIovecAtMostBytes(&test_iov[2], 1, 6)); + ASSERT_LE(ARRAYSIZE_UNSAFE(test_iov), static_cast(iov.Size())); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_iov); ++i) { + EXPECT_EQ(test_iov[i].iov_base, iov.iovec()[i].iov_base); + EXPECT_EQ(test_iov[i].iov_len, iov.iovec()[i].iov_len); + } +} + +TEST(IOVectorTest, ConsumeHalfBlocks) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast(test_data[i]), str_len); + length += str_len; + } + const char* endp = iov.LastBlockEnd(); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const struct iovec* iov2 = iov.iovec(); + const size_t str_len = strlen(test_data[i]); + size_t tmp = str_len / 2; + + ASSERT_TRUE(iov2 != NULL); + ASSERT_TRUE(iov2[0].iov_base == test_data[i]); + ASSERT_EQ(str_len, iov2[0].iov_len); + + // Consume half of the first block. + size_t consumed = iov.Consume(tmp); + ASSERT_EQ(tmp, consumed); + ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data) - i, static_cast(iov.Size())); + iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + ASSERT_TRUE(iov2[0].iov_base == test_data[i] + tmp); + ASSERT_EQ(iov2[0].iov_len, str_len - tmp); + + // Consume the rest of the first block + consumed = iov.Consume(str_len - tmp); + ASSERT_EQ(str_len - tmp, consumed); + ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data) - i - 1, + static_cast(iov.Size())); + iov2 = iov.iovec(); + if (iov.Size() > 0) { + ASSERT_TRUE(iov2 != NULL); + ASSERT_TRUE(iov.LastBlockEnd() == endp); + } else { + ASSERT_TRUE(iov2 == NULL); + ASSERT_TRUE(iov.LastBlockEnd() == NULL); + } + } +} + +TEST(IOVectorTest, ConsumeTwoAndHalfBlocks) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast(test_data[i]), str_len); + length += str_len; + } + const size_t last_len = strlen(test_data[ARRAYSIZE_UNSAFE(test_data) - 1]); + const size_t half_len = last_len / 2; + + const char* endp = iov.LastBlockEnd(); + size_t consumed = iov.Consume(length - half_len); + ASSERT_EQ(length - half_len, consumed); + const struct iovec* iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + ASSERT_EQ(1u, iov.Size()); + ASSERT_TRUE(iov2[0].iov_base == + test_data[ARRAYSIZE_UNSAFE(test_data) - 1] + last_len - half_len); + ASSERT_EQ(half_len, iov2[0].iov_len); + ASSERT_TRUE(iov.LastBlockEnd() == endp); + + consumed = iov.Consume(half_len); + ASSERT_EQ(half_len, consumed); + iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); + ASSERT_TRUE(iov.LastBlockEnd() == NULL); +} + +TEST(IOVectorTest, ConsumeTooMuch) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast(test_data[i]), str_len); + length += str_len; + } + + int consumed = 0; + consumed = iov.Consume(length); + // TODO(rtenneti): enable when chromium supports EXPECT_DFATAL. + /* + EXPECT_DFATAL( + {consumed = iov.Consume(length + 1);}, + "Attempting to consume 1 non-existent bytes."); + */ + ASSERT_EQ(length, consumed); + const struct iovec* iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); + ASSERT_TRUE(iov.LastBlockEnd() == NULL); +} + +TEST(IOVectorTest, Clear) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast(test_data[i]), str_len); + length += str_len; + } + const struct iovec* iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data), static_cast(iov.Size())); + + iov.Clear(); + iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); +} + +TEST(IOVectorTest, Capacity) { + IOVector iov; + // Note: IOVector merges adjacent Appends() into a single iov. + // Therefore, if we expect final size of iov to be 3, we must insure + // that the items we are appending are not adjacent. To achieve that + // we use use an array (a[1] provides a buffer between a[0] and b[0], + // and makes them non-adjacent). + char a[2], b[2], c[2]; + iov.Append(&a[0], 1); + iov.Append(&b[0], 1); + iov.Append(&c[0], 1); + ASSERT_EQ(3u, iov.Size()); + size_t capacity = iov.Capacity(); + EXPECT_LE(iov.Size(), capacity); + iov.Consume(2); + // The capacity should not have changed. + EXPECT_EQ(capacity, iov.Capacity()); +} + +TEST(IOVectorTest, Swap) { + IOVector iov1, iov2; + // See IOVector merge comment above. + char a[2], b[2], c[2], d[2], e[2]; + iov1.Append(&a[0], 1); + iov1.Append(&b[0], 1); + + iov2.Append(&c[0], 1); + iov2.Append(&d[0], 1); + iov2.Append(&e[0], 1); + iov1.Swap(&iov2); + + ASSERT_EQ(3u, iov1.Size()); + EXPECT_EQ(&c[0], iov1.iovec()[0].iov_base); + EXPECT_EQ(1u, iov1.iovec()[0].iov_len); + EXPECT_EQ(&d[0], iov1.iovec()[1].iov_base); + EXPECT_EQ(1u, iov1.iovec()[1].iov_len); + EXPECT_EQ(&e[0], iov1.iovec()[2].iov_base); + EXPECT_EQ(1u, iov1.iovec()[2].iov_len); + + ASSERT_EQ(2u, iov2.Size()); + EXPECT_EQ(&a[0], iov2.iovec()[0].iov_base); + EXPECT_EQ(1u, iov2.iovec()[0].iov_len); + EXPECT_EQ(&b[0], iov2.iovec()[1].iov_base); + EXPECT_EQ(1u, iov2.iovec()[1].iov_len); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc index 545d4c1..3fa8211 100644 --- a/net/quic/quic_config.cc +++ b/net/quic/quic_config.cc @@ -19,7 +19,9 @@ QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence) } QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence) - : QuicNegotiableValue(tag, presence) { + : QuicNegotiableValue(tag, presence), + max_value_(0), + default_value_(0) { } void QuicNegotiableUint32::set(uint32 max, uint32 default_value) { @@ -224,9 +226,15 @@ QuicConfig::QuicConfig() : kICSL, QuicNegotiableValue::PRESENCE_REQUIRED), keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL), max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED), - max_time_before_crypto_handshake_(QuicTime::Delta::Zero()) { - idle_connection_state_lifetime_seconds_.set(0, 0); - keepalive_timeout_seconds_.set(0, 0); + max_time_before_crypto_handshake_(QuicTime::Delta::Zero()), + server_initial_congestion_window_( + kSWND, QuicNegotiableValue::PRESENCE_OPTIONAL), + server_max_packet_size_(kSMSS, QuicNegotiableValue::PRESENCE_OPTIONAL), + initial_round_trip_time_us_(kIRTT, QuicNegotiableValue::PRESENCE_OPTIONAL) { + // All optional non-zero parameters should be initialized here. + server_initial_congestion_window_.set(kMaxInitialWindow, + kDefaultInitialWindow); + server_max_packet_size_.set(kMaxPacketSize, kDefaultMaxPacketSize); } QuicConfig::~QuicConfig() {} @@ -277,11 +285,45 @@ QuicTime::Delta QuicConfig::max_time_before_crypto_handshake() const { return max_time_before_crypto_handshake_; } +void QuicConfig::set_server_initial_congestion_window(size_t max_initial_window, + size_t default_initial_window) { + server_initial_congestion_window_.set(max_initial_window, + default_initial_window); +} + +uint32 QuicConfig::server_initial_congestion_window() const { + return server_initial_congestion_window_.GetUint32(); +} + +void QuicConfig::set_server_max_packet_size(size_t max_bytes, + size_t default_bytes) { + server_max_packet_size_.set(max_bytes, default_bytes); +} + +uint32 QuicConfig::server_max_packet_size() const { + return server_max_packet_size_.GetUint32(); +} + +void QuicConfig::set_initial_round_trip_time_us(size_t max_rtt, + size_t default_rtt) { + initial_round_trip_time_us_.set(max_rtt, default_rtt); +} + +uint32 QuicConfig::initial_round_trip_time_us() const { + return initial_round_trip_time_us_.GetUint32(); +} + bool QuicConfig::negotiated() { + // TODO(ianswett): Add the negotiated parameters once and iterate over all + // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and + // ProcessServerHello. return congestion_control_.negotiated() && idle_connection_state_lifetime_seconds_.negotiated() && keepalive_timeout_seconds_.negotiated() && - max_streams_per_connection_.negotiated(); + max_streams_per_connection_.negotiated() && + server_initial_congestion_window_.negotiated() && + server_max_packet_size_.negotiated() && + initial_round_trip_time_us_.negotiated(); } void QuicConfig::SetDefaults() { @@ -294,6 +336,9 @@ void QuicConfig::SetDefaults() { kDefaultMaxStreamsPerConnection); max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds( kDefaultMaxTimeForCryptoHandshakeSecs); + server_initial_congestion_window_.set(kMaxInitialWindow, + kDefaultInitialWindow); + server_max_packet_size_.set(kMaxPacketSize, kDefaultMaxPacketSize); } void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { @@ -301,6 +346,10 @@ void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out); keepalive_timeout_seconds_.ToHandshakeMessage(out); max_streams_per_connection_.ToHandshakeMessage(out); + server_initial_congestion_window_.ToHandshakeMessage(out); + server_max_packet_size_.ToHandshakeMessage(out); + // TODO(ianswett): Don't transmit parameters which are optional and not set. + initial_round_trip_time_us_.ToHandshakeMessage(out); } QuicErrorCode QuicConfig::ProcessClientHello( @@ -324,6 +373,18 @@ QuicErrorCode QuicConfig::ProcessClientHello( error = max_streams_per_connection_.ProcessClientHello( client_hello, error_details); } + if (error == QUIC_NO_ERROR) { + error = server_initial_congestion_window_.ProcessClientHello( + client_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = server_max_packet_size_.ProcessClientHello( + client_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = initial_round_trip_time_us_.ProcessClientHello( + client_hello, error_details); + } return error; } @@ -348,6 +409,18 @@ QuicErrorCode QuicConfig::ProcessServerHello( error = max_streams_per_connection_.ProcessServerHello( server_hello, error_details); } + if (error == QUIC_NO_ERROR) { + error = server_initial_congestion_window_.ProcessServerHello( + server_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = server_max_packet_size_.ProcessServerHello( + server_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = initial_round_trip_time_us_.ProcessServerHello( + server_hello, error_details); + } return error; } diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h index c07f332..9328c67 100644 --- a/net/quic/quic_config.h +++ b/net/quic/quic_config.h @@ -41,6 +41,7 @@ class NET_EXPORT_PRIVATE QuicNegotiableValue { class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { public: + // Default and max values default to 0. QuicNegotiableUint32(QuicTag name, Presence presence); // Sets the maximum possible value that can be achieved after negotiation and @@ -159,6 +160,23 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicTime::Delta max_time_before_crypto_handshake() const; + // Sets the server's TCP sender's max and default initial congestion window + // in packets. + void set_server_initial_congestion_window(size_t max_initial_window, + size_t default_initial_window); + + uint32 server_initial_congestion_window() const; + + // Sets the server's max packet size and default max packet size in bytes. + void set_server_max_packet_size(size_t max_bytes, size_t default_bytes); + + uint32 server_max_packet_size() const; + + // Sets an estimated initial round trip time in us. + void set_initial_round_trip_time_us(size_t max_rtt, size_t default_rtt); + + uint32 initial_round_trip_time_us() const; + bool negotiated(); // SetDefaults sets the members to sensible, default values. @@ -190,6 +208,12 @@ class NET_EXPORT_PRIVATE QuicConfig { // Maximum time till the session can be alive before crypto handshake is // finished. (Not negotiated). QuicTime::Delta max_time_before_crypto_handshake_; + // Initial congestion window in packets. + QuicNegotiableUint32 server_initial_congestion_window_; + // Maximum packet size for the server to send in bytes. + QuicNegotiableUint32 server_max_packet_size_; + // Initial round trip time estimate in microseconds. + QuicNegotiableUint32 initial_round_trip_time_us_; }; } // namespace net diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc index eeaa97d..c7ed364 100644 --- a/net/quic/quic_config_test.cc +++ b/net/quic/quic_config_test.cc @@ -20,6 +20,7 @@ class QuicConfigTest : public ::testing::Test { protected: QuicConfigTest() { config_.SetDefaults(); + config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0); } QuicConfig config_; @@ -59,6 +60,9 @@ TEST_F(QuicConfigTest, ProcessClientHello) { QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs)); client_config.set_max_streams_per_connection( 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); + client_config.set_initial_round_trip_time_us( + 10 * base::Time::kMicrosecondsPerMillisecond, + 10 * base::Time::kMicrosecondsPerMillisecond); CryptoHandshakeMessage msg; client_config.ToHandshakeMessage(&msg); @@ -72,6 +76,8 @@ TEST_F(QuicConfigTest, ProcessClientHello) { EXPECT_EQ(kDefaultMaxStreamsPerConnection, config_.max_streams_per_connection()); EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); + EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond, + config_.initial_round_trip_time_us()); } TEST_F(QuicConfigTest, ProcessServerHello) { @@ -85,6 +91,13 @@ TEST_F(QuicConfigTest, ProcessServerHello) { server_config.set_max_streams_per_connection( kDefaultMaxStreamsPerConnection / 2, kDefaultMaxStreamsPerConnection / 2); + server_config.set_server_max_packet_size(kDefaultMaxPacketSize / 2, + kDefaultMaxPacketSize / 2); + server_config.set_server_initial_congestion_window(kDefaultInitialWindow / 2, + kDefaultInitialWindow / 2); + server_config.set_initial_round_trip_time_us( + 10 * base::Time::kMicrosecondsPerMillisecond, + 10 * base::Time::kMicrosecondsPerMillisecond); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg); @@ -97,7 +110,12 @@ TEST_F(QuicConfigTest, ProcessServerHello) { config_.idle_connection_state_lifetime()); EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2, config_.max_streams_per_connection()); + EXPECT_EQ(kDefaultMaxPacketSize / 2, config_.server_max_packet_size()); + EXPECT_EQ(kDefaultInitialWindow / 2, + config_.server_initial_congestion_window()); EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); + EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond, + config_.initial_round_trip_time_us()); } TEST_F(QuicConfigTest, MissingValueInCHLO) { diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 4c9a6bc..c426868 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -19,6 +19,7 @@ #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/iovector.h" #include "net/quic/quic_bandwidth.h" +#include "net/quic/quic_config.h" #include "net/quic/quic_utils.h" using base::hash_map; @@ -54,7 +55,7 @@ const QuicPacketSequenceNumber kMaxPacketGap = 5000; // We want to make sure if we get a nack packet which triggers several // retransmissions, we don't queue up too many packets. 10 is TCP's default // initial congestion window(RFC 6928). -const size_t kMaxRetransmissionsPerAck = 10; +const size_t kMaxRetransmissionsPerAck = kDefaultInitialWindow; // TCP retransmits after 3 nacks. // TODO(ianswett): Change to match TCP's rule of retransmitting once an ack @@ -252,6 +253,19 @@ QuicConnection::~QuicConnection() { } } +void QuicConnection::SetFromConfig(const QuicConfig& config) { + DCHECK_LT(0u, config.server_max_packet_size()); + DCHECK_LT(0u, config.server_initial_congestion_window()); + SetIdleNetworkTimeout(config.idle_connection_state_lifetime()); + // Set the max packet length only when QUIC_VERSION_12 or later is supported, + // with explicitly truncated acks. + if (version() > QUIC_VERSION_11) { + options()->max_packet_length = config.server_max_packet_size(); + } + congestion_manager_.SetFromConfig(config, is_server_); + // TODO(satyamshekhar): Set congestion control and ICSL also. +} + bool QuicConnection::SelectMutualVersion( const QuicVersionVector& available_versions) { // Try to find the highest mutual version by iterating over supported @@ -506,8 +520,10 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) { largest_seen_packet_with_ack_ = last_header_.packet_sequence_number; - received_truncated_ack_ = incoming_ack.received_info.missing_packets.size() >= - QuicFramer::GetMaxUnackedPackets(last_header_); + received_truncated_ack_ = version() <= QUIC_VERSION_11 ? + incoming_ack.received_info.missing_packets.size() >= + QuicFramer::GetMaxUnackedPackets(last_header_) : + incoming_ack.received_info.is_truncated; received_packet_manager_.UpdatePacketInformationReceivedByPeer(incoming_ack); received_packet_manager_.UpdatePacketInformationSentByPeer(incoming_ack); @@ -590,8 +606,10 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { // We can't have too many unacked packets, or our ack frames go over // kMaxPacketSize. - DCHECK_LE(incoming_ack.received_info.missing_packets.size(), - QuicFramer::GetMaxUnackedPackets(last_header_)); + if (version() <= QUIC_VERSION_11) { + DCHECK_LE(incoming_ack.received_info.missing_packets.size(), + QuicFramer::GetMaxUnackedPackets(last_header_)); + } if (incoming_ack.sent_info.least_unacked < received_packet_manager_.peer_least_packet_awaiting_ack()) { @@ -672,9 +690,8 @@ bool QuicConnection::OnConnectionCloseFrame( DLOG(INFO) << ENDPOINT << "Connection " << guid() << " closed with error " << QuicUtils::ErrorToString(frame.error_code) << " " << frame.error_details; - CloseConnection(frame.error_code, true); - DCHECK(!connected_); - return false; + last_close_frames_.push_back(frame); + return connected_; } bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { @@ -743,6 +760,10 @@ void QuicConnection::OnPacketComplete() { congestion_manager_.OnIncomingQuicCongestionFeedbackFrame( last_congestion_frames_[i], time_of_last_received_packet_); } + if (!last_close_frames_.empty()) { + CloseConnection(last_close_frames_[0].error_code, true); + DCHECK(!connected_); + } if (received_packet_manager_.GetNumMissingPackets() != 0) { send_ack_immediately = true; @@ -1286,13 +1307,20 @@ bool QuicConnection::WritePacket(EncryptionLevel level, CloseConnection(QUIC_ENCRYPTION_FAILURE, false); return false; } + + if (encrypted->length() > options()->max_packet_length) { + LOG(DFATAL) << ENDPOINT + << "Writing an encrypted packet larger than max_packet_length:" + << options()->max_packet_length; + } DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number << " : " << (packet->is_fec_packet() ? "FEC " : (retransmittable == HAS_RETRANSMITTABLE_DATA ? "data bearing " : " ack only ")) << ", encryption level: " << QuicUtils::EncryptionLevelToString(level) - << ", length:" << packet->length(); + << ", length:" << packet->length() << ", encrypted length:" + << encrypted->length(); DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece()); @@ -1344,7 +1372,7 @@ bool QuicConnection::ShouldDiscardPacket( HasRetransmittableData retransmittable) { if (!connected_) { DLOG(INFO) << ENDPOINT - << "Not sending packet as connection is disconnected."; + << "Not sending packet as connection is disconnected."; return true; } @@ -1353,7 +1381,7 @@ bool QuicConnection::ShouldDiscardPacket( // Drop packets that are NULL encrypted since the peer won't accept them // anymore. DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number - << " since the packet is NULL encrypted."; + << " since the packet is NULL encrypted."; sent_packet_manager_.DiscardUnackedPacket(sequence_number); return true; } @@ -1367,7 +1395,7 @@ bool QuicConnection::ShouldDiscardPacket( // high water mark, all before we're able to send the packet // then we can simply drop it. DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number - << " since it has already been acked."; + << " since it has already been acked."; return true; } @@ -1385,7 +1413,7 @@ bool QuicConnection::ShouldDiscardPacket( if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) { DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number - << " since a previous transmission has been acked."; + << " since a previous transmission has been acked."; sent_packet_manager_.DiscardUnackedPacket(sequence_number); return true; } @@ -1737,6 +1765,7 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, DLOG(INFO) << ENDPOINT << "Force closing " << guid() << " with error " << QuicUtils::ErrorToString(error) << " (" << error << ") " << details; + ScopedPacketBundler ack_bundler(this, version() > QUIC_VERSION_11); QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(); frame->error_code = error; frame->error_details = details; @@ -1751,6 +1780,13 @@ void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { DCHECK(connected_); connected_ = false; visitor_->OnConnectionClosed(error, from_peer); + // Cancel the alarms so they don't trigger any action now that the + // connection is closed. + ack_alarm_->Cancel(); + resume_writes_alarm_->Cancel(); + retransmission_alarm_->Cancel(); + send_alarm_->Cancel(); + timeout_alarm_->Cancel(); } void QuicConnection::SendGoAway(QuicErrorCode error, diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index c5ad5c4..84dba1d 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -34,7 +34,6 @@ #include "net/quic/quic_alarm.h" #include "net/quic/quic_blocked_writer_interface.h" #include "net/quic/quic_connection_stats.h" -#include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_packet_generator.h" #include "net/quic/quic_packet_writer.h" @@ -49,6 +48,7 @@ NET_EXPORT_PRIVATE extern bool FLAGS_bundle_ack_with_outgoing_packet; namespace net { class QuicClock; +class QuicConfig; class QuicConnection; class QuicDecrypter; class QuicEncrypter; @@ -85,6 +85,9 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called once a specific QUIC version is agreed by both endpoints. virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0; + // Indicates a new QuicConfig has been negotiated. + virtual void OnConfigNegotiated() = 0; + // Called when a blocked socket becomes writable. If all pending bytes for // this visitor are consumed by the connection successfully this should // return true, otherwise it should return false. @@ -199,6 +202,9 @@ class NET_EXPORT_PRIVATE QuicConnection const QuicVersionVector& supported_versions); virtual ~QuicConnection(); + // Sets connection parameters from the supplied |config|. + void SetFromConfig(const QuicConfig& config); + // Send the data in |data| to the peer in as few packets as possible. // Returns a pair with the number of bytes consumed from data, and a boolean // indicating if the fin bit was consumed. This does not indicate the data @@ -672,6 +678,7 @@ class NET_EXPORT_PRIVATE QuicConnection std::vector last_congestion_frames_; std::vector last_rst_frames_; std::vector last_goaway_frames_; + std::vector last_close_frames_; // Then number of packets retransmitted because of nacks // while processed the current ack frame. size_t retransmitted_nacked_packet_count_; diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 2433648..73706f1 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -546,6 +546,7 @@ class QuicConnectionTest : public ::testing::TestWithParam { FLAGS_track_retransmission_history = true; connection_.set_visitor(&visitor_); connection_.SetSendAlgorithm(send_algorithm_); + framer_.set_received_entropy_calculator(&entropy_calculator_); // Simplify tests by not sending feedback unless specifically configured. SetFeedback(NULL); EXPECT_CALL( @@ -810,6 +811,7 @@ class QuicConnectionTest : public ::testing::TestWithParam { QuicGuid guid_; QuicFramer framer_; QuicPacketCreator creator_; + MockEntropyCalculator entropy_calculator_; MockSendAlgorithm* send_algorithm_; TestReceiveAlgorithm* receive_algorithm_; @@ -936,7 +938,11 @@ TEST_P(QuicConnectionTest, RejectPacketTooFarOut) { ProcessDataPacket(6000, 0, !kEntropyFlag); } -TEST_P(QuicConnectionTest, TruncatedAck) { +// TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. +TEST_P(QuicConnectionTest, TruncatedAck11) { + if (QuicVersionMax() > QUIC_VERSION_11) { + return; + } EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); @@ -964,6 +970,38 @@ TEST_P(QuicConnectionTest, TruncatedAck) { EXPECT_FALSE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); } +TEST_P(QuicConnectionTest, TruncatedAck) { + // TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. + if (QuicVersionMax() <= QUIC_VERSION_11) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(256); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(2); + int num_packets = 256 * 2 + 1; + for (int i = 0; i < num_packets; ++i) { + SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL); + } + + QuicAckFrame frame(num_packets, QuicTime::Zero(), 1); + // Create an ack with 256 nacks, none adjacent to one another. + for (QuicPacketSequenceNumber i = 1; i <= 256; ++i) { + frame.received_info.missing_packets.insert(i * 2); + } + EXPECT_CALL(entropy_calculator_, + EntropyHash(511)).WillOnce(testing::Return(0)); + ProcessAckPacket(&frame); + + EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); + + frame.received_info.missing_packets.erase(192); + + // Removing one missing packet allows us to ack 192 and one more range. + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); + ProcessAckPacket(&frame); + EXPECT_FALSE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); +} + TEST_P(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -1099,33 +1137,6 @@ TEST_P(QuicConnectionTest, LargestObservedLower) { ProcessAckPacket(&frame2); } -TEST_P(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) { - EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - // Create an ack with least_unacked is 2 in packet number 1. - creator_.set_sequence_number(0); - QuicAckFrame frame(0, QuicTime::Zero(), 2); - EXPECT_CALL(visitor_, OnCanWrite()).Times(0); - ProcessAckPacket(&frame); -} - -TEST_P(QuicConnectionTest, - NackSequenceNumberGreaterThanLargestReceived) { - EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - - SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); - SendStreamDataToPeer(1, "bar", 3, !kFin, NULL); - SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); - - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - QuicAckFrame frame(0, QuicTime::Zero(), 1); - frame.received_info.missing_packets.insert(3); - EXPECT_CALL(visitor_, OnCanWrite()).Times(0); - ProcessAckPacket(&frame); -} - TEST_P(QuicConnectionTest, AckUnsentData) { // Ack a packet which has not been sent. EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); @@ -2329,6 +2340,12 @@ TEST_P(QuicConnectionTest, InitialTimeout) { connection_.GetTimeoutAlarm()->Fire(); EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); + + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); } TEST_P(QuicConnectionTest, TimeoutAfterSend) { @@ -3063,7 +3080,7 @@ TEST_P(QuicConnectionTest, TestFecGroupLimits) { ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL); } -TEST_P(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { +TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { // Construct a packet with stream frame and connection close frame. header_.public_header.guid = guid_; header_.packet_sequence_number = 1; @@ -3089,7 +3106,7 @@ TEST_P(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { ENCRYPTION_NONE, 1, *packet)); EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true)); - EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(0); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); @@ -3277,7 +3294,7 @@ TEST_P(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) { ack_header.public_header.version_flag = false; ack_header.entropy_flag = !kEntropyFlag; ack_header.fec_flag = true; - ack_header.packet_sequence_number = 42; + ack_header.packet_sequence_number = 1; ack_header.is_in_fec_group = IN_FEC_GROUP; ack_header.fec_group = 1; diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index b0f8117..4451fb6 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -346,6 +346,8 @@ void QuicCryptoClientStream::DoHandshakeLoop( error, "Server hello invalid: " + error_details); return; } + session()->OnConfigNegotiated(); + CrypterPair* crypters = &crypto_negotiated_params_.forward_secure_crypters; // TODO(agl): we don't currently latch this decrypter because the idea diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 5a9042b..ca2fea7 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -9,8 +9,8 @@ #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" -#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/proof_verifier.h" +#include "net/quic/crypto/quic_crypto_client_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_crypto_stream.h" diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 63637f5..1234483 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -7,8 +7,8 @@ #include "base/base64.h" #include "crypto/secure_hash.h" #include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" @@ -62,6 +62,7 @@ void QuicCryptoServerStream::OnHandshakeMessage( CloseConnectionWithDetails(error, error_details); return; } + session()->OnConfigNegotiated(); config->ToHandshakeMessage(&reply); diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index d622342..2b15853 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -12,8 +12,8 @@ #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 493cf80..4d7c99f 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -13,6 +13,8 @@ using base::StringPiece; using std::make_pair; using std::map; +using std::max; +using std::min; using std::numeric_limits; using std::string; @@ -20,19 +22,6 @@ namespace net { namespace { -// TODO(jri): Remove uses of QuicFrameTypeOld when -// QUIC versions < 10 are no longer supported. -enum QuicFrameTypeOld { - PADDING_FRAME_OLD = 0, - STREAM_FRAME_OLD, - ACK_FRAME_OLD, - CONGESTION_FEEDBACK_FRAME_OLD, - RST_STREAM_FRAME_OLD, - CONNECTION_CLOSE_FRAME_OLD, - GOAWAY_FRAME_OLD, - NUM_FRAME_TYPES_OLD -}; - // Mask to select the lowest 48 bits of a sequence number. const QuicPacketSequenceNumber k6ByteSequenceNumberMask = GG_UINT64_C(0x0000FFFFFFFFFFFF); @@ -46,6 +35,10 @@ const QuicPacketSequenceNumber k1ByteSequenceNumberMask = const QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF); const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF); +// Number of bits the sequence number length bits are shifted from the right +// edge of the public header. +const uint8 kPublicHeaderSequenceNumberShift = 4; + // New Frame Types, QUIC v. >= 10: // There are two interpretations for the Frame Type byte in the QUIC protocol, // resulting in two Frame Types: Special Frame Types and Regular Frame Types. @@ -89,6 +82,15 @@ const uint8 kQuicStreamDataLengthMask = 0x01; const uint8 kQuicStreamFinShift = 1; const uint8 kQuicStreamFinMask = 0x01; +// Sequence number size shift used in AckFrames. +const uint8 kQuicSequenceNumberLengthShift = 2; + +// Acks may be truncated. +const uint8 kQuicAckTruncatedShift = 1; +const uint8 kQuicAckTruncatedMask = 0x01; + +// Acks may not have any nacks. +const uint8 kQuicHasNacksMask = 0x01; const uint32 kInvalidDeltaTime = 0xffffffff; @@ -119,6 +121,7 @@ QuicFramer::QuicFramer(const QuicVersionVector& supported_versions, bool is_server) : visitor_(NULL), fec_builder_(NULL), + entropy_calculator_(NULL), error_(QUIC_NO_ERROR), last_sequence_number_(0), last_serialized_guid_(0), @@ -146,14 +149,28 @@ size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version, } // static -size_t QuicFramer::GetMinAckFrameSize() { +size_t QuicFramer::GetMinAckFrameSizev11() { return kQuicFrameTypeSize + kQuicEntropyHashSize + PACKET_6BYTE_SEQUENCE_NUMBER + kQuicEntropyHashSize + - PACKET_6BYTE_SEQUENCE_NUMBER + kQuicDeltaTimeLargestObservedSize + + PACKET_6BYTE_SEQUENCE_NUMBER + kQuicv11DeltaTimeLargestObservedSize + kNumberOfMissingPacketsSize; } // static +size_t QuicFramer::GetMinAckFrameSize( + QuicVersion version, + QuicSequenceNumberLength sequence_number_length, + QuicSequenceNumberLength largest_observed_length) { + if (version <= QUIC_VERSION_11) { + return GetMinAckFrameSizev11(); + } else { + return kQuicFrameTypeSize + kQuicEntropyHashSize + + sequence_number_length + kQuicEntropyHashSize + + largest_observed_length + kQuicDeltaTimeLargestObservedSize; + } +} + +// static size_t QuicFramer::GetMinRstStreamFrameSize() { return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize; @@ -161,8 +178,7 @@ size_t QuicFramer::GetMinRstStreamFrameSize() { // static size_t QuicFramer::GetMinConnectionCloseFrameSize() { - return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize + - GetMinAckFrameSize() - 1; // Don't include the frame type again. + return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize; } // static @@ -176,9 +192,10 @@ size_t QuicFramer::GetMinGoAwayFrameSize() { // QuicEncrypter::GetMaxPlaintextSize. // 16 is a conservative estimate in the case of AEAD_AES_128_GCM_12, which uses // 12-byte tags. +// TODO(ianswett): Deprecate this with QUIC_VERSION_11. size_t QuicFramer::GetMaxUnackedPackets(QuicPacketHeader header) { - return (kMaxPacketSize - GetPacketHeaderSize(header) - - GetMinAckFrameSize() - 16) / PACKET_6BYTE_SEQUENCE_NUMBER; + return (kDefaultMaxPacketSize - GetPacketHeaderSize(header) - + GetMinAckFrameSizev11() - 16) / PACKET_6BYTE_SEQUENCE_NUMBER; } // static @@ -219,12 +236,15 @@ size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { } // static -bool QuicFramer::CanTruncate(const QuicFrame& frame, size_t free_bytes) { +bool QuicFramer::CanTruncate( + QuicVersion version, const QuicFrame& frame, size_t free_bytes) { // TODO(ianswett): GetMinConnectionCloseFrameSize may be incorrect, because // checking for it here results in frames not being added, but the resulting // frames do actually fit. if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) && - free_bytes >= GetMinAckFrameSize()) { + free_bytes >= GetMinAckFrameSize(version, + PACKET_6BYTE_SEQUENCE_NUMBER, + PACKET_6BYTE_SEQUENCE_NUMBER)) { return true; } return false; @@ -239,22 +259,25 @@ bool QuicFramer::IsSupportedVersion(const QuicVersion version) const { return false; } -size_t QuicFramer::GetSerializedFrameLength(const QuicFrame& frame, - size_t free_bytes, - bool first_frame, - bool last_frame) { +size_t QuicFramer::GetSerializedFrameLength( + const QuicFrame& frame, + size_t free_bytes, + bool first_frame, + bool last_frame, + QuicSequenceNumberLength sequence_number_length) { if (frame.type == PADDING_FRAME) { // PADDING implies end of packet. return free_bytes; } - size_t frame_len = ComputeFrameLength(frame, last_frame); + size_t frame_len = + ComputeFrameLength(frame, last_frame, sequence_number_length); if (frame_len > free_bytes) { // Only truncate the first frame in a packet, so if subsequent ones go // over, stop including more frames. if (!first_frame) { return 0; } - if (CanTruncate(frame, free_bytes)) { + if (CanTruncate(quic_version_, frame, free_bytes)) { // Truncate the frame so the packet will not exceed kMaxPacketSize. // Note that we may not use every byte of the writer in this case. DLOG(INFO) << "Truncating large frame"; @@ -264,6 +287,10 @@ size_t QuicFramer::GetSerializedFrameLength(const QuicFrame& frame, return frame_len; } +QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) { } + +QuicFramer::AckFrameInfo::~AckFrameInfo() { } + QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash( const QuicPacketHeader& header) const { if (!header.entropy_flag) { @@ -274,6 +301,7 @@ QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash( return 1 << (header.packet_sequence_number % 8); } +// Test only. SerializedPacket QuicFramer::BuildUnsizedDataPacket( const QuicPacketHeader& header, const QuicFrames& frames) { @@ -284,7 +312,8 @@ SerializedPacket QuicFramer::BuildUnsizedDataPacket( bool first_frame = i == 0; bool last_frame = i == frames.size() - 1; const size_t frame_size = GetSerializedFrameLength( - frames[i], max_plaintext_size - packet_size, first_frame, last_frame); + frames[i], max_plaintext_size - packet_size, first_frame, last_frame, + header.public_header.sequence_number_length); DCHECK(frame_size); packet_size += frame_size; } @@ -298,7 +327,7 @@ SerializedPacket QuicFramer::BuildDataPacket( QuicDataWriter writer(packet_size); const SerializedPacket kNoPacket( 0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); - if (!WritePacketHeader(header, &writer)) { + if (!AppendPacketHeader(header, &writer)) { return kNoPacket; } @@ -321,8 +350,15 @@ SerializedPacket QuicFramer::BuildDataPacket( } break; case ACK_FRAME: - if (!AppendAckFramePayload(*frame.ack_frame, &writer)) { - return kNoPacket; + if (quic_version_ <= QUIC_VERSION_11) { + if (!AppendAckFramePayloadV11(*frame.ack_frame, &writer)) { + return kNoPacket; + } + } else { + if (!AppendAckFramePayloadAndTypeByte( + header, *frame.ack_frame, &writer)) { + return kNoPacket; + } } break; case CONGESTION_FEEDBACK_FRAME: @@ -383,7 +419,7 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header, QuicDataWriter writer(len); const SerializedPacket kNoPacket( 0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); - if (!WritePacketHeader(header, &writer)) { + if (!AppendPacketHeader(header, &writer)) { return kNoPacket; } @@ -538,7 +574,7 @@ bool QuicFramer::ProcessDataPacket( StringPiece payload = reader_->PeekRemainingPayload(); visitor_->OnFecProtectedPayload(payload); } - if (!ProcessFrameData()) { + if (!ProcessFrameData(header)) { DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. DLOG(WARNING) << "Unable to process frame data."; return false; @@ -589,7 +625,7 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, } reader_.reset(new QuicDataReader(payload.data(), payload.length())); - if (!ProcessFrameData()) { + if (!ProcessFrameData(*header)) { DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. DLOG(WARNING) << "Unable to process frame data."; return false; @@ -600,8 +636,8 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, return true; } -bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, - QuicDataWriter* writer) { +bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer) { DCHECK(header.fec_group > 0 || header.is_in_fec_group == NOT_IN_FEC_GROUP); uint8 public_flags = 0; if (header.public_header.reset_flag) { @@ -610,20 +646,10 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, if (header.public_header.version_flag) { public_flags |= PACKET_PUBLIC_FLAGS_VERSION; } - switch (header.public_header.sequence_number_length) { - case PACKET_1BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE; - break; - case PACKET_2BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE; - break; - case PACKET_4BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE; - break; - case PACKET_6BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE; - break; - } + + public_flags |= + GetSequenceNumberFlags(header.public_header.sequence_number_length) + << kPublicHeaderSequenceNumberShift; switch (header.public_header.guid_length) { case PACKET_0BYTE_GUID: @@ -636,7 +662,7 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, return false; } if (!writer->WriteUInt8(header.public_header.guid & k1ByteGuidMask)) { - return false; + return false; } break; case PACKET_4BYTE_GUID: @@ -789,20 +815,9 @@ bool QuicFramer::ProcessPublicHeader( break; } - switch (public_flags & PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE) { - case PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER; - break; - case PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER; - break; - case PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER; - break; - case PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - break; - } + public_header->sequence_number_length = + ReadSequenceNumberLength( + public_flags >> kPublicHeaderSequenceNumberShift); // Read the version only if the packet is from the client. // version flag from the server means version negotiation packet. @@ -843,6 +858,91 @@ bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, return reader.ReadUInt64(guid); } +// static +QuicSequenceNumberLength QuicFramer::ReadSequenceNumberLength(uint8 flags) { + switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) { + case PACKET_FLAGS_6BYTE_SEQUENCE: + return PACKET_6BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_4BYTE_SEQUENCE: + return PACKET_4BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_2BYTE_SEQUENCE: + return PACKET_2BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_1BYTE_SEQUENCE: + return PACKET_1BYTE_SEQUENCE_NUMBER; + default: + LOG(DFATAL) << "Unreachable case statement."; + return PACKET_6BYTE_SEQUENCE_NUMBER; + } +} + +// static +QuicSequenceNumberLength QuicFramer::GetMinSequenceNumberLength( + QuicPacketSequenceNumber sequence_number) { + if (sequence_number < 1 << (PACKET_1BYTE_SEQUENCE_NUMBER * 8)) { + return PACKET_1BYTE_SEQUENCE_NUMBER; + } else if (sequence_number < 1 << (PACKET_2BYTE_SEQUENCE_NUMBER * 8)) { + return PACKET_2BYTE_SEQUENCE_NUMBER; + } else if (sequence_number < + GG_UINT64_C(1) << (PACKET_4BYTE_SEQUENCE_NUMBER * 8)) { + return PACKET_4BYTE_SEQUENCE_NUMBER; + } else { + return PACKET_6BYTE_SEQUENCE_NUMBER; + } +} + +// static +uint8 QuicFramer::GetSequenceNumberFlags( + QuicSequenceNumberLength sequence_number_length) { + switch (sequence_number_length) { + case PACKET_1BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_1BYTE_SEQUENCE; + case PACKET_2BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_2BYTE_SEQUENCE; + case PACKET_4BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_4BYTE_SEQUENCE; + case PACKET_6BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_6BYTE_SEQUENCE; + default: + LOG(DFATAL) << "Unreachable case statement."; + return PACKET_FLAGS_6BYTE_SEQUENCE; + } +} + +// static +QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo( + const QuicAckFrame& frame) { + const ReceivedPacketInfo& received_info = frame.received_info; + + AckFrameInfo ack_info; + if (!received_info.missing_packets.empty()) { + DCHECK_GE(frame.received_info.largest_observed, + *frame.received_info.missing_packets.rend()); + size_t cur_range_length = 0; + SequenceNumberSet::const_iterator iter = + received_info.missing_packets.begin(); + QuicPacketSequenceNumber last_missing = *iter; + ++iter; + for (; iter != received_info.missing_packets.end(); ++iter) { + if (cur_range_length != numeric_limits::max() && + *iter == (last_missing + 1)) { + ++cur_range_length; + } else { + ack_info.nack_ranges[last_missing - cur_range_length] + = cur_range_length; + cur_range_length = 0; + } + ack_info.max_delta = max(ack_info.max_delta, *iter - last_missing); + last_missing = *iter; + } + // Include the last nack range. + ack_info.nack_ranges[last_missing - cur_range_length] = cur_range_length; + // Include the range to the largest observed. + ack_info.max_delta = max(ack_info.max_delta, + received_info.largest_observed - last_missing); + } + return ack_info; +} + bool QuicFramer::ProcessPacketHeader( QuicPacketHeader* header, const QuicEncryptedPacket& packet) { @@ -915,7 +1015,7 @@ bool QuicFramer::ProcessPacketSequenceNumber( return true; } -bool QuicFramer::ProcessFrameData() { +bool QuicFramer::ProcessFrameData(const QuicPacketHeader& header) { if (reader_->IsDoneReading()) { set_detailed_error("Packet has no frames."); return RaiseError(QUIC_MISSING_PAYLOAD); @@ -945,7 +1045,7 @@ bool QuicFramer::ProcessFrameData() { // Ack Frame if (frame_type & kQuicFrameTypeAckMask) { QuicAckFrame frame; - if (!ProcessAckFrame(&frame)) { + if (!ProcessAckFrame(header, frame_type, &frame)) { return RaiseError(QUIC_INVALID_ACK_DATA); } if (!visitor_->OnAckFrame(frame)) { @@ -1002,10 +1102,12 @@ bool QuicFramer::ProcessFrameData() { return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA); } - if (!visitor_->OnAckFrame(frame.ack_frame)) { - DLOG(INFO) << "Visitor asked to stop further processing."; - // Returning true since there was no parsing error. - return true; + if (version() <= QUIC_VERSION_11) { + if (!visitor_->OnAckFrame(frame.ack_frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } } if (!visitor_->OnConnectionCloseFrame(frame)) { @@ -1090,17 +1192,25 @@ bool QuicFramer::ProcessStreamFrame(uint8 frame_type, return true; } -bool QuicFramer::ProcessAckFrame(QuicAckFrame* frame) { - if (!ProcessSentInfo(&frame->sent_info)) { +bool QuicFramer::ProcessAckFrame(const QuicPacketHeader& header, + uint8 frame_type, + QuicAckFrame* frame) { + if (!ProcessSentInfo(header, &frame->sent_info)) { return false; } - if (!ProcessReceivedInfo(&frame->received_info)) { - return false; + if (quic_version_ <= QUIC_VERSION_11) { + if (!ProcessReceivedInfoV11(&frame->received_info)) { + return false; + } + } else { + if (!ProcessReceivedInfo(frame_type, &frame->received_info)) { + return false; + } } return true; } -bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) { +bool QuicFramer::ProcessReceivedInfoV11(ReceivedPacketInfo* received_info) { if (!reader_->ReadBytes(&received_info->entropy_hash, 1)) { set_detailed_error("Unable to read entropy hash for received packets."); return false; @@ -1144,16 +1254,103 @@ bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) { return true; } -bool QuicFramer::ProcessSentInfo(SentPacketInfo* sent_info) { +bool QuicFramer::ProcessReceivedInfo(uint8 frame_type, + ReceivedPacketInfo* received_info) { + // Determine the three lengths from the frame type: largest observed length, + // missing sequence number length, and missing range length. + const QuicSequenceNumberLength missing_sequence_number_length = + ReadSequenceNumberLength(frame_type); + frame_type >>= kQuicSequenceNumberLengthShift; + const QuicSequenceNumberLength largest_observed_sequence_number_length = + ReadSequenceNumberLength(frame_type); + frame_type >>= kQuicSequenceNumberLengthShift; + received_info->is_truncated = frame_type & kQuicAckTruncatedMask; + frame_type >>= kQuicAckTruncatedShift; + bool has_nacks = frame_type & kQuicHasNacksMask; + + if (!reader_->ReadBytes(&received_info->entropy_hash, 1)) { + set_detailed_error("Unable to read entropy hash for received packets."); + return false; + } + + if (!reader_->ReadBytes(&received_info->largest_observed, + largest_observed_sequence_number_length)) { + set_detailed_error("Unable to read largest observed."); + return false; + } + + uint64 delta_time_largest_observed_us; + if (!reader_->ReadUFloat16(&delta_time_largest_observed_us)) { + set_detailed_error("Unable to read delta time largest observed."); + return false; + } + + if (delta_time_largest_observed_us == kUFloat16MaxValue) { + received_info->delta_time_largest_observed = QuicTime::Delta::Infinite(); + } else { + received_info->delta_time_largest_observed = + QuicTime::Delta::FromMicroseconds(delta_time_largest_observed_us); + } + + if (!has_nacks) { + return true; + } + + uint8 num_missing_ranges; + if (!reader_->ReadBytes(&num_missing_ranges, 1)) { + set_detailed_error("Unable to read num missing packet ranges."); + return false; + } + + QuicPacketSequenceNumber last_sequence_number = + received_info->largest_observed; + for (size_t i = 0; i < num_missing_ranges; ++i) { + QuicPacketSequenceNumber missing_delta = 0; + if (!reader_->ReadBytes(&missing_delta, missing_sequence_number_length)) { + set_detailed_error("Unable to read missing sequence number delta."); + return false; + } + last_sequence_number -= missing_delta; + QuicPacketSequenceNumber range_length = 0; + if (!reader_->ReadBytes(&range_length, PACKET_1BYTE_SEQUENCE_NUMBER)) { + set_detailed_error("Unable to read missing sequence number range."); + return false; + } + for (size_t i = 0; i <= range_length; ++i) { + received_info->missing_packets.insert(last_sequence_number - i); + } + // Subtract an extra 1 to ensure ranges are represented efficiently and + // can't overlap by 1 sequence number. This allows a missing_delta of 0 + // to represent an adjacent nack range. + last_sequence_number -= (range_length + 1); + } + + return true; +} + +bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header, + SentPacketInfo* sent_info) { if (!reader_->ReadBytes(&sent_info->entropy_hash, 1)) { set_detailed_error("Unable to read entropy hash for sent packets."); return false; } - if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, - &sent_info->least_unacked)) { - set_detailed_error("Unable to read least unacked."); - return false; + if (quic_version_ <= QUIC_VERSION_11) { + if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + &sent_info->least_unacked)) { + set_detailed_error("Unable to read least unacked."); + return false; + } + } else { + QuicPacketSequenceNumber least_unacked_delta = 0; + if (!reader_->ReadBytes(&least_unacked_delta, + header.public_header.sequence_number_length)) { + set_detailed_error("Unable to read least unacked delta."); + return false; + } + DCHECK_GE(header.packet_sequence_number, least_unacked_delta); + sent_info->least_unacked = + header.packet_sequence_number - least_unacked_delta; } return true; @@ -1204,7 +1401,7 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( inter_arrival->received_packet_times.insert( make_pair(smallest_received, time_received)); - for (int i = 0; i < num_received_packets - 1; ++i) { + for (uint8 i = 0; i < num_received_packets - 1; ++i) { uint16 sequence_delta; if (!reader_->ReadUInt16(&sequence_delta)) { set_detailed_error( @@ -1242,6 +1439,7 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( "Unable to read accumulated number of lost packets."); return false; } + // TODO(ianswett): Remove receive window, since it's constant. uint16 receive_window = 0; if (!reader_->ReadUInt16(&receive_window)) { set_detailed_error("Unable to read receive window."); @@ -1313,9 +1511,11 @@ bool QuicFramer::ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame) { } frame->error_details = error_details.as_string(); - if (!ProcessAckFrame(&frame->ack_frame)) { - DLOG(WARNING) << "Unable to process ack frame."; - return false; + if (quic_version_ <= QUIC_VERSION_11) { + if (!ProcessAckFrame(QuicPacketHeader(), 0, &frame->ack_frame)) { + DLOG(WARNING) << "Unable to process ack frame."; + return false; + } } return true; @@ -1494,8 +1694,32 @@ bool QuicFramer::DecryptPayload(const QuicPacketHeader& header, return true; } -size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, - bool last_frame_in_packet) { +size_t QuicFramer::GetAckFrameSize( + const QuicAckFrame& ack, + QuicSequenceNumberLength sequence_number_length) { + if (quic_version_ <= QUIC_VERSION_11) { + return GetMinAckFrameSizev11() + PACKET_6BYTE_SEQUENCE_NUMBER * + ack.received_info.missing_packets.size(); + } else { + AckFrameInfo ack_info = GetAckFrameInfo(ack); + QuicSequenceNumberLength largest_observed_length = + GetMinSequenceNumberLength(ack.received_info.largest_observed); + QuicSequenceNumberLength missing_sequence_number_length = + GetMinSequenceNumberLength(ack_info.max_delta); + + return GetMinAckFrameSize(quic_version_, + sequence_number_length, + largest_observed_length) + + (ack_info.nack_ranges.empty() ? 0 : kNumberOfMissingPacketsSize) + + ack_info.nack_ranges.size() * + (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); + } +} + +size_t QuicFramer::ComputeFrameLength( + const QuicFrame& frame, + bool last_frame_in_packet, + QuicSequenceNumberLength sequence_number_length) { switch (frame.type) { case STREAM_FRAME: return GetMinStreamFrameSize(quic_version_, @@ -1504,9 +1728,7 @@ size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, last_frame_in_packet) + frame.stream_frame->data.size(); case ACK_FRAME: { - const QuicAckFrame& ack = *frame.ack_frame; - return GetMinAckFrameSize() + PACKET_6BYTE_SEQUENCE_NUMBER * - ack.received_info.missing_packets.size(); + return GetAckFrameSize(*frame.ack_frame, sequence_number_length); } case CONGESTION_FEEDBACK_FRAME: { size_t len = kQuicFrameTypeSize; @@ -1546,11 +1768,17 @@ size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, return GetMinRstStreamFrameSize() + frame.rst_stream_frame->error_details.size(); case CONNECTION_CLOSE_FRAME: { - const QuicAckFrame& ack = frame.connection_close_frame->ack_frame; - return GetMinConnectionCloseFrameSize() + - frame.connection_close_frame->error_details.size() + - PACKET_6BYTE_SEQUENCE_NUMBER * - ack.received_info.missing_packets.size(); + if (quic_version_ <= QUIC_VERSION_11) { + const QuicAckFrame& ack = frame.connection_close_frame->ack_frame; + // Don't include the frame type again. + return GetMinConnectionCloseFrameSize() + GetMinAckFrameSizev11() - 1 + + frame.connection_close_frame->error_details.size() + + PACKET_6BYTE_SEQUENCE_NUMBER * + ack.received_info.missing_packets.size(); + } else { + return GetMinConnectionCloseFrameSize() + + frame.connection_close_frame->error_details.size(); + } } case GOAWAY_FRAME: return GetMinGoAwayFrameSize() + frame.goaway_frame->reason_phrase.size(); @@ -1597,7 +1825,10 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, break; } case ACK_FRAME: { - // TODO(ianswett): Use extra 5 bits in the ack framing. + // Quic Version 12 and above append the type byte later. + if (quic_version_ >= QUIC_VERSION_12) { + return true; + } type_byte = kQuicFrameTypeAckMask; break; } @@ -1701,8 +1932,7 @@ void QuicFramer::set_version(const QuicVersion version) { quic_version_ = version; } -// TODO(ianswett): Use varints or another more compact approach for all deltas. -bool QuicFramer::AppendAckFramePayload( +bool QuicFramer::AppendAckFramePayloadV11( const QuicAckFrame& frame, QuicDataWriter* writer) { // TODO(satyamshekhar): Decide how often we really should send this @@ -1775,6 +2005,139 @@ bool QuicFramer::AppendAckFramePayload( return true; } +bool QuicFramer::AppendAckFramePayloadAndTypeByte( + const QuicPacketHeader& header, + const QuicAckFrame& frame, + QuicDataWriter* writer) { + AckFrameInfo ack_info = GetAckFrameInfo(frame); + QuicPacketSequenceNumber ack_largest_observed = + frame.received_info.largest_observed; + QuicSequenceNumberLength largest_observed_length = + GetMinSequenceNumberLength(ack_largest_observed); + QuicSequenceNumberLength missing_sequence_number_length = + GetMinSequenceNumberLength(ack_info.max_delta); + // Determine whether we need to truncate ranges. + size_t available_range_bytes = writer->capacity() - writer->length() - + GetMinAckFrameSize(quic_version_, + header.public_header.sequence_number_length, + largest_observed_length); + size_t max_num_ranges = available_range_bytes / + (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); + max_num_ranges = + min(static_cast(numeric_limits::max()), max_num_ranges); + bool truncated = ack_info.nack_ranges.size() > max_num_ranges; + DLOG_IF(INFO, truncated) << "Truncating ack from " + << ack_info.nack_ranges.size() << " ranges to " + << max_num_ranges; + + // Write out the type byte by setting the low order bits and doing shifts + // to make room for the next bit flags to be set. + // Whether there are any nacks. + uint8 type_byte = ack_info.nack_ranges.empty() ? 0 : kQuicHasNacksMask; + + // truncating bit. + type_byte <<= kQuicAckTruncatedShift; + type_byte |= truncated ? kQuicAckTruncatedMask : 0; + + // Largest observed sequence number length. + type_byte <<= kQuicSequenceNumberLengthShift; + type_byte |= GetSequenceNumberFlags(largest_observed_length); + + // Missing sequence number length. + type_byte <<= kQuicSequenceNumberLengthShift; + type_byte |= GetSequenceNumberFlags(missing_sequence_number_length); + + type_byte |= kQuicFrameTypeAckMask; + + if (!writer->WriteUInt8(type_byte)) { + return false; + } + + // TODO(satyamshekhar): Decide how often we really should send this + // entropy_hash update. + if (!writer->WriteUInt8(frame.sent_info.entropy_hash)) { + return false; + } + + DCHECK_GE(header.packet_sequence_number, frame.sent_info.least_unacked); + const QuicPacketSequenceNumber least_unacked_delta = + header.packet_sequence_number - frame.sent_info.least_unacked; + if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length, + least_unacked_delta, writer)) { + return false; + } + + const ReceivedPacketInfo& received_info = frame.received_info; + QuicPacketEntropyHash ack_entropy_hash = received_info.entropy_hash; + NackRangeMap::reverse_iterator ack_iter = ack_info.nack_ranges.rbegin(); + if (truncated) { + // Skip the nack ranges which the truncated ack won't include and set + // a correct largest observed for the truncated ack. + for (size_t i = 1; i < (ack_info.nack_ranges.size() - max_num_ranges); + ++i) { + ++ack_iter; + } + // If the last range is followed by acks, include them. + // If the last range is followed by another range, specify the end of the + // range as the largest_observed. + ack_largest_observed = ack_iter->first - 1; + // Also update the entropy so it matches the largest observed. + ack_entropy_hash = entropy_calculator_->EntropyHash(ack_largest_observed); + ++ack_iter; + } + + if (!writer->WriteUInt8(ack_entropy_hash)) { + return false; + } + + if (!AppendPacketSequenceNumber(largest_observed_length, + ack_largest_observed, writer)) { + return false; + } + + uint64 delta_time_largest_observed_us = kUFloat16MaxValue; + if (!received_info.delta_time_largest_observed.IsInfinite()) { + delta_time_largest_observed_us = + received_info.delta_time_largest_observed.ToMicroseconds(); + } + + if (!writer->WriteUFloat16(delta_time_largest_observed_us)) { + return false; + } + + if (ack_info.nack_ranges.empty()) { + return true; + } + + const uint8 num_missing_ranges = + min(ack_info.nack_ranges.size(), max_num_ranges); + if (!writer->WriteBytes(&num_missing_ranges, 1)) { + return false; + } + + int num_ranges_written = 0; + QuicPacketSequenceNumber last_sequence_written = ack_largest_observed; + for (; ack_iter != ack_info.nack_ranges.rend(); ++ack_iter) { + // Calculate the delta to the last number in the range. + QuicPacketSequenceNumber missing_delta = + last_sequence_written - (ack_iter->first + ack_iter->second); + if (!AppendPacketSequenceNumber(missing_sequence_number_length, + missing_delta, writer)) { + return false; + } + if (!AppendPacketSequenceNumber(PACKET_1BYTE_SEQUENCE_NUMBER, + ack_iter->second, writer)) { + return false; + } + // Subtract 1 so a missing_delta of 0 means an adjacent range. + last_sequence_written = ack_iter->first - 1; + ++num_ranges_written; + } + + DCHECK_EQ(num_missing_ranges, num_ranges_written); + return true; +} + bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( const QuicCongestionFeedbackFrame& frame, QuicDataWriter* writer) { @@ -1893,7 +2256,9 @@ bool QuicFramer::AppendConnectionCloseFramePayload( if (!writer->WriteStringPiece16(frame.error_details)) { return false; } - AppendAckFramePayload(frame.ack_frame, writer); + if (quic_version_ <= QUIC_VERSION_11) { + return AppendAckFramePayloadV11(frame.ack_frame, writer); + } return true; } diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 5d55138..5b07091 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -44,7 +44,9 @@ const size_t kQuicStreamPayloadLengthSize = 2; const size_t kQuicEntropyHashSize = 1; // Size in bytes reserved for the delta time of the largest observed // sequence number in ack frames. -const size_t kQuicDeltaTimeLargestObservedSize = 4; +// TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. +const size_t kQuicv11DeltaTimeLargestObservedSize = 4; +const size_t kQuicDeltaTimeLargestObservedSize = 2; // Size in bytes reserved for the number of missing packets in ack frames. const size_t kNumberOfMissingPacketsSize = 1; @@ -233,7 +235,12 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicStreamOffset offset, bool last_frame_in_packet); // Size in bytes of all ack frame fields without the missing packets. - static size_t GetMinAckFrameSize(); + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. + static size_t GetMinAckFrameSizev11(); + static size_t GetMinAckFrameSize( + QuicVersion version, + QuicSequenceNumberLength sequence_number_length, + QuicSequenceNumberLength largest_observed_length); // Size in bytes of all reset stream frame without the error details. static size_t GetMinRstStreamFrameSize(); // Size in bytes of all connection close frame fields without the error @@ -242,7 +249,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // Size in bytes of all GoAway frame fields without the reason phrase. static size_t GetMinGoAwayFrameSize(); // The maximum number of nacks which can be transmitted in a single ack packet - // without exceeding kMaxPacketSize. + // without exceeding kDefaultMaxPacketSize. static size_t GetMaxUnackedPackets(QuicPacketHeader header); // Size in bytes required to serialize the stream id. static size_t GetStreamIdSize(QuicStreamId stream_id); @@ -252,15 +259,18 @@ class NET_EXPORT_PRIVATE QuicFramer { static size_t GetVersionNegotiationPacketSize(size_t number_versions); - static bool CanTruncate(const QuicFrame& frame, size_t free_bytes); + static bool CanTruncate( + QuicVersion version, const QuicFrame& frame, size_t free_bytes); // Returns the number of bytes added to the packet for the specified frame, // and 0 if the frame doesn't fit. Includes the header size for the first // frame. - size_t GetSerializedFrameLength(const QuicFrame& frame, - size_t free_bytes, - bool first_frame, - bool last_frame); + size_t GetSerializedFrameLength( + const QuicFrame& frame, + size_t free_bytes, + bool first_frame, + bool last_frame, + QuicSequenceNumberLength sequence_number_length); // Returns the associated data from the encrypted packet |encrypted| as a // stringpiece. @@ -341,9 +351,28 @@ class NET_EXPORT_PRIVATE QuicFramer { static bool ReadGuidFromPacket(const QuicEncryptedPacket& packet, QuicGuid* guid); + static QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags); + + // The minimum sequence number length required to represent |sequence_number|. + static QuicSequenceNumberLength GetMinSequenceNumberLength( + QuicPacketSequenceNumber sequence_number); + private: friend class test::QuicFramerPeer; + typedef std::map NackRangeMap; + + class AckFrameInfo { + public: + AckFrameInfo(); + virtual ~AckFrameInfo(); + + // The maximum delta between ranges. + QuicPacketSequenceNumber max_delta; + // Nack ranges starting with start sequence numbers and lengths. + NackRangeMap nack_ranges; + }; + QuicPacketEntropyHash GetPacketEntropyHash( const QuicPacketHeader& header) const; @@ -354,9 +383,6 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessVersionNegotiationPacket(QuicPacketPublicHeader* public_header); - bool WritePacketHeader(const QuicPacketHeader& header, - QuicDataWriter* writer); - bool ProcessPublicHeader(QuicPacketPublicHeader* header); bool ProcessPacketHeader(QuicPacketHeader* header, @@ -365,11 +391,16 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessPacketSequenceNumber( QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber* sequence_number); - bool ProcessFrameData(); + bool ProcessFrameData(const QuicPacketHeader& header); bool ProcessStreamFrame(uint8 frame_type, QuicStreamFrame* frame); - bool ProcessAckFrame(QuicAckFrame* frame); - bool ProcessReceivedInfo(ReceivedPacketInfo* received_info); - bool ProcessSentInfo(SentPacketInfo* sent_info); + bool ProcessAckFrame(const QuicPacketHeader& header, + uint8 frame_type, + QuicAckFrame* frame); + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. + bool ProcessReceivedInfoV11(ReceivedPacketInfo* received_info); + bool ProcessReceivedInfo(uint8 frame_type, ReceivedPacketInfo* received_info); + bool ProcessSentInfo(const QuicPacketHeader& public_header, + SentPacketInfo* sent_info); bool ProcessQuicCongestionFeedbackFrame( QuicCongestionFeedbackFrame* congestion_feedback); bool ProcessRstStreamFrame(QuicRstStreamFrame* frame); @@ -385,22 +416,39 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber packet_sequence_number) const; + // Computes the wire size in bytes of the |ack| frame, assuming no truncation. + size_t GetAckFrameSize(const QuicAckFrame& ack, + QuicSequenceNumberLength sequence_number_length); + // Computes the wire size in bytes of the payload of |frame|. - size_t ComputeFrameLength(const QuicFrame& frame, bool last_frame_in_packet); + size_t ComputeFrameLength(const QuicFrame& frame, + bool last_frame_in_packet, + QuicSequenceNumberLength sequence_number_length); static bool AppendPacketSequenceNumber( QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber packet_sequence_number, QuicDataWriter* writer); + static uint8 GetSequenceNumberFlags( + QuicSequenceNumberLength sequence_number_length); + + static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame); + + bool AppendPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer); bool AppendTypeByte(const QuicFrame& frame, bool last_frame_in_packet, QuicDataWriter* writer); bool AppendStreamFramePayload(const QuicStreamFrame& frame, bool last_frame_in_packet, QuicDataWriter* builder); - bool AppendAckFramePayload(const QuicAckFrame& frame, - QuicDataWriter* builder); + bool AppendAckFramePayloadAndTypeByte(const QuicPacketHeader& header, + const QuicAckFrame& frame, + QuicDataWriter* builder); + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. + bool AppendAckFramePayloadV11(const QuicAckFrame& frame, + QuicDataWriter* builder); bool AppendQuicCongestionFeedbackFramePayload( const QuicCongestionFeedbackFrame& frame, QuicDataWriter* builder); diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 31c7c45..c599f66 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -1589,7 +1589,10 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) { EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); } -TEST_P(QuicFramerTest, AckFrame) { +TEST_P(QuicFramerTest, AckFramev11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -1651,13 +1654,13 @@ TEST_P(QuicFramerTest, AckFrame) { const size_t kMissingDeltaTimeOffset = kLargestObservedOffset + PACKET_6BYTE_SEQUENCE_NUMBER; const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + - kQuicDeltaTimeLargestObservedSize; + kQuicv11DeltaTimeLargestObservedSize; const size_t kMissingPacketsOffset = kNumMissingPacketOffset + kNumberOfMissingPacketsSize; // Now test framing boundaries const size_t missing_packets_size = 1 * PACKET_6BYTE_SEQUENCE_NUMBER; for (size_t i = kQuicFrameTypeSize; - i < QuicFramer::GetMinAckFrameSize() + missing_packets_size; ++i) { + i < QuicFramer::GetMinAckFrameSizev11() + missing_packets_size; ++i) { string expected_error; if (i < kLeastUnackedOffset) { expected_error = "Unable to read entropy hash for sent packets."; @@ -1682,6 +1685,249 @@ TEST_P(QuicFramerTest, AckFrame) { } } +TEST_P(QuicFramerTest, AckFrame) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packets + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0xAB, frame.sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame.received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed); + ASSERT_EQ(1u, frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + frame.received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked); + + const size_t kSentEntropyOffset = kQuicFrameTypeSize; + const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize; + const size_t kReceivedEntropyOffset = kLeastUnackedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kLargestObservedOffset = kReceivedEntropyOffset + + kQuicEntropyHashSize; + const size_t kMissingDeltaTimeOffset = kLargestObservedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + + kQuicDeltaTimeLargestObservedSize; + const size_t kMissingPacketsOffset = kNumMissingPacketOffset + + kNumberOfMissingPacketsSize; + const size_t kMissingPacketsRange = kMissingPacketsOffset + + PACKET_1BYTE_SEQUENCE_NUMBER; + // Now test framing boundaries + const size_t ack_frame_size = kMissingPacketsRange + + PACKET_1BYTE_SEQUENCE_NUMBER; + for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { + string expected_error; + if (i < kLeastUnackedOffset) { + expected_error = "Unable to read entropy hash for sent packets."; + } else if (i < kReceivedEntropyOffset) { + expected_error = "Unable to read least unacked delta."; + } else if (i < kLargestObservedOffset) { + expected_error = "Unable to read entropy hash for received packets."; + } else if (i < kMissingDeltaTimeOffset) { + expected_error = "Unable to read largest observed."; + } else if (i < kNumMissingPacketOffset) { + expected_error = "Unable to read delta time largest observed."; + } else if (i < kMissingPacketsOffset) { + expected_error = "Unable to read num missing packet ranges."; + } else if (i < kMissingPacketsRange) { + expected_error = "Unable to read missing sequence number delta."; + } else { + expected_error = "Unable to read missing sequence number range."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_ACK_DATA); + } +} + +TEST_P(QuicFramerTest, AckFrameNoNacks) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (no nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x4C, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame* frame = visitor_.ack_frames_[0]; + EXPECT_EQ(0xAB, frame->sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame->received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), + frame->received_info.largest_observed); + ASSERT_EQ(0u, frame->received_info.missing_packets.size()); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame->sent_info.least_unacked); + + // Verify that the packet re-serializes identically. + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + scoped_ptr data( + framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, AckFrame500Nacks) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges + 0x02, + // missing packet delta + 0x01, + // 243 more missing packets in range. + // The ranges are listed in this order so the re-constructed packet matches. + 0xF3, + // No gap between ranges + 0x00, + // 255 more missing packets in range. + 0xFF, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame* frame = visitor_.ack_frames_[0]; + EXPECT_EQ(0xAB, frame->sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame->received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), + frame->received_info.largest_observed); + ASSERT_EQ(500u, frame->received_info.missing_packets.size()); + SequenceNumberSet::const_iterator first_missing_iter = + frame->received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter); + SequenceNumberSet::const_reverse_iterator last_missing_iter = + frame->received_info.missing_packets.rbegin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame->sent_info.least_unacked); + + // Verify that the packet re-serializes identically. + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + scoped_ptr data( + framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { unsigned char packet[] = { // public flags (8 byte guid) @@ -1971,7 +2217,10 @@ TEST_P(QuicFramerTest, RstStreamFrame) { } } -TEST_P(QuicFramerTest, ConnectionCloseFrame) { +TEST_P(QuicFramerTest, ConnectionCloseFramev11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -2042,8 +2291,68 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { // Now test framing boundaries for (size_t i = kQuicFrameTypeSize; - i < QuicFramer::GetMinConnectionCloseFrameSize() - - QuicFramer::GetMinAckFrameSize(); ++i) { + i < QuicFramer::GetMinConnectionCloseFrameSize(); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) { + expected_error = "Unable to read connection close error code."; + } else { + expected_error = "Unable to read connection close error details."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA); + } +} + +TEST_P(QuicFramerTest, ConnectionCloseFrame) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (connection close frame) + 0x02, + // error code + 0x11, 0x00, 0x00, 0x00, + + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(0x11, visitor_.connection_close_frame_.error_code); + EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details); + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + // Now test framing boundaries + for (size_t i = kQuicFrameTypeSize; + i < QuicFramer::GetMinConnectionCloseFrameSize(); ++i) { string expected_error; if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) { expected_error = "Unable to read connection close error code."; @@ -2571,7 +2880,10 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, BuildAckFramePacket) { +TEST_P(QuicFramerTest, BuildAckFramePacketv11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2635,6 +2947,75 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) { AsChars(packet), arraysize(packet)); } +TEST_P(QuicFramerTest, BuildAckFramePacket) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + QuicPacketHeader header; + header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8); + header.fec_group = 0; + + QuicAckFrame ack_frame; + ack_frame.received_info.entropy_hash = 0x43; + ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF); + ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + ack_frame.received_info.missing_packets.insert( + GG_UINT64_C(0x770123456789ABE)); + ack_frame.sent_info.entropy_hash = 0x14; + ack_frame.sent_info.least_unacked = GG_UINT64_C(0x770123456789AA0); + + QuicFrames frames; + frames.push_back(QuicFrame(&ack_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of sent packets till least awaiting - 1. + 0x14, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0x43, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + }; + + scoped_ptr data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); @@ -2880,7 +3261,10 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, BuildCloseFramePacket) { +TEST_P(QuicFramerTest, BuildCloseFramePacketv11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2957,6 +3341,60 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { AsChars(packet), arraysize(packet)); } +TEST_P(QuicFramerTest, BuildCloseFramePacket) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + QuicPacketHeader header; + header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; + + QuicConnectionCloseFrame close_frame; + close_frame.error_code = static_cast(0x05060708); + close_frame.error_details = "because I can"; + + QuicFrames frames; + frames.push_back(QuicFrame(&close_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (connection close frame) + 0x02, + // error code + 0x08, 0x07, 0x06, 0x05, + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + scoped_ptr data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_P(QuicFramerTest, BuildGoAwayPacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); @@ -3193,9 +3631,9 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { QuicAckFrame* ack_frame = &close_frame.ack_frame; close_frame.error_code = static_cast(0x05); close_frame.error_details = "because I can"; - ack_frame->received_info.largest_observed = 201; + ack_frame->received_info.largest_observed = 601; ack_frame->sent_info.least_unacked = 0; - for (uint64 i = 1; i < ack_frame->received_info.largest_observed; ++i) { + for (uint64 i = 1; i < ack_frame->received_info.largest_observed; i += 2) { ack_frame->received_info.missing_packets.insert(i); } @@ -3215,6 +3653,8 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { *raw_ack_packet)); // Create a packet with just connection close. + // TODO(ianswett): Remove this section when v11 is retired because v12 no + // longer embeds an ack frame in the connection close frame. frames.clear(); frame.type = CONNECTION_CLOSE_FRAME; frame.connection_close_frame = &close_frame; @@ -3230,6 +3670,20 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { // Now make sure we can turn our ack packet back into an ack frame ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0u, processed_ack_frame.sent_info.least_unacked); + if (GetParam() > QUIC_VERSION_11) { + EXPECT_TRUE(processed_ack_frame.received_info.is_truncated); + EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed); + ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + processed_ack_frame.received_info.missing_packets.begin(); + EXPECT_EQ(1u, *missing_iter); + SequenceNumberSet::const_reverse_iterator last_missing_iter = + processed_ack_frame.received_info.missing_packets.rbegin(); + EXPECT_EQ(509u, *last_missing_iter); + } // And do the same for the close frame. ASSERT_TRUE(framer_.ProcessPacket(*close_packet)); @@ -3447,6 +3901,9 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { } TEST_P(QuicFramerTest, ConnectionCloseWithInvalidAck) { + if (GetParam() > QUIC_VERSION_11) { + return; + } unsigned char packet[] = { // public flags (8 byte guid) 0x3C, diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index 17839af..fe5a712 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -90,15 +90,8 @@ void QuicPacketCreator::UpdateSequenceNumberLength( bytes_per_second / options_.max_packet_length; const uint64 delta = max(current_delta, congestion_window); - if (delta < 1 << ((PACKET_1BYTE_SEQUENCE_NUMBER * 8) - 2)) { - options_.send_sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - } else if (delta < 1 << ((PACKET_2BYTE_SEQUENCE_NUMBER * 8) - 2)) { - options_.send_sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER; - } else if (delta < 1 << ((PACKET_4BYTE_SEQUENCE_NUMBER * 8) - 2)) { - options_.send_sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER; - } else { - options_.send_sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER; - } + options_.send_sequence_number_length = + QuicFramer::GetMinSequenceNumberLength(delta * 4); } bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, @@ -137,7 +130,9 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, } if (data.size() == 0) { - DCHECK(fin); + if (!fin) { + LOG(DFATAL) << "Creating a stream frame with no data or fin."; + } // Create a new packet for the fin, if necessary. *frame = QuicFrame(new QuicStreamFrame(id, true, offset, "")); return 0; @@ -294,8 +289,8 @@ SerializedPacket QuicPacketCreator::SerializePacket() { size_t max_plaintext_size = framer_->GetMaxPlaintextSize(options_.max_packet_length); DCHECK_GE(max_plaintext_size, packet_size_); - // ACK and CONNECTION_CLOSE Frames will only be truncated only if they're - // the first frame in the packet. If truncation is to occure, then + // ACK and CONNECTION_CLOSE Frames will be truncated only if they're + // the first frame in the packet. If truncation is to occur, then // GetSerializedFrameLength will have returned all bytes free. bool possibly_truncated = packet_size_ != max_plaintext_size || @@ -304,7 +299,10 @@ SerializedPacket QuicPacketCreator::SerializePacket() { queued_frames_.back().type == CONNECTION_CLOSE_FRAME); SerializedPacket serialized = framer_->BuildDataPacket(header, queued_frames_, packet_size_); - DCHECK(serialized.packet); + if (!serialized.packet) { + LOG(DFATAL) << "Failed to serialize " << queued_frames_.size() + << " frames."; + } // Because of possible truncation, we can't be confident that our // packet size calculation worked correctly. if (!possibly_truncated) @@ -329,7 +327,10 @@ SerializedPacket QuicPacketCreator::SerializeFec() { fec_group_.reset(NULL); fec_group_number_ = 0; packet_size_ = 0; - DCHECK(serialized.packet); + if (!serialized.packet) { + LOG(DFATAL) << "Failed to serialize fec packet for group:" + << fec_data.fec_group; + } DCHECK_GE(options_.max_packet_length, serialized.packet->length()); return serialized; } @@ -393,7 +394,8 @@ bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) { bool QuicPacketCreator::AddFrame(const QuicFrame& frame, bool save_retransmittable_frames) { size_t frame_len = framer_->GetSerializedFrameLength( - frame, BytesFree(), queued_frames_.empty(), true); + frame, BytesFree(), queued_frames_.empty(), true, + options()->send_sequence_number_length); if (frame_len == 0) { return false; } diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index 614ebbc..1d76d87 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h @@ -33,7 +33,7 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // Options for controlling how packets are created. struct Options { Options() - : max_packet_length(kMaxPacketSize), + : max_packet_length(kDefaultMaxPacketSize), max_packets_per_fec_group(0), send_guid_length(PACKET_8BYTE_GUID), send_sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER) {} @@ -128,6 +128,9 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // in BytesFree. size_t PacketSize() const; + // TODO(jri): AddSavedFrame calls AddFrame, which only saves the frame + // if it is a stream frame, not other types of frames. Fix this API; + // add a AddNonSavedFrame method. // Adds |frame| to the packet creator's list of frames to be serialized. // Returns false if the frame doesn't fit into the current packet. bool AddSavedFrame(const QuicFrame& frame); diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index 841d5d1..393de10 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -258,7 +258,7 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) { // Ensure the next FEC group starts using the new sequence number length. serialized = creator_.SerializeAllFrames(frames_); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); - delete frames_[0].stream_frame; + delete frames_[0].ack_frame; delete serialized.packet; } @@ -292,7 +292,10 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { +// TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. +TEST_F(QuicPacketCreatorTest, SerializeConnectionClosev11) { + client_framer_.set_version(QUIC_VERSION_11); + server_framer_.set_version(QUIC_VERSION_11); QuicConnectionCloseFrame frame; frame.error_code = QUIC_NO_ERROR; frame.error_details = "error"; @@ -313,6 +316,30 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { delete serialized.packet; } +TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { + // TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. + if (QuicVersionMax() <= QUIC_VERSION_11) { + return; + } + QuicConnectionCloseFrame frame; + frame.error_code = QUIC_NO_ERROR; + frame.error_details = "error"; + frame.ack_frame = QuicAckFrame(0u, QuicTime::Zero(), 0u); + + SerializedPacket serialized = creator_.SerializeConnectionClose(&frame); + ASSERT_EQ(1u, serialized.sequence_number); + ASSERT_EQ(1u, creator_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPacket(serialized.packet); + delete serialized.packet; +} + TEST_F(QuicPacketCreatorTest, CreateStreamFrame) { QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, "test", 0u, false, &frame); @@ -362,7 +389,7 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) { // Compute the total overhead for a single frame in packet. const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() + GetStreamFrameOverhead(); - size_t capacity = kMaxPacketSize - overhead; + size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { string data(capacity + delta, 'A'); @@ -391,7 +418,7 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() + GetStreamFrameOverhead(); ASSERT_GT(kMaxPacketSize, overhead); - size_t capacity = kMaxPacketSize - overhead; + size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { string data(capacity + delta, 'A'); @@ -408,10 +435,10 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { // (1 byte) and to expand the stream frame (another 2 bytes) the packet // will not be padded. if (bytes_free < 3) { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize) - bytes_free, - serialized_packet.packet->length()); + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize) + - bytes_free, serialized_packet.packet->length()); } else { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize), + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize), serialized_packet.packet->length()); } delete serialized_packet.packet; @@ -426,8 +453,8 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { // Compute the total overhead for a single frame in packet. const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() + GetStreamFrameOverhead(); - ASSERT_GT(kMaxPacketSize, overhead); - size_t capacity = kMaxPacketSize - overhead; + ASSERT_GT(kDefaultMaxPacketSize, overhead); + size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { string data(capacity + delta, 'A'); @@ -441,10 +468,10 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { SerializedPacket serialized_packet = creator_.SerializePacket(); ASSERT_TRUE(serialized_packet.packet); if (bytes_free > 0) { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize) - bytes_free, - serialized_packet.packet->length()); + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize) + - bytes_free, serialized_packet.packet->length()); } else { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize), + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize), serialized_packet.packet->length()); } delete serialized_packet.packet; @@ -576,7 +603,7 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { creator_.BytesFree()); // Add a variety of frame types and then a padding frame. - QuicAckFrame ack_frame; + QuicAckFrame ack_frame(0u, QuicTime::Zero(), 0u); EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); EXPECT_TRUE(creator_.HasPendingFrames()); diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index 01275e2..d2bf7fa 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -216,7 +216,9 @@ bool QuicPacketGenerator::AddNextPendingFrame() { return !should_send_feedback_; } - DCHECK(!queued_control_frames_.empty()); + if (queued_control_frames_.empty()) { + LOG(DFATAL) << "AddNextPendingFrame called with no queued control frames."; + } if (!AddFrame(queued_control_frames_.back())) { // Packet was full. return false; diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc index a1e30aa..71b35e7 100644 --- a/net/quic/quic_packet_generator_test.cc +++ b/net/quic/quic_packet_generator_test.cc @@ -434,7 +434,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) { } // Send enough data to create 3 packets: two full and one partial. - size_t data_len = 2 * kMaxPacketSize + 100; + size_t data_len = 2 * kDefaultMaxPacketSize + 100; QuicConsumedData consumed = generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -466,7 +466,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) { } // Send enough data to create 2 packets: one full and one partial. - size_t data_len = 1 * kMaxPacketSize + 100; + size_t data_len = 1 * kDefaultMaxPacketSize + 100; QuicConsumedData consumed = generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -584,7 +584,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { } // Send enough data to exceed one packet - size_t data_len = kMaxPacketSize + 100; + size_t data_len = kDefaultMaxPacketSize + 100; QuicConsumedData consumed = generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index 23c3b4f..acb0edc 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -127,6 +127,8 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) { return MakeQuicTag('Q', '0', '1', '0'); case QUIC_VERSION_11: return MakeQuicTag('Q', '0', '1', '1'); + case QUIC_VERSION_12: + return MakeQuicTag('Q', '0', '1', '2'); default: // This shold be an ERROR because we should never attempt to convert an // invalid QuicVersion to be written to the wire. @@ -155,6 +157,7 @@ string QuicVersionToString(const QuicVersion version) { switch (version) { RETURN_STRING_LITERAL(QUIC_VERSION_10); RETURN_STRING_LITERAL(QUIC_VERSION_11); + RETURN_STRING_LITERAL(QUIC_VERSION_12); default: return "QUIC_VERSION_UNSUPPORTED"; } @@ -196,7 +199,8 @@ ostream& operator<<(ostream& os, const QuicPacketHeader& header) { // TODO(ianswett): Initializing largest_observed to 0 should not be necessary. ReceivedPacketInfo::ReceivedPacketInfo() : largest_observed(0), - delta_time_largest_observed(QuicTime::Delta::Infinite()) { + delta_time_largest_observed(QuicTime::Delta::Infinite()), + is_truncated(false) { } ReceivedPacketInfo::~ReceivedPacketInfo() {} diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 6c152e8..aff081c 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -45,8 +45,18 @@ typedef std::vector QuicTagVector; typedef uint32 QuicPriority; // TODO(rch): Consider Quic specific names for these constants. -// Maximum size in bytes of a QUIC packet. -const QuicByteCount kMaxPacketSize = 1200; +// Default and initial maximum size in bytes of a QUIC packet. +const QuicByteCount kDefaultMaxPacketSize = 1200; +// The maximum packet size of any QUIC packet, based on ethernet's max size, +// minus the IP and UDP headers. +const QuicByteCount kMaxPacketSize = 1472; + +// Maximum size of the initial congestion window in packets. +const size_t kDefaultInitialWindow = 10; +const size_t kMaxInitialWindow = 100; + +// Don't allow a client to suggest an RTT longer than 15 seconds. +const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond; // Maximum number of open streams per connection. const size_t kDefaultMaxStreamsPerConnection = 100; @@ -146,6 +156,14 @@ enum QuicSequenceNumberLength { PACKET_6BYTE_SEQUENCE_NUMBER = 6 }; +// Used to indicate a QuicSequenceNumberLength using two flag bits. +enum QuicSequenceNumberLengthFlags { + PACKET_FLAGS_1BYTE_SEQUENCE = 0, // 00 + PACKET_FLAGS_2BYTE_SEQUENCE = 1, // 01 + PACKET_FLAGS_4BYTE_SEQUENCE = 1 << 1, // 10 + PACKET_FLAGS_6BYTE_SEQUENCE = 1 << 1 | 1, // 11 +}; + // The public flags are specified in one byte. enum QuicPacketPublicFlags { PACKET_PUBLIC_FLAGS_NONE = 0, @@ -171,10 +189,10 @@ enum QuicPacketPublicFlags { // --01----: 2 bytes // --10----: 4 bytes // --11----: 6 bytes - PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE = 0, - PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE = 1 << 4, - PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE = 1 << 5, - PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE = 1 << 5 | 1 << 4, + PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE = PACKET_FLAGS_1BYTE_SEQUENCE << 4, + PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE = PACKET_FLAGS_2BYTE_SEQUENCE << 4, + PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE = PACKET_FLAGS_4BYTE_SEQUENCE << 4, + PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE = PACKET_FLAGS_6BYTE_SEQUENCE << 4, // All bits set (bits 6 and 7 are not currently used): 00111111 PACKET_PUBLIC_FLAGS_MAX = (1 << 6) - 1 @@ -208,7 +226,8 @@ enum QuicVersion { QUIC_VERSION_UNSUPPORTED = 0, QUIC_VERSION_10 = 10, - QUIC_VERSION_11 = 11, // Current version. + QUIC_VERSION_11 = 11, + QUIC_VERSION_12 = 12, // Current version. }; // This vector contains QUIC versions which we currently support. @@ -533,6 +552,9 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo { // structure. // The set of packets which we're expecting and have not received. SequenceNumberSet missing_packets; + + // Whether the ack had to be truncated when sent. + bool is_truncated; }; // True if the sequence number is greater than largest_observed or is listed @@ -634,6 +656,7 @@ struct NET_EXPORT_PRIVATE QuicRstStreamFrame { struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame { QuicErrorCode error_code; std::string error_details; + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. QuicAckFrame ack_frame; }; diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 77494b5..6f28dc5 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -59,6 +59,10 @@ class VisitorShim : public QuicConnectionVisitorInterface { session_->OnSuccessfulVersionNegotiation(version); } + virtual void OnConfigNegotiated() OVERRIDE { + session_->OnConfigNegotiated(); + } + virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE { session_->OnConnectionClosed(error, from_peer); @@ -89,12 +93,11 @@ QuicSession::QuicSession(QuicConnection* connection, has_pending_handshake_(false) { connection_->set_visitor(visitor_shim_.get()); - connection_->SetIdleNetworkTimeout(config_.idle_connection_state_lifetime()); + connection_->SetFromConfig(config_); if (connection_->connected()) { connection_->SetOverallConnectionTimeout( config_.max_time_before_crypto_handshake()); } - // TODO(satyamshekhar): Set congestion control and ICSL also. } QuicSession::~QuicSession() { @@ -339,6 +342,10 @@ bool QuicSession::IsCryptoHandshakeConfirmed() { return GetCryptoStream()->handshake_confirmed(); } +void QuicSession::OnConfigNegotiated() { + connection_->SetFromConfig(config_); +} + void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { switch (event) { // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter @@ -356,8 +363,6 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { case HANDSHAKE_CONFIRMED: LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT << "Handshake confirmed without parameter negotiation."; - connection_->SetIdleNetworkTimeout( - config_.idle_connection_state_lifetime()); connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite()); max_open_streams_ = config_.max_streams_per_connection(); break; diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index 3867ff6..0254086 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -66,6 +66,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE; virtual void OnSuccessfulVersionNegotiation( const QuicVersion& version) OVERRIDE{} + virtual void OnConfigNegotiated() OVERRIDE; // Not needed for HTTP. virtual bool OnCanWrite() OVERRIDE; virtual bool HasPendingHandshake() const OVERRIDE; diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index e90b20f..ef1eb38 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -48,6 +48,7 @@ class TestCryptoStream : public QuicCryptoStream { const QuicErrorCode error = session()->config()->ProcessClientHello( msg, &error_details); EXPECT_EQ(QUIC_NO_ERROR, error); + session()->OnConfigNegotiated(); session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); } diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc index afba1f0..b17086b 100644 --- a/net/quic/reliable_quic_stream.cc +++ b/net/quic/reliable_quic_stream.cc @@ -502,6 +502,7 @@ uint32 ReliableQuicStream::StripPriorityAndHeaderId( data, data_len, &headers_id_and_priority_buffer_, &temporary_priority); if (total_bytes_parsed > 0 && headers_id_and_priority_buffer_.size() == 0) { priority_parsed_ = true; + // Spdy priorities are inverted, so the highest numerical value is the // lowest legal priority. if (temporary_priority > static_cast(kLowestPriority)) { diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 76de7fa..0de17f9 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -7,7 +7,7 @@ #include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/common_cert_set.h" #include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index e1d148f..dd41526 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -432,7 +432,8 @@ size_t GetPacketLengthForOneStream( version, PACKET_8BYTE_GUID, include_version, sequence_number_length, is_in_fec_group); const size_t ack_length = NullEncrypter(false).GetCiphertextSize( - QuicFramer::GetMinAckFrameSize()) + + QuicFramer::GetMinAckFrameSize( + version, sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) + GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version, sequence_number_length, is_in_fec_group); if (stream_length < ack_length) { @@ -451,11 +452,19 @@ size_t GetMinStreamFrameSize(QuicVersion version) { return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; } +TestEntropyCalculator::TestEntropyCalculator() { } + +TestEntropyCalculator::~TestEntropyCalculator() { } + QuicPacketEntropyHash TestEntropyCalculator::EntropyHash( QuicPacketSequenceNumber sequence_number) const { return 1u; } +MockEntropyCalculator::MockEntropyCalculator() { } + +MockEntropyCalculator::~MockEntropyCalculator() { } + QuicConfig DefaultQuicConfig() { QuicConfig config; config.SetDefaults(); diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 3ad8e3d..86c5caa 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -193,6 +193,7 @@ class MockConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_CONST_METHOD0(HasPendingHandshake, bool()); MOCK_METHOD1(OnSuccessfulVersionNegotiation, void(const QuicVersion& version)); + MOCK_METHOD0(OnConfigNegotiated, void()); private: DISALLOW_COPY_AND_ASSIGN(MockConnectionVisitor); @@ -348,6 +349,7 @@ class MockSendAlgorithm : public SendAlgorithmInterface { MockSendAlgorithm(); virtual ~MockSendAlgorithm(); + MOCK_METHOD2(SetFromConfig, void(const QuicConfig& config, bool is_server)); MOCK_METHOD3(OnIncomingQuicCongestionFeedbackFrame, void(const QuicCongestionFeedbackFrame&, QuicTime feedback_receive_time, @@ -376,13 +378,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface { class TestEntropyCalculator : public QuicReceivedEntropyHashCalculatorInterface { public: - TestEntropyCalculator() { } - virtual ~TestEntropyCalculator() { } + TestEntropyCalculator(); + virtual ~TestEntropyCalculator(); virtual QuicPacketEntropyHash EntropyHash( QuicPacketSequenceNumber sequence_number) const OVERRIDE; }; +class MockEntropyCalculator : public TestEntropyCalculator { + public: + MockEntropyCalculator(); + virtual ~MockEntropyCalculator(); + + MOCK_CONST_METHOD1( + EntropyHash, + QuicPacketEntropyHash(QuicPacketSequenceNumber sequence_number)); +}; + class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { public: virtual ~TestDecompressorVisitor() {} diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index b0a963f..220e4f7 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -11,6 +11,7 @@ #include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" #include "net/base/ip_endpoint.h" +#include "net/quic/congestion_control/tcp_cubic_sender.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/null_encrypter.h" #include "net/quic/quic_framer.h" @@ -145,6 +146,8 @@ class EndToEndTest : public ::testing::TestWithParam { FLAGS_track_retransmission_history = true; client_config_.SetDefaults(); server_config_.SetDefaults(); + server_config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, + 0); QuicInMemoryCachePeer::ResetForTests(); AddToCache("GET", kLargeRequest, "HTTP/1.1", "200", "OK", kFooResponseBody); @@ -536,6 +539,7 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) { // The 0-RTT handshake should succeed. client_->Connect(); + client_->WaitForResponseForMs(-1); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); EXPECT_EQ(1, client_->client()->session()->GetNumSentClientHellos()); @@ -677,6 +681,106 @@ TEST_P(EndToEndTest, LimitMaxOpenStreams) { EXPECT_EQ(2u, client_negotiated_config->max_streams_per_connection()); } +TEST_P(EndToEndTest, LimitMaxPacketSizeAndCongestionWindowAndRTT) { + // Client tries to negotiate twice the server's max and negotiation settles + // on the max. + client_config_.set_server_max_packet_size(2 * kMaxPacketSize, + kDefaultMaxPacketSize); + client_config_.set_server_initial_congestion_window(2 * kMaxInitialWindow, + kDefaultInitialWindow); + client_config_.set_initial_round_trip_time_us(1, 1); + + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + while (!session->IsCryptoHandshakeConfirmed()) { + server_thread_->server()->WaitForEvents(); + } + + QuicConfig* client_negotiated_config = client_->client()->session()->config(); + QuicConfig* server_negotiated_config = session->config(); + QuicCongestionManager* client_congestion_manager = + QuicConnectionPeer::GetCongestionManager( + client_->client()->session()->connection()); + QuicCongestionManager* server_congestion_manager = + QuicConnectionPeer::GetCongestionManager(session->connection()); + + EXPECT_EQ(kMaxPacketSize, client_negotiated_config->server_max_packet_size()); + if (negotiated_version_ > QUIC_VERSION_11) { + EXPECT_EQ(kMaxPacketSize, client_->client()->options()->max_packet_length); + EXPECT_EQ(kMaxPacketSize, session->options()->max_packet_length); + } else { + EXPECT_EQ(kDefaultMaxPacketSize, + client_->client()->options()->max_packet_length); + EXPECT_EQ(kDefaultMaxPacketSize, + session->options()->max_packet_length); + } + + EXPECT_EQ(kMaxInitialWindow, + client_negotiated_config->server_initial_congestion_window()); + EXPECT_EQ(kMaxInitialWindow, + server_negotiated_config->server_initial_congestion_window()); + // The client shouldn't set it's initial window based on the negotiated value. + EXPECT_EQ(kDefaultInitialWindow * kDefaultTCPMSS, + client_congestion_manager->GetCongestionWindow()); + EXPECT_EQ(kMaxInitialWindow * kDefaultTCPMSS, + server_congestion_manager->GetCongestionWindow()); + + EXPECT_EQ(1u, client_negotiated_config->initial_round_trip_time_us()); + EXPECT_EQ(1u, server_negotiated_config->initial_round_trip_time_us()); + + // Now use the negotiated limits with packet loss. + SetPacketLossPercentage(30); + + // 10 Kb body. + string body; + GenerateBody(&body, 1024 * 10); + + HTTPMessage request(HttpConstants::HTTP_1_1, + HttpConstants::POST, "/foo"); + request.AddBody(body, true); + + EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); +} + +TEST_P(EndToEndTest, InitialRTT) { + // Client tries to negotiate twice the server's max and negotiation settles + // on the max. + client_config_.set_initial_round_trip_time_us(2 * kMaxInitialRoundTripTimeUs, + 0); + + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + while (!session->IsCryptoHandshakeConfirmed()) { + server_thread_->server()->WaitForEvents(); + } + + QuicConfig* client_negotiated_config = client_->client()->session()->config(); + QuicConfig* server_negotiated_config = session->config(); + QuicCongestionManager* client_congestion_manager = + QuicConnectionPeer::GetCongestionManager( + client_->client()->session()->connection()); + QuicCongestionManager* server_congestion_manager = + QuicConnectionPeer::GetCongestionManager(session->connection()); + + EXPECT_EQ(kMaxInitialRoundTripTimeUs, + client_negotiated_config->initial_round_trip_time_us()); + EXPECT_EQ(kMaxInitialRoundTripTimeUs, + server_negotiated_config->initial_round_trip_time_us()); + // Now that acks have been exchanged, the RTT estimate has decreased on the + // server and is not infinite on the client. + EXPECT_FALSE(client_congestion_manager->SmoothedRtt().IsInfinite()); + EXPECT_GE(kMaxInitialRoundTripTimeUs, + server_congestion_manager->SmoothedRtt().ToMicroseconds()); +} + TEST_P(EndToEndTest, ResetConnection) { ASSERT_TRUE(Initialize()); diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index b1fa935..50e2a7c 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc @@ -45,6 +45,12 @@ QuicClient::QuicClient(IPEndPoint server_address, supported_versions_(supported_versions), print_response_(print_response) { config_.SetDefaults(); + // TODO(ianswett): Allow the client to change the server's max packet size and + // initial congestion window. + config_.set_server_max_packet_size(kDefaultMaxPacketSize, + kDefaultMaxPacketSize); + config_.set_server_initial_congestion_window(kDefaultInitialWindow, + kDefaultInitialWindow); } QuicClient::QuicClient(IPEndPoint server_address, diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc index 90b3074..f15b18d 100644 --- a/net/tools/quic/quic_dispatcher_test.cc +++ b/net/tools/quic/quic_dispatcher_test.cc @@ -8,7 +8,7 @@ #include "base/strings/string_piece.h" #include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_crypto_stream.h" #include "net/quic/test_tools/quic_test_utils.h" diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc index 66a9aa9..0d87d1c 100644 --- a/net/tools/quic/quic_server.cc +++ b/net/tools/quic/quic_server.cc @@ -43,6 +43,7 @@ QuicServer::QuicServer() supported_versions_(QuicSupportedVersions()) { // Use hardcoded crypto parameters for now. config_.SetDefaults(); + config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0); Initialize(); } diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h index f166555..7c8e405 100644 --- a/net/tools/quic/quic_server.h +++ b/net/tools/quic/quic_server.h @@ -10,7 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_framer.h" #include "net/tools/epoll_server/epoll_server.h" diff --git a/net/tools/quic/quic_server_session_test.cc b/net/tools/quic/quic_server_session_test.cc index 1412f38..08c25ca 100644 --- a/net/tools/quic/quic_server_session_test.cc +++ b/net/tools/quic/quic_server_session_test.cc @@ -5,7 +5,7 @@ #include "net/tools/quic/quic_server_session.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_connection.h" #include "net/quic/test_tools/quic_connection_peer.h" diff --git a/net/tools/quic/test_tools/mock_quic_dispatcher.h b/net/tools/quic/test_tools/mock_quic_dispatcher.h index ac198bc..505025c 100644 --- a/net/tools/quic/test_tools/mock_quic_dispatcher.h +++ b/net/tools/quic/test_tools/mock_quic_dispatcher.h @@ -6,7 +6,7 @@ #define NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/tools/epoll_server/epoll_server.h" -- cgit v1.1