Reduce CPU impact of QUIC stateless reset packet generation

In b/180640485 we saw that QUIC stateless reset packet generation is causing noticeable CPU impact on our servers.

The randomness in IETF QUIC stateless reset packets only exists to prevent middleboxes 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. This CL introduces the notion of insecure randomness, which uses the non-secure and fast PRNG xoshiro256++. In a release build, this function was empirically shown to perform 150x faster than its secure counterpart.

Protected by FLAGS_quic_reloadable_flag_quic_stateless_reset_faster_randomness.

PiperOrigin-RevId: 358886449
Change-Id: I31ba3e19f802f3012a497ac7220430ba0d93fb9d
diff --git a/quic/core/crypto/quic_random.cc b/quic/core/crypto/quic_random.cc
index d35f526..8292e9b 100644
--- a/quic/core/crypto/quic_random.cc
+++ b/quic/core/crypto/quic_random.cc
@@ -3,9 +3,12 @@
 // found in the LICENSE file.
 
 #include "quic/core/crypto/quic_random.h"
+#include <cstdint>
+#include <cstring>
 
 #include "third_party/boringssl/src/include/openssl/rand.h"
 #include "quic/platform/api/quic_bug_tracker.h"
+#include "common/platform/api/quiche_logging.h"
 
 namespace quic {
 
@@ -13,7 +16,7 @@
 
 class DefaultRandom : public QuicRandom {
  public:
-  DefaultRandom() {}
+  DefaultRandom();
   DefaultRandom(const DefaultRandom&) = delete;
   DefaultRandom& operator=(const DefaultRandom&) = delete;
   ~DefaultRandom() override {}
@@ -21,8 +24,15 @@
   // QuicRandom implementation
   void RandBytes(void* data, size_t len) override;
   uint64_t RandUint64() override;
+  void InsecureRandBytes(void* data, size_t len) override;
+  uint64_t InsecureRandUint64() override;
+
+ private:
+  QuicInsecureRandom insecure_random_;
 };
 
+DefaultRandom::DefaultRandom() : insecure_random_(this) {}
+
 void DefaultRandom::RandBytes(void* data, size_t len) {
   RAND_bytes(reinterpret_cast<uint8_t*>(data), len);
 }
@@ -33,6 +43,14 @@
   return value;
 }
 
+void DefaultRandom::InsecureRandBytes(void* data, size_t len) {
+  insecure_random_.InsecureRandBytes(data, len);
+}
+
+uint64_t DefaultRandom::InsecureRandUint64() {
+  return insecure_random_.InsecureRandUint64();
+}
+
 }  // namespace
 
 // static
@@ -41,4 +59,44 @@
   return random;
 }
 
+// QuicInsecureRandom uses an implementation of xoshiro256++ 1.0 based on code
+// in the public domain from <http://prng.di.unimi.it/xoshiro256plusplus.c>.
+
+namespace {
+inline uint64_t Xoshiro256PlusPlusRotl(uint64_t x, int k) {
+  return (x << k) | (x >> (64 - k));
+}
+}  // namespace
+
+QuicInsecureRandom::QuicInsecureRandom(QuicRandom* random) {
+  random->RandBytes(&rng_state_, sizeof(rng_state_));
+}
+
+void QuicInsecureRandom::InsecureRandBytes(void* data, size_t len) {
+  while (len >= sizeof(uint64_t)) {
+    uint64_t random_bytes64 = InsecureRandUint64();
+    memcpy(data, &random_bytes64, sizeof(uint64_t));
+    data = reinterpret_cast<char*>(data) + sizeof(uint64_t);
+    len -= sizeof(uint64_t);
+  }
+  if (len > 0) {
+    QUICHE_DCHECK_LT(len, sizeof(uint64_t));
+    uint64_t random_bytes64 = InsecureRandUint64();
+    memcpy(data, &random_bytes64, len);
+  }
+}
+
+uint64_t QuicInsecureRandom::InsecureRandUint64() {
+  const uint64_t result =
+      Xoshiro256PlusPlusRotl(rng_state_[0] + rng_state_[3], 23) + rng_state_[0];
+  const uint64_t t = rng_state_[1] << 17;
+  rng_state_[2] ^= rng_state_[0];
+  rng_state_[3] ^= rng_state_[1];
+  rng_state_[1] ^= rng_state_[2];
+  rng_state_[0] ^= rng_state_[3];
+  rng_state_[2] ^= t;
+  rng_state_[3] = Xoshiro256PlusPlusRotl(rng_state_[3], 45);
+  return result;
+}
+
 }  // namespace quic
