// 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 #include #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, 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(packet_size_)) { ReCreateChaosProtector(); } void ReCreateChaosProtector() { chaos_protector_ = std::make_unique( 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(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 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 packet_buffer_; std::unique_ptr 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