Close Quic connections on AEAD integrity limit of received packets that fail authentication.
Protected by FLAGS_quic_reloadable_flag_quic_enable_aead_limits.
PiperOrigin-RevId: 337912276
Change-Id: I1bf2ba93e693a0b3326d8e6a9122aa8346f094b9
diff --git a/quic/core/crypto/aes_base_decrypter.cc b/quic/core/crypto/aes_base_decrypter.cc
index 5bd9c33..464e765 100644
--- a/quic/core/crypto/aes_base_decrypter.cc
+++ b/quic/core/crypto/aes_base_decrypter.cc
@@ -36,4 +36,17 @@
return out;
}
+QuicPacketCount AesBaseDecrypter::GetIntegrityLimit() const {
+ // For AEAD_AES_128_GCM ... endpoints that do not attempt to remove
+ // protection from packets larger than 2^11 bytes can attempt to remove
+ // protection from at most 2^57 packets.
+ // For AEAD_AES_256_GCM [the limit] is substantially larger than the limit for
+ // AEAD_AES_128_GCM. However, this document recommends that the same limit be
+ // applied to both functions as either limit is acceptably large.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-integrity-limit
+ static_assert(kMaxIncomingPacketSize <= 2048,
+ "This key limit requires limits on decryption payload sizes");
+ return 144115188075855872U;
+}
+
} // namespace quic
diff --git a/quic/core/crypto/aes_base_decrypter.h b/quic/core/crypto/aes_base_decrypter.h
index e3613e4..ced9810 100644
--- a/quic/core/crypto/aes_base_decrypter.h
+++ b/quic/core/crypto/aes_base_decrypter.h
@@ -21,6 +21,7 @@
bool SetHeaderProtectionKey(absl::string_view key) override;
std::string GenerateHeaderProtectionMask(
QuicDataReader* sample_reader) override;
+ QuicPacketCount GetIntegrityLimit() const override;
private:
// The key used for packet number encryption.
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter.cc b/quic/core/crypto/chacha20_poly1305_decrypter.cc
index e2f55aa..b860348 100644
--- a/quic/core/crypto/chacha20_poly1305_decrypter.cc
+++ b/quic/core/crypto/chacha20_poly1305_decrypter.cc
@@ -32,4 +32,12 @@
return TLS1_CK_CHACHA20_POLY1305_SHA256;
}
+QuicPacketCount ChaCha20Poly1305Decrypter::GetIntegrityLimit() const {
+ // For AEAD_CHACHA20_POLY1305, the integrity limit is 2^36 invalid packets.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-limits-on-aead-usage
+ static_assert(kMaxIncomingPacketSize < 16384,
+ "This key limit requires limits on decryption payload sizes");
+ return 68719476736U;
+}
+
} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter.h b/quic/core/crypto/chacha20_poly1305_decrypter.h
index 50bb348..2e4b32b 100644
--- a/quic/core/crypto/chacha20_poly1305_decrypter.h
+++ b/quic/core/crypto/chacha20_poly1305_decrypter.h
@@ -33,6 +33,7 @@
~ChaCha20Poly1305Decrypter() override;
uint32_t cipher_id() const override;
+ QuicPacketCount GetIntegrityLimit() const override;
};
} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc b/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
index 8d98da8..7bf8478 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
+++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
@@ -34,4 +34,12 @@
return TLS1_CK_CHACHA20_POLY1305_SHA256;
}
+QuicPacketCount ChaCha20Poly1305TlsDecrypter::GetIntegrityLimit() const {
+ // For AEAD_CHACHA20_POLY1305, the integrity limit is 2^36 invalid packets.
+ // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-limits-on-aead-usage
+ static_assert(kMaxIncomingPacketSize < 16384,
+ "This key limit requires limits on decryption payload sizes");
+ return 68719476736U;
+}
+
} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter.h b/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
index 702fb8c..04ee5b8 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
+++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
@@ -31,6 +31,7 @@
~ChaCha20Poly1305TlsDecrypter() override;
uint32_t cipher_id() const override;
+ QuicPacketCount GetIntegrityLimit() const override;
};
} // namespace quic
diff --git a/quic/core/crypto/null_decrypter.cc b/quic/core/crypto/null_decrypter.cc
index b34e6cd..2fc1ace 100644
--- a/quic/core/crypto/null_decrypter.cc
+++ b/quic/core/crypto/null_decrypter.cc
@@ -102,6 +102,10 @@
return 0;
}
+QuicPacketCount NullDecrypter::GetIntegrityLimit() const {
+ return std::numeric_limits<QuicPacketCount>::max();
+}
+
bool NullDecrypter::ReadHash(QuicDataReader* reader, QuicUint128* hash) {
uint64_t lo;
uint32_t hi;
diff --git a/quic/core/crypto/null_decrypter.h b/quic/core/crypto/null_decrypter.h
index a760ab5..3c856f4 100644
--- a/quic/core/crypto/null_decrypter.h
+++ b/quic/core/crypto/null_decrypter.h
@@ -50,6 +50,7 @@
absl::string_view GetNoncePrefix() const override;
uint32_t cipher_id() const override;
+ QuicPacketCount GetIntegrityLimit() const override;
private:
bool ReadHash(QuicDataReader* reader, QuicUint128* hash);
diff --git a/quic/core/crypto/quic_decrypter.h b/quic/core/crypto/quic_decrypter.h
index 21712f6..4a6cc59 100644
--- a/quic/core/crypto/quic_decrypter.h
+++ b/quic/core/crypto/quic_decrypter.h
@@ -73,6 +73,10 @@
// selector'.
virtual uint32_t cipher_id() const = 0;
+ // Returns the maximum number of packets that can safely fail decryption with
+ // this decrypter.
+ virtual QuicPacketCount GetIntegrityLimit() const = 0;
+
// For use by unit tests only.
virtual absl::string_view GetKey() const = 0;
virtual absl::string_view GetNoncePrefix() const = 0;
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index a19eae0..fa02801 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -267,6 +267,7 @@
support_key_update_for_connection_(false),
enable_aead_limits_(GetQuicReloadableFlag(quic_enable_aead_limits) &&
version().UsesTls()),
+ num_failed_authentication_packets_received_(0),
last_packet_decrypted_(false),
last_size_(0),
current_packet_data_(nullptr),
@@ -2210,6 +2211,29 @@
debug_visitor_->OnUndecryptablePacket(decryption_level,
/*dropped=*/!should_enqueue);
}
+
+ if (has_decryption_key) {
+ num_failed_authentication_packets_received_++;
+ if (enable_aead_limits_) {
+ // Should always be non-null if has_decryption_key is true.
+ DCHECK(framer_.GetDecrypter(decryption_level));
+ const QuicPacketCount integrity_limit =
+ framer_.GetDecrypter(decryption_level)->GetIntegrityLimit();
+ QUIC_DVLOG(2) << ENDPOINT << "Checking AEAD integrity limits:"
+ << " num_failed_authentication_packets_received_="
+ << num_failed_authentication_packets_received_
+ << " integrity_limit=" << integrity_limit;
+ if (num_failed_authentication_packets_received_ >= integrity_limit) {
+ const std::string error_details = quiche::QuicheStrCat(
+ "decrypter integrity limit reached:"
+ " num_failed_authentication_packets_received_=",
+ num_failed_authentication_packets_received_,
+ " integrity_limit=", integrity_limit);
+ CloseConnection(QUIC_AEAD_LIMIT_REACHED, error_details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+ }
+ }
}
bool QuicConnection::ShouldEnqueueUnDecryptablePacket(
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 73c6706..4be1b96 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1567,6 +1567,10 @@
// update (if allowed) and/or closing the connection, as necessary.
bool enable_aead_limits_;
+ // Counts the number of undecryptable packets received across all keys. Does
+ // not include packets where a decryption key for that level was absent.
+ QuicPacketCount num_failed_authentication_packets_received_;
+
// True if the last packet has gotten far enough in the framer to be
// decrypted.
bool last_packet_decrypted_;
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index fd84b30..0ccfefe 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -128,6 +128,20 @@
QuicPacketCount confidentiality_limit_;
};
+class StrictTaggingDecrypterWithIntegrityLimit : public StrictTaggingDecrypter {
+ public:
+ StrictTaggingDecrypterWithIntegrityLimit(uint8_t tag,
+ QuicPacketCount integrity_limit)
+ : StrictTaggingDecrypter(tag), integrity_limit_(integrity_limit) {}
+
+ QuicPacketCount GetIntegrityLimit() const override {
+ return integrity_limit_;
+ }
+
+ private:
+ QuicPacketCount integrity_limit_;
+};
+
class TestConnectionHelper : public QuicConnectionHelperInterface {
public:
TestConnectionHelper(MockClock* clock, MockRandom* random_generator)
@@ -12457,6 +12471,252 @@
TestConnectionCloseQuicErrorCode(QUIC_AEAD_LIMIT_REACHED);
}
+TEST_P(QuicConnectionTest, CloseConnectionOnIntegrityLimitDuringHandshake) {
+ if (!connection_.version().UsesTls()) {
+ return;
+ }
+
+ QuicConnectionPeer::SetEnableAeadLimits(&connection_, true);
+
+ constexpr uint8_t correct_tag = 0x01;
+ constexpr uint8_t wrong_tag = 0xFE;
+ constexpr QuicPacketCount kIntegrityLimit = 3;
+
+ SetDecrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<StrictTaggingDecrypterWithIntegrityLimit>(
+ correct_tag, kIntegrityLimit));
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(correct_tag));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(wrong_tag));
+ for (uint64_t i = 1; i <= kIntegrityLimit; ++i) {
+ EXPECT_TRUE(connection_.connected());
+ if (i == kIntegrityLimit) {
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(AnyNumber());
+ }
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_HANDSHAKE);
+ }
+ EXPECT_FALSE(connection_.connected());
+ TestConnectionCloseQuicErrorCode(QUIC_AEAD_LIMIT_REACHED);
+}
+
+TEST_P(QuicConnectionTest, CloseConnectionOnIntegrityLimitAfterHandshake) {
+ if (!connection_.version().UsesTls()) {
+ return;
+ }
+
+ QuicConnectionPeer::SetEnableAeadLimits(&connection_, true);
+
+ constexpr uint8_t correct_tag = 0x01;
+ constexpr uint8_t wrong_tag = 0xFE;
+ constexpr QuicPacketCount kIntegrityLimit = 3;
+
+ use_tagging_decrypter();
+ SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<StrictTaggingDecrypterWithIntegrityLimit>(
+ correct_tag, kIntegrityLimit));
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(correct_tag));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_CALL(visitor_, GetHandshakeState())
+ .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
+ connection_.OnHandshakeComplete();
+ connection_.RemoveEncrypter(ENCRYPTION_INITIAL);
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(wrong_tag));
+ for (uint64_t i = 1; i <= kIntegrityLimit; ++i) {
+ EXPECT_TRUE(connection_.connected());
+ if (i == kIntegrityLimit) {
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
+ }
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ }
+ EXPECT_FALSE(connection_.connected());
+ TestConnectionCloseQuicErrorCode(QUIC_AEAD_LIMIT_REACHED);
+}
+
+TEST_P(QuicConnectionTest,
+ CloseConnectionOnIntegrityLimitAcrossEncryptionLevels) {
+ if (!connection_.version().UsesTls()) {
+ return;
+ }
+
+ QuicConnectionPeer::SetEnableAeadLimits(&connection_, true);
+
+ constexpr uint8_t correct_tag = 0x01;
+ constexpr uint8_t wrong_tag = 0xFE;
+ constexpr QuicPacketCount kIntegrityLimit = 4;
+
+ use_tagging_decrypter();
+ SetDecrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<StrictTaggingDecrypterWithIntegrityLimit>(
+ correct_tag, kIntegrityLimit));
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(correct_tag));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(wrong_tag));
+ for (uint64_t i = 1; i <= 2; ++i) {
+ EXPECT_TRUE(connection_.connected());
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_HANDSHAKE);
+ }
+
+ SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<StrictTaggingDecrypterWithIntegrityLimit>(
+ correct_tag, kIntegrityLimit));
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(correct_tag));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_CALL(visitor_, GetHandshakeState())
+ .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
+ connection_.OnHandshakeComplete();
+ connection_.RemoveEncrypter(ENCRYPTION_INITIAL);
+ connection_.RemoveEncrypter(ENCRYPTION_HANDSHAKE);
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(wrong_tag));
+ for (uint64_t i = 3; i <= kIntegrityLimit; ++i) {
+ EXPECT_TRUE(connection_.connected());
+ if (i == kIntegrityLimit) {
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
+ }
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ }
+ EXPECT_FALSE(connection_.connected());
+ TestConnectionCloseQuicErrorCode(QUIC_AEAD_LIMIT_REACHED);
+}
+
+TEST_P(QuicConnectionTest, IntegrityLimitDoesNotApplyWithoutDecryptionKey) {
+ if (!connection_.version().UsesTls()) {
+ return;
+ }
+
+ QuicConnectionPeer::SetEnableAeadLimits(&connection_, true);
+
+ constexpr uint8_t correct_tag = 0x01;
+ constexpr uint8_t wrong_tag = 0xFE;
+ constexpr QuicPacketCount kIntegrityLimit = 3;
+
+ use_tagging_decrypter();
+ SetDecrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<StrictTaggingDecrypterWithIntegrityLimit>(
+ correct_tag, kIntegrityLimit));
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(correct_tag));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(wrong_tag));
+ for (uint64_t i = 1; i <= kIntegrityLimit * 2; ++i) {
+ EXPECT_TRUE(connection_.connected());
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ }
+ EXPECT_TRUE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, CloseConnectionOnIntegrityLimitAcrossKeyPhases) {
+ if (!connection_.version().UsesTls()) {
+ return;
+ }
+
+ constexpr QuicPacketCount kIntegrityLimit = 4;
+
+ QuicConnectionPeer::SetEnableAeadLimits(&connection_, true);
+ TransportParameters params;
+ params.key_update_not_yet_supported = false;
+ QuicConfig config;
+ std::string error_details;
+ EXPECT_THAT(config.ProcessTransportParameters(
+ params, /* is_resumption = */ false, &error_details),
+ IsQuicNoError());
+ config.SetKeyUpdateSupportedLocally();
+ QuicConfigPeer::SetNegotiated(&config, true);
+ if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+ QuicConfigPeer::SetReceivedOriginalConnectionId(
+ &config, connection_.connection_id());
+ QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+ &config, connection_.connection_id());
+ }
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+
+ MockFramerVisitor peer_framer_visitor_;
+ peer_framer_.set_visitor(&peer_framer_visitor_);
+
+ use_tagging_decrypter();
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(0x01));
+ SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<StrictTaggingDecrypterWithIntegrityLimit>(
+ 0x01, kIntegrityLimit));
+ EXPECT_CALL(visitor_, GetHandshakeState())
+ .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
+ connection_.OnHandshakeComplete();
+ connection_.RemoveEncrypter(ENCRYPTION_INITIAL);
+
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(0xFF));
+ for (uint64_t i = 1; i <= 2; ++i) {
+ EXPECT_TRUE(connection_.connected());
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ }
+
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(0x01));
+ // Send packet 1.
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);
+ EXPECT_EQ(QuicPacketNumber(1u), last_packet);
+ // Receive ack for packet 1.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame1 = InitAckFrame(1);
+ ProcessAckPacket(&frame1);
+ // Key update should now be allowed, initiate it.
+ EXPECT_CALL(visitor_, AdvanceKeysAndCreateCurrentOneRttDecrypter())
+ .WillOnce([kIntegrityLimit]() {
+ return std::make_unique<StrictTaggingDecrypterWithIntegrityLimit>(
+ 0x02, kIntegrityLimit);
+ });
+ EXPECT_CALL(visitor_, CreateCurrentOneRttEncrypter()).WillOnce([]() {
+ return std::make_unique<TaggingEncrypter>(0x02);
+ });
+ EXPECT_CALL(visitor_, OnKeyUpdate(KeyUpdateReason::kLocalForTests));
+ EXPECT_TRUE(connection_.InitiateKeyUpdate(KeyUpdateReason::kLocalForTests));
+
+ // Pretend that peer accepts the key update.
+ EXPECT_CALL(peer_framer_visitor_,
+ AdvanceKeysAndCreateCurrentOneRttDecrypter())
+ .WillOnce(
+ []() { return std::make_unique<StrictTaggingDecrypter>(0x02); });
+ EXPECT_CALL(peer_framer_visitor_, CreateCurrentOneRttEncrypter())
+ .WillOnce([]() { return std::make_unique<TaggingEncrypter>(0x02); });
+ peer_framer_.SetKeyUpdateSupportForConnection(true);
+ peer_framer_.DoKeyUpdate(KeyUpdateReason::kLocalForTests);
+
+ // Send packet 2.
+ SendStreamDataToPeer(2, "bar", 0, NO_FIN, &last_packet);
+ EXPECT_EQ(QuicPacketNumber(2u), last_packet);
+ // Receive ack for packet 2.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame2 = InitAckFrame(2);
+ ProcessAckPacket(&frame2);
+
+ // Do two more undecryptable packets. Integrity limit should be reached.
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(0xFF));
+ for (uint64_t i = 3; i <= kIntegrityLimit; ++i) {
+ EXPECT_TRUE(connection_.connected());
+ if (i == kIntegrityLimit) {
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
+ }
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ }
+ EXPECT_FALSE(connection_.connected());
+ TestConnectionCloseQuicErrorCode(QUIC_AEAD_LIMIT_REACHED);
+}
+
TEST_P(QuicConnectionTest, SendAckFrequencyFrame) {
if (!version().HasIetfQuicFrames()) {
return;
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 949d246..d9baee2 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -183,6 +183,9 @@
}
// Use a distinct value starting with 0xFFFFFF, which is never used by TLS.
uint32_t cipher_id() const override { return 0xFFFFFFF2; }
+ QuicPacketCount GetIntegrityLimit() const override {
+ return std::numeric_limits<QuicPacketCount>::max();
+ }
QuicPacketNumber packet_number_;
std::string associated_data_;
std::string ciphertext_;
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index cff9d1a..a5a774d 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -1927,6 +1927,9 @@
}
// Use a distinct value starting with 0xFFFFFF, which is never used by TLS.
uint32_t cipher_id() const override { return 0xFFFFFFF0; }
+ QuicPacketCount GetIntegrityLimit() const override {
+ return std::numeric_limits<QuicPacketCount>::max();
+ }
protected:
virtual uint8_t GetTag(absl::string_view ciphertext) {