diff --git a/quic/core/crypto/quic_random.h b/quic/core/crypto/quic_random.h
index 7fa4c93..ff1c4bb 100644
--- a/quic/core/crypto/quic_random.h
+++ b/quic/core/crypto/quic_random.h
@@ -26,6 +26,35 @@
 
   // Returns a random number in the range [0, kuint64max].
   virtual uint64_t RandUint64() = 0;
+
+  // Generates |len| random bytes in the |data| buffer. This MUST NOT be used
+  // for any application that requires cryptographically-secure randomness.
+  virtual void InsecureRandBytes(void* data, size_t len) = 0;
+
+  // Returns a random number in the range [0, kuint64max]. This MUST NOT be used
+  // for any application that requires cryptographically-secure randomness.
+  virtual uint64_t InsecureRandUint64() = 0;
+};
+
+// A class that generates non-cryptographically-secure random numbers. It uses
+// a QuicRandom instance to seed its initial randomness. This MUST NOT be used
+// for any application that requires cryptographically-secure randomness.
+class QUIC_EXPORT_PRIVATE QuicInsecureRandom {
+ public:
+  // |random| is only used during construction of the QuicInsecureRandom to seed
+  // its inital state.
+  explicit QuicInsecureRandom(QuicRandom* random);
+
+  // Generates |len| random bytes in the |data| buffer. This MUST NOT be used
+  // for any application that requires cryptographically-secure randomness.
+  void InsecureRandBytes(void* data, size_t len);
+
+  // Returns a random number in the range [0, kuint64max]. This MUST NOT be used
+  // for any application that requires cryptographically-secure randomness.
+  uint64_t InsecureRandUint64();
+
+ private:
+  uint64_t rng_state_[4];
 };
 
 }  // namespace quic
diff --git a/quic/core/crypto/quic_random_test.cc b/quic/core/crypto/quic_random_test.cc
index a583c27..ab27918 100644
--- a/quic/core/crypto/quic_random_test.cc
+++ b/quic/core/crypto/quic_random_test.cc
@@ -30,5 +30,24 @@
   EXPECT_NE(value1, value2);
 }
 
+TEST_F(QuicRandomTest, InsecureRandBytes) {
+  unsigned char buf1[16];
+  unsigned char buf2[16];
+  memset(buf1, 0xaf, sizeof(buf1));
+  memset(buf2, 0xaf, sizeof(buf2));
+  ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1)));
+
+  QuicRandom* rng = QuicRandom::GetInstance();
+  rng->InsecureRandBytes(buf1, sizeof(buf1));
+  EXPECT_NE(0, memcmp(buf1, buf2, sizeof(buf1)));
+}
+
+TEST_F(QuicRandomTest, InsecureRandUint64) {
+  QuicRandom* rng = QuicRandom::GetInstance();
+  uint64_t value1 = rng->InsecureRandUint64();
+  uint64_t value2 = rng->InsecureRandUint64();
+  EXPECT_NE(value1, value2);
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_data_writer.cc b/quic/core/quic_data_writer.cc
index 1753bbe..4ed9b45 100644
--- a/quic/core/quic_data_writer.cc
+++ b/quic/core/quic_data_writer.cc
@@ -91,6 +91,17 @@
   return true;
 }
 
+bool QuicDataWriter::WriteInsecureRandomBytes(QuicRandom* random,
+                                              size_t length) {
+  char* dest = BeginWrite(length);
+  if (!dest) {
+    return false;
+  }
+
+  random->InsecureRandBytes(dest, length);
+  IncreaseLength(length);
+  return true;
+}
 
 // Converts a uint64_t into an IETF/Quic formatted Variable Length
 // Integer. IETF Variable Length Integers have 62 significant bits, so
diff --git a/quic/core/quic_data_writer.h b/quic/core/quic_data_writer.h
index 64fb2f5..2a23ce2 100644
--- a/quic/core/quic_data_writer.h
+++ b/quic/core/quic_data_writer.h
@@ -89,6 +89,10 @@
 
   // Write |length| random bytes generated by |random|.
   bool WriteRandomBytes(QuicRandom* random, size_t length);
+
+  // Write |length| random bytes generated by |random|. This MUST NOT be used
+  // for any application that requires cryptographically-secure randomness.
+  bool WriteInsecureRandomBytes(QuicRandom* random, size_t length);
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_data_writer_test.cc b/quic/core/quic_data_writer_test.cc
index b355105..cbe0351 100644
--- a/quic/core/quic_data_writer_test.cc
+++ b/quic/core/quic_data_writer_test.cc
@@ -1131,6 +1131,21 @@
                                               20);
 }
 
