Allow caller of QuicFramer::BuildIetfStatelessResetPacket to provide a random bytes generator. PiperOrigin-RevId: 473306804
diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc index 99d9daa..9f81d06 100644 --- a/quiche/quic/core/quic_framer.cc +++ b/quiche/quic/core/quic_framer.cc
@@ -1297,8 +1297,17 @@ // static std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( - QuicConnectionId /*connection_id*/, size_t received_packet_length, + QuicConnectionId connection_id, size_t received_packet_length, StatelessResetToken stateless_reset_token) { + return BuildIetfStatelessResetPacket(connection_id, received_packet_length, + stateless_reset_token, + QuicRandom::GetInstance()); +} + +// static +std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( + QuicConnectionId /*connection_id*/, size_t received_packet_length, + StatelessResetToken stateless_reset_token, QuicRandom* random) { QUIC_DVLOG(1) << "Building IETF stateless reset packet."; if (received_packet_length <= GetMinStatelessResetPacketLength()) { QUICHE_DLOG(ERROR) @@ -1318,10 +1327,10 @@ // from comparing the entire packet to a known value. Therefore it has no // cryptographic use, and does not need a secure cryptographic pseudo-random // number generator. It's therefore safe to use WriteInsecureRandomBytes. - if (!writer.WriteInsecureRandomBytes(QuicRandom::GetInstance(), - len - kStatelessResetTokenLength)) { + const size_t random_bytes_size = len - kStatelessResetTokenLength; + if (!writer.WriteInsecureRandomBytes(random, random_bytes_size)) { QUIC_BUG(362045737_2) << "Failed to append random bytes of length: " - << len - kStatelessResetTokenLength; + << random_bytes_size; return nullptr; } // Change first 2 fixed bits to 01.
diff --git a/quiche/quic/core/quic_framer.h b/quiche/quic/core/quic_framer.h index fbf9fdb..694e741 100644 --- a/quiche/quic/core/quic_framer.h +++ b/quiche/quic/core/quic_framer.h
@@ -476,6 +476,13 @@ QuicConnectionId connection_id, size_t received_packet_length, StatelessResetToken stateless_reset_token); + // Returns a new IETF stateless reset packet with random bytes generated from + // |random|->InsecureRandBytes(). NOTE: the first two bits of the random bytes + // will be modified to 01b to make it look like a short header packet. + static std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket( + QuicConnectionId connection_id, size_t received_packet_length, + StatelessResetToken stateless_reset_token, QuicRandom* random); + // Returns a new version negotiation packet. static std::unique_ptr<QuicEncryptedPacket> BuildVersionNegotiationPacket( QuicConnectionId server_connection_id,
diff --git a/quiche/quic/core/quic_framer_test.cc b/quiche/quic/core/quic_framer_test.cc index 8d1bc41..f4fa8a8 100644 --- a/quiche/quic/core/quic_framer_test.cc +++ b/quiche/quic/core/quic_framer_test.cc
@@ -6,6 +6,7 @@ #include <algorithm> #include <cstdint> +#include <cstring> #include <map> #include <memory> #include <string> @@ -10373,6 +10374,43 @@ data3->length()); } +TEST_P(QuicFramerTest, BuildIetfStatelessResetPacketCallerProvidedRandomBytes) { + // clang-format off + unsigned char packet[] = { + // 1st byte 01XX XXXX + 0x7c, + // Random bytes + 0x7c, 0x7c, 0x7c, 0x7c, + // stateless reset token + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f + }; + // clang-format on + + // Build the minimal stateless reset packet with caller-provided random bytes. + MockRandom random; + auto generate_random_bytes = [](void* data, size_t len) { + std::string bytes(len, 0x7c); + memcpy(data, bytes.data(), bytes.size()); + }; + EXPECT_CALL(random, InsecureRandBytes(_, _)) + .WillOnce(testing::Invoke(generate_random_bytes)); + std::unique_ptr<QuicEncryptedPacket> data( + framer_.BuildIetfStatelessResetPacket( + FramerTestConnectionId(), + QuicFramer::GetMinStatelessResetPacketLength() + 1, + kTestStatelessResetToken, &random)); + ASSERT_TRUE(data); + EXPECT_EQ(QuicFramer::GetMinStatelessResetPacketLength(), data->length()); + // Verify the first 2 bits are 01. + EXPECT_FALSE(data->data()[0] & FLAGS_LONG_HEADER); + EXPECT_TRUE(data->data()[0] & FLAGS_FIXED_BIT); + // Verify the entire packet. + quiche::test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(packet), + ABSL_ARRAYSIZE(packet)); +} + TEST_P(QuicFramerTest, EncryptPacket) { QuicPacketNumber packet_number = kPacketNumber; // clang-format off
diff --git a/quiche/quic/test_tools/mock_random.cc b/quiche/quic/test_tools/mock_random.cc index 2bea7c6..2c45e65 100644 --- a/quiche/quic/test_tools/mock_random.cc +++ b/quiche/quic/test_tools/mock_random.cc
@@ -9,21 +9,33 @@ namespace quic { namespace test { -MockRandom::MockRandom() : base_(0xDEADBEEF), increment_(0) {} +using testing::_; +using testing::Invoke; -MockRandom::MockRandom(uint32_t base) : base_(base), increment_(0) {} +MockRandom::MockRandom() : MockRandom(0xDEADBEEF) {} -void MockRandom::RandBytes(void* data, size_t len) { +MockRandom::MockRandom(uint32_t base) : base_(base), increment_(0) { + ON_CALL(*this, RandBytes(_, _)) + .WillByDefault(Invoke(this, &MockRandom::DefaultRandBytes)); + ON_CALL(*this, RandUint64()) + .WillByDefault(Invoke(this, &MockRandom::DefaultRandUint64)); + ON_CALL(*this, InsecureRandBytes(_, _)) + .WillByDefault(Invoke(this, &MockRandom::DefaultInsecureRandBytes)); + ON_CALL(*this, InsecureRandUint64()) + .WillByDefault(Invoke(this, &MockRandom::DefaultInsecureRandUint64)); +} + +void MockRandom::DefaultRandBytes(void* data, size_t len) { memset(data, increment_ + static_cast<uint8_t>('r'), len); } -uint64_t MockRandom::RandUint64() { return base_ + increment_; } +uint64_t MockRandom::DefaultRandUint64() { return base_ + increment_; } -void MockRandom::InsecureRandBytes(void* data, size_t len) { - RandBytes(data, len); +void MockRandom::DefaultInsecureRandBytes(void* data, size_t len) { + DefaultRandBytes(data, len); } -uint64_t MockRandom::InsecureRandUint64() { return RandUint64(); } +uint64_t MockRandom::DefaultInsecureRandUint64() { return DefaultRandUint64(); } void MockRandom::ChangeValue() { increment_++; }
diff --git a/quiche/quic/test_tools/mock_random.h b/quiche/quic/test_tools/mock_random.h index 03c1f00..0a4918d 100644 --- a/quiche/quic/test_tools/mock_random.h +++ b/quiche/quic/test_tools/mock_random.h
@@ -6,6 +6,7 @@ #define QUICHE_QUIC_TEST_TOOLS_MOCK_RANDOM_H_ #include "quiche/quic/core/crypto/quic_random.h" +#include "quiche/quic/platform/api/quic_test.h" namespace quic { namespace test { @@ -18,22 +19,31 @@ MockRandom(const MockRandom&) = delete; MockRandom& operator=(const MockRandom&) = delete; - // QuicRandom: + MOCK_METHOD(void, RandBytes, (void* data, size_t len), (override)); + MOCK_METHOD(uint64_t, RandUint64, (), (override)); + MOCK_METHOD(void, InsecureRandBytes, (void* data, size_t len), (override)); + MOCK_METHOD(uint64_t, InsecureRandUint64, (), (override)); + + // Default QuicRandom implementations. They are used if the caller does not + // setup the MockRandom via EXPECT_CALLs. + // Fills the |data| buffer with a repeating byte, initially 'r'. - void RandBytes(void* data, size_t len) override; + void DefaultRandBytes(void* data, size_t len); // Returns base + the current increment. - uint64_t RandUint64() override; + uint64_t DefaultRandUint64(); // InsecureRandBytes behaves equivalently to RandBytes. - void InsecureRandBytes(void* data, size_t len) override; + void DefaultInsecureRandBytes(void* data, size_t len); // InsecureRandUint64 behaves equivalently to RandUint64. - uint64_t InsecureRandUint64() override; + uint64_t DefaultInsecureRandUint64(); // ChangeValue increments |increment_|. This causes the value returned by // |RandUint64| and the byte that |RandBytes| fills with, to change. + // Used by the Default implementations. void ChangeValue(); // Sets the base to |base| and resets increment to zero. + // Used by the Default implementations. void ResetBase(uint32_t base); private: