Introduce Chaos Protection
This feature reduces the likelihood of QUIC ossification due to middleboxes. When active, chaos protection modifies client initial packets in a way that defeats naive parsers but makes no difference to real QUIC implementations. This will hopefully encourage middlebox vendors to use robust third party QUIC libraries instead of looking for fields at fixed offsets.
PiperOrigin-RevId: 370906208
Change-Id: I36bba5dc2016f06cf9686cd77cb425a760ed9b4f
diff --git a/quic/core/quic_chaos_protector.h b/quic/core/quic_chaos_protector.h
new file mode 100644
index 0000000..6bcd420
--- /dev/null
+++ b/quic/core/quic_chaos_protector.h
@@ -0,0 +1,99 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CHAOS_PROTECTOR_H_
+#define QUICHE_QUIC_CORE_QUIC_CHAOS_PROTECTOR_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "quic/core/crypto/quic_random.h"
+#include "quic/core/frames/quic_crypto_frame.h"
+#include "quic/core/frames/quic_frame.h"
+#include "quic/core/quic_data_writer.h"
+#include "quic/core/quic_framer.h"
+#include "quic/core/quic_packets.h"
+#include "quic/core/quic_stream_frame_data_producer.h"
+#include "quic/core/quic_types.h"
+
+namespace quic {
+
+namespace test {
+class QuicChaosProtectorTest;
+}
+
+// QuicChaosProtector will take a crypto frame and an amount of padding and
+// build a data packet that will parse to something equivalent.
+class QUIC_EXPORT_PRIVATE QuicChaosProtector
+ : public QuicStreamFrameDataProducer {
+ public:
+ // |framer| and |random| must be valid for the lifetime of QuicChaosProtector.
+ explicit QuicChaosProtector(const QuicCryptoFrame& crypto_frame,
+ int num_padding_bytes,
+ size_t packet_size,
+ QuicFramer* framer,
+ QuicRandom* random);
+
+ ~QuicChaosProtector() override;
+
+ QuicChaosProtector(const QuicChaosProtector&) = delete;
+ QuicChaosProtector(QuicChaosProtector&&) = delete;
+ QuicChaosProtector& operator=(const QuicChaosProtector&) = delete;
+ QuicChaosProtector& operator=(QuicChaosProtector&&) = delete;
+
+ // Attempts to build a data packet with chaos protection. If an error occurs,
+ // then absl::nullopt is returned. Otherwise returns the serialized length.
+ absl::optional<size_t> BuildDataPacket(const QuicPacketHeader& header,
+ char* buffer);
+
+ // From QuicStreamFrameDataProducer.
+ WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* /*writer*/) override;
+ bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+
+ private:
+ friend class test::QuicChaosProtectorTest;
+
+ // Allocate the crypto data buffer, create the CRYPTO frame and write the
+ // crypto data to our buffer.
+ bool CopyCryptoDataToLocalBuffer();
+
+ // Split the CRYPTO frame in |frames_| into one or more CRYPTO frames that
+ // collectively represent the same data. Adjusts padding to compensate.
+ void SplitCryptoFrame();
+
+ // Add a random number of PING frames to |frames_| and adjust padding.
+ void AddPingFrames();
+
+ // Randomly reorder |frames_|.
+ void ReorderFrames();
+
+ // Add PADDING frames randomly between all other frames.
+ void SpreadPadding();
+
+ // Serialize |frames_| using |framer_|.
+ absl::optional<size_t> BuildPacket(const QuicPacketHeader& header,
+ char* buffer);
+
+ size_t packet_size_;
+ std::unique_ptr<char[]> crypto_frame_buffer_;
+ const char* crypto_data_buffer_ = nullptr;
+ QuicByteCount crypto_data_length_;
+ QuicStreamOffset crypto_buffer_offset_;
+ EncryptionLevel level_;
+ int remaining_padding_bytes_;
+ QuicFrames frames_; // Inner frames owned, will be deleted by destructor.
+ QuicFramer* framer_; // Unowned.
+ QuicRandom* random_; // Unowned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CHAOS_PROTECTOR_H_