+TEST_P(QuicDataWriterTest, WriteInsecureRandomBytes) {
+  char buffer[20];
+  char expected[20];
+  for (size_t i = 0; i < 20; ++i) {
+    expected[i] = 'r';
+  }
+  MockRandom random;
+  QuicDataWriter writer(20, buffer, GetParam().endianness);
+  EXPECT_FALSE(writer.WriteInsecureRandomBytes(&random, 30));
+
+  EXPECT_TRUE(writer.WriteInsecureRandomBytes(&random, 20));
+  quiche::test::CompareCharArraysWithHexError("random", buffer, 20, expected,
+                                              20);
+}
+
 TEST_P(QuicDataWriterTest, PeekVarInt62Length) {
   // In range [0, 63], variable length should be 1 byte.
   char buffer[20];
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 1ccb794..9a88972 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -56,6 +56,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_start_peer_migration_earlier, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_stateless_reset_faster_randomness, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_use_normalized_sni_for_cert_selectioon, false)
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 60f069c..432bd13 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -1287,10 +1287,22 @@
   if (!writer.WriteUInt8(type)) {
     return nullptr;
   }
-  // Append random bytes.
-  if (!writer.WriteRandomBytes(QuicRandom::GetInstance(),
-                               kMinRandomBytesLengthInStatelessReset)) {
-    return nullptr;
+
+  // Append random bytes. This randomness only exists to prevent middleboxes
+  // 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 here.
+  if (GetQuicReloadableFlag(quic_stateless_reset_faster_randomness)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_stateless_reset_faster_randomness);
+    if (!writer.WriteInsecureRandomBytes(
+            QuicRandom::GetInstance(), kMinRandomBytesLengthInStatelessReset)) {
+      return nullptr;
+    }
+  } else {
+    if (!writer.WriteRandomBytes(QuicRandom::GetInstance(),
+                                 kMinRandomBytesLengthInStatelessReset)) {
+      return nullptr;
+    }
   }
 
   // Append stateless reset token.
diff --git a/quic/test_tools/mock_random.cc b/quic/test_tools/mock_random.cc
index 3a4b561..c50c75c 100644
--- a/quic/test_tools/mock_random.cc
+++ b/quic/test_tools/mock_random.cc
@@ -21,6 +21,14 @@
   return base_ + increment_;
 }
 
+void MockRandom::InsecureRandBytes(void* data, size_t len) {
+  RandBytes(data, len);
+}
+
+uint64_t MockRandom::InsecureRandUint64() {
+  return RandUint64();
+}
+
 void MockRandom::ChangeValue() {
   increment_++;
 }
diff --git a/quic/test_tools/mock_random.h b/quic/test_tools/mock_random.h
index e03e6a7..63d438d 100644
--- a/quic/test_tools/mock_random.h
+++ b/quic/test_tools/mock_random.h
@@ -24,6 +24,11 @@
   // Returns base + the current increment.
   uint64_t RandUint64() override;
 
+  // InsecureRandBytes behaves equivalently to RandBytes.
+  void InsecureRandBytes(void* data, size_t len) override;
+  // InsecureRandUint64 behaves equivalently to RandUint64.
+  uint64_t InsecureRandUint64() override;
+
   // ChangeValue increments |increment_|. This causes the value returned by
   // |RandUint64| and the byte that |RandBytes| fills with, to change.
   void ChangeValue();
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc
index fb5d772..035680c 100644
--- a/quic/test_tools/quic_test_utils.cc
+++ b/quic/test_tools/quic_test_utils.cc
@@ -241,6 +241,14 @@
   }
 }
 
+void SimpleRandom::InsecureRandBytes(void* data, size_t len) {
+  RandBytes(data, len);
+}
+
+uint64_t SimpleRandom::InsecureRandUint64() {
+  return RandUint64();
+}
+
 void SimpleRandom::FillBuffer() {
   uint8_t nonce[12];
   memcpy(nonce, buffer_, sizeof(nonce));
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 2863ce8..fe255b4 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -267,10 +267,15 @@
   SimpleRandom& operator=(const SimpleRandom&) = delete;
   ~SimpleRandom() override {}
 
+  // Generates |len| random bytes in the |data| buffer.
+  void RandBytes(void* data, size_t len) override;
   // Returns a random number in the range [0, kuint64max].
   uint64_t RandUint64() override;
 
-  void RandBytes(void* data, size_t len) override;
+  // InsecureRandBytes behaves equivalently to RandBytes.
+  void InsecureRandBytes(void* data, size_t len) override;
+  // InsecureRandUint64 behaves equivalently to RandUint64.
+  uint64_t InsecureRandUint64() override;
 
   void set_seed(uint64_t seed);