| // Copyright (c) 2021 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 "quiche/quic/core/quic_chaos_protector.h" |
| |
| #include <cstddef> |
| #include <memory> |
| |
| #include "absl/strings/string_view.h" |
| #include "quiche/quic/core/frames/quic_crypto_frame.h" |
| #include "quiche/quic/core/quic_connection_id.h" |
| #include "quiche/quic/core/quic_framer.h" |
| #include "quiche/quic/core/quic_packet_number.h" |
| #include "quiche/quic/core/quic_packets.h" |
| #include "quiche/quic/core/quic_stream_frame_data_producer.h" |
| #include "quiche/quic/core/quic_time.h" |
| #include "quiche/quic/core/quic_types.h" |
| #include "quiche/quic/core/quic_versions.h" |
| #include "quiche/quic/platform/api/quic_test.h" |
| #include "quiche/quic/test_tools/mock_random.h" |
| #include "quiche/quic/test_tools/quic_test_utils.h" |
| #include "quiche/quic/test_tools/simple_quic_framer.h" |
| |
| namespace quic { |
| namespace test { |
| |
| class QuicChaosProtectorTest : public QuicTestWithParam<ParsedQuicVersion>, |
| public QuicStreamFrameDataProducer { |
| public: |
| QuicChaosProtectorTest() |
| : version_(GetParam()), |
| framer_({version_}, QuicTime::Zero(), Perspective::IS_CLIENT, |
| kQuicDefaultConnectionIdLength), |
| validation_framer_({version_}), |
| random_(/*base=*/3), |
| level_(ENCRYPTION_INITIAL), |
| crypto_offset_(0), |
| crypto_data_length_(100), |
| crypto_frame_(level_, crypto_offset_, crypto_data_length_), |
| num_padding_bytes_(50), |
| packet_size_(1000), |
| packet_buffer_(std::make_unique<char[]>(packet_size_)) { |
| ReCreateChaosProtector(); |
| } |
| |
| void ReCreateChaosProtector() { |
| chaos_protector_ = std::make_unique<QuicChaosProtector>( |
| crypto_frame_, num_padding_bytes_, packet_size_, |
| SetupHeaderAndFramers(), &random_); |
| } |
| |
| // From QuicStreamFrameDataProducer. |
| WriteStreamDataResult WriteStreamData(QuicStreamId /*id*/, |
| QuicStreamOffset /*offset*/, |
| QuicByteCount /*data_length*/, |
| QuicDataWriter* /*writer*/) override { |
| ADD_FAILURE() << "This should never be called"; |
| return STREAM_MISSING; |
| } |
| |
| // From QuicStreamFrameDataProducer. |
| bool WriteCryptoData(EncryptionLevel level, QuicStreamOffset offset, |
| QuicByteCount data_length, |
| QuicDataWriter* writer) override { |
| EXPECT_EQ(level, level); |
| EXPECT_EQ(offset, crypto_offset_); |
| EXPECT_EQ(data_length, crypto_data_length_); |
| for (QuicByteCount i = 0; i < data_length; i++) { |
| EXPECT_TRUE(writer->WriteUInt8(static_cast<uint8_t>(i & 0xFF))); |
| } |
| return true; |
| } |
| |
| protected: |
| QuicFramer* SetupHeaderAndFramers() { |
| // Setup header. |
| header_.destination_connection_id = TestConnectionId(); |
| header_.destination_connection_id_included = CONNECTION_ID_PRESENT; |
| header_.source_connection_id = EmptyQuicConnectionId(); |
| header_.source_connection_id_included = CONNECTION_ID_PRESENT; |
| header_.reset_flag = false; |
| header_.version_flag = true; |
| header_.has_possible_stateless_reset_token = false; |
| header_.packet_number_length = PACKET_4BYTE_PACKET_NUMBER; |
| header_.version = version_; |
| header_.packet_number = QuicPacketNumber(1); |
| header_.form = IETF_QUIC_LONG_HEADER_PACKET; |
| header_.long_packet_type = INITIAL; |
| header_.retry_token_length_length = |
| quiche::VARIABLE_LENGTH_INTEGER_LENGTH_1; |
| header_.length_length = quiche::kQuicheDefaultLongHeaderLengthLength; |
| // Setup validation framer. |
| validation_framer_.framer()->SetInitialObfuscators( |
| header_.destination_connection_id); |
| // Setup framer. |
| framer_.SetInitialObfuscators(header_.destination_connection_id); |
| framer_.set_data_producer(this); |
| return &framer_; |
| } |
| |
| void BuildEncryptAndParse() { |
| absl::optional<size_t> length = |
| chaos_protector_->BuildDataPacket(header_, packet_buffer_.get()); |
| ASSERT_TRUE(length.has_value()); |
| ASSERT_GT(length.value(), 0u); |
| size_t encrypted_length = framer_.EncryptInPlace( |
| level_, header_.packet_number, |
| GetStartOfEncryptedData(framer_.transport_version(), header_), |
| length.value(), packet_size_, packet_buffer_.get()); |
| ASSERT_GT(encrypted_length, 0u); |
| ASSERT_TRUE(validation_framer_.ProcessPacket(QuicEncryptedPacket( |
| absl::string_view(packet_buffer_.get(), encrypted_length)))); |
| } |
| |
| void ResetOffset(QuicStreamOffset offset) { |
| crypto_offset_ = offset; |
| crypto_frame_.offset = offset; |
| ReCreateChaosProtector(); |
| } |
| |
| void ResetLength(QuicByteCount length) { |
| crypto_data_length_ = length; |
| crypto_frame_.data_length = length; |
| ReCreateChaosProtector(); |
| } |
| |
| ParsedQuicVersion version_; |
| QuicPacketHeader header_; |
| QuicFramer framer_; |
| SimpleQuicFramer validation_framer_; |
| MockRandom random_; |
| EncryptionLevel level_; |
| QuicStreamOffset crypto_offset_; |
| QuicByteCount crypto_data_length_; |
| QuicCryptoFrame crypto_frame_; |
| int num_padding_bytes_; |
| size_t packet_size_; |
| std::unique_ptr<char[]> packet_buffer_; |
| std::unique_ptr<QuicChaosProtector> chaos_protector_; |
| }; |
| |
| namespace { |
| |
| ParsedQuicVersionVector TestVersions() { |
| ParsedQuicVersionVector versions; |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { |
| if (version.UsesCryptoFrames()) { |
| versions.push_back(version); |
| } |
| } |
| return versions; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(QuicChaosProtectorTests, QuicChaosProtectorTest, |
| ::testing::ValuesIn(TestVersions()), |
| ::testing::PrintToStringParamName()); |
| |
| TEST_P(QuicChaosProtectorTest, Main) { |
| BuildEncryptAndParse(); |
| ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, 0u); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, 1u); |
| ASSERT_EQ(validation_framer_.ping_frames().size(), 3u); |
| ASSERT_EQ(validation_framer_.padding_frames().size(), 7u); |
| EXPECT_EQ(validation_framer_.padding_frames()[0].num_padding_bytes, 3); |
| } |
| |
| TEST_P(QuicChaosProtectorTest, DifferentRandom) { |
| random_.ResetBase(4); |
| BuildEncryptAndParse(); |
| ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); |
| ASSERT_EQ(validation_framer_.ping_frames().size(), 4u); |
| ASSERT_EQ(validation_framer_.padding_frames().size(), 8u); |
| } |
| |
| TEST_P(QuicChaosProtectorTest, RandomnessZero) { |
| random_.ResetBase(0); |
| BuildEncryptAndParse(); |
| ASSERT_EQ(validation_framer_.crypto_frames().size(), 1u); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, |
| crypto_data_length_); |
| ASSERT_EQ(validation_framer_.ping_frames().size(), 0u); |
| ASSERT_EQ(validation_framer_.padding_frames().size(), 1u); |
| } |
| |
| TEST_P(QuicChaosProtectorTest, Offset) { |
| ResetOffset(123); |
| BuildEncryptAndParse(); |
| ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, 1u); |
| ASSERT_EQ(validation_framer_.ping_frames().size(), 3u); |
| ASSERT_EQ(validation_framer_.padding_frames().size(), 7u); |
| EXPECT_EQ(validation_framer_.padding_frames()[0].num_padding_bytes, 3); |
| } |
| |
| TEST_P(QuicChaosProtectorTest, OffsetAndRandomnessZero) { |
| ResetOffset(123); |
| random_.ResetBase(0); |
| BuildEncryptAndParse(); |
| ASSERT_EQ(validation_framer_.crypto_frames().size(), 1u); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, |
| crypto_data_length_); |
| ASSERT_EQ(validation_framer_.ping_frames().size(), 0u); |
| ASSERT_EQ(validation_framer_.padding_frames().size(), 1u); |
| } |
| |
| TEST_P(QuicChaosProtectorTest, ZeroRemainingBytesAfterSplit) { |
| QuicPacketLength new_length = 63; |
| num_padding_bytes_ = QuicFramer::GetMinCryptoFrameSize( |
| crypto_frame_.offset + new_length, new_length); |
| ResetLength(new_length); |
| BuildEncryptAndParse(); |
| |
| ASSERT_EQ(validation_framer_.crypto_frames().size(), 2u); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); |
| EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, 4); |
| EXPECT_EQ(validation_framer_.crypto_frames()[1]->offset, crypto_offset_ + 4); |
| EXPECT_EQ(validation_framer_.crypto_frames()[1]->data_length, |
| crypto_data_length_ - 4); |
| ASSERT_EQ(validation_framer_.ping_frames().size(), 0u); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace quic |