gfe-relnote: In QUIC, enable multiple packet number support in QuicFramer. Not used yet. Not protected.
PiperOrigin-RevId: 239686851
Change-Id: Ia2bb33e823fa4d80ad11366f8bc58079e39eeb47
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 3e4f921..4640d39 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -316,6 +316,35 @@
return PACKET_4BYTE_PACKET_NUMBER;
}
+// Used to get packet number space before packet gets decrypted.
+PacketNumberSpace GetPacketNumberSpace(const QuicPacketHeader& header) {
+ switch (header.form) {
+ case GOOGLE_QUIC_PACKET:
+ QUIC_BUG << "Try to get packet number space of Google QUIC packet";
+ break;
+ case IETF_QUIC_SHORT_HEADER_PACKET:
+ return APPLICATION_DATA;
+ case IETF_QUIC_LONG_HEADER_PACKET:
+ switch (header.long_packet_type) {
+ case INITIAL:
+ return INITIAL_DATA;
+ case HANDSHAKE:
+ return HANDSHAKE_DATA;
+ case ZERO_RTT_PROTECTED:
+ return APPLICATION_DATA;
+ case VERSION_NEGOTIATION:
+ case RETRY:
+ case INVALID_PACKET_TYPE:
+ QUIC_BUG << "Try to get packet number space of long header type: "
+ << QuicUtils::QuicLongHeaderTypetoString(
+ header.long_packet_type);
+ break;
+ }
+ }
+
+ return NUM_PACKET_NUMBER_SPACES;
+}
+
QuicStringPiece TruncateErrorString(QuicStringPiece error) {
if (error.length() <= kMaxErrorStringLength) {
return error;
@@ -472,7 +501,8 @@
infer_packet_header_type_from_version_(perspective ==
Perspective::IS_CLIENT),
expected_connection_id_length_(expected_connection_id_length),
- should_update_expected_connection_id_length_(false) {
+ should_update_expected_connection_id_length_(false),
+ supports_multiple_packet_number_spaces_(false) {
DCHECK(!supported_versions.empty());
version_ = supported_versions_[0];
decrypter_ = QuicMakeUnique<NullDecrypter>(perspective);
@@ -1770,7 +1800,13 @@
if (header->form == IETF_QUIC_SHORT_HEADER_PACKET ||
header->long_packet_type != VERSION_NEGOTIATION) {
// Process packet number.
- QuicPacketNumber base_packet_number = largest_packet_number_;
+ QuicPacketNumber base_packet_number;
+ if (supports_multiple_packet_number_spaces_) {
+ base_packet_number =
+ largest_decrypted_packet_numbers_[GetPacketNumberSpace(*header)];
+ } else {
+ base_packet_number = largest_packet_number_;
+ }
uint64_t full_packet_number;
if (!ProcessAndCalculatePacketNumber(
encrypted_reader, header->packet_number_length, base_packet_number,
@@ -1831,8 +1867,9 @@
header->length_length);
size_t decrypted_length = 0;
+ EncryptionLevel decrypted_level;
if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer,
- buffer_length, &decrypted_length)) {
+ buffer_length, &decrypted_length, &decrypted_level)) {
if (IsIetfStatelessResetPacket(*header)) {
// This is a stateless reset packet.
QuicIetfStatelessResetPacket packet(
@@ -1848,7 +1885,13 @@
// Update the largest packet number after we have decrypted the packet
// so we are confident is not attacker controlled.
- largest_packet_number_.UpdateMax(header->packet_number);
+ if (supports_multiple_packet_number_spaces_) {
+ largest_decrypted_packet_numbers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_level)]
+ .UpdateMax(header->packet_number);
+ } else {
+ largest_packet_number_.UpdateMax(header->packet_number);
+ }
if (!visitor_->OnPacketHeader(*header)) {
RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER);
@@ -1910,8 +1953,9 @@
header->length_length);
size_t decrypted_length = 0;
+ EncryptionLevel decrypted_level;
if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer,
- buffer_length, &decrypted_length)) {
+ buffer_length, &decrypted_length, &decrypted_level)) {
RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE);
set_detailed_error("Unable to decrypt payload.");
return RaiseError(QUIC_DECRYPTION_FAILURE);
@@ -1921,7 +1965,13 @@
// Update the largest packet number after we have decrypted the packet
// so we are confident is not attacker controlled.
- largest_packet_number_.UpdateMax(header->packet_number);
+ if (supports_multiple_packet_number_spaces_) {
+ largest_decrypted_packet_numbers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_level)]
+ .UpdateMax(header->packet_number);
+ } else {
+ largest_packet_number_.UpdateMax(header->packet_number);
+ }
if (!visitor_->OnPacketHeader(*header)) {
// The visitor suppresses further processing of the packet.
@@ -2418,7 +2468,13 @@
bool QuicFramer::ProcessUnauthenticatedHeader(QuicDataReader* encrypted_reader,
QuicPacketHeader* header) {
- QuicPacketNumber base_packet_number = largest_packet_number_;
+ QuicPacketNumber base_packet_number;
+ if (supports_multiple_packet_number_spaces_) {
+ base_packet_number =
+ largest_decrypted_packet_numbers_[GetPacketNumberSpace(*header)];
+ } else {
+ base_packet_number = largest_packet_number_;
+ }
uint64_t full_packet_number;
if (!ProcessAndCalculatePacketNumber(
encrypted_reader, header->packet_number_length, base_packet_number,
@@ -3959,7 +4015,8 @@
const QuicPacketHeader& header,
char* decrypted_buffer,
size_t buffer_length,
- size_t* decrypted_length) {
+ size_t* decrypted_length,
+ EncryptionLevel* decrypted_level) {
DCHECK(decrypter_ != nullptr);
bool success = decrypter_->DecryptPacket(
@@ -3967,6 +4024,7 @@
decrypted_buffer, decrypted_length, buffer_length);
if (success) {
visitor_->OnDecryptedPacket(decrypter_level_);
+ *decrypted_level = decrypter_level_;
} else if (alternative_decrypter_ != nullptr) {
if (header.nonce != nullptr) {
DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
@@ -3991,6 +4049,7 @@
}
if (success) {
visitor_->OnDecryptedPacket(alternative_decrypter_level_);
+ *decrypted_level = decrypter_level_;
if (alternative_decrypter_latch_) {
// Switch to the alternative decrypter and latch so that we cannot
// switch back.
@@ -5700,5 +5759,19 @@
infer_packet_header_type_from_version_ = true;
}
+void QuicFramer::EnableMultiplePacketNumberSpacesSupport() {
+ if (supports_multiple_packet_number_spaces_) {
+ QUIC_BUG << "Multiple packet number spaces has already been enabled";
+ return;
+ }
+ if (largest_packet_number_.IsInitialized()) {
+ QUIC_BUG << "Try to enable multiple packet number spaces support after any "
+ "packet has been received.";
+ return;
+ }
+
+ supports_multiple_packet_number_spaces_ = true;
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 312dbb2..98f7385 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -577,6 +577,8 @@
return expected_connection_id_length_;
}
+ void EnableMultiplePacketNumberSpacesSupport();
+
private:
friend class test::QuicFramerPeer;
@@ -678,7 +680,8 @@
const QuicPacketHeader& header,
char* decrypted_buffer,
size_t buffer_length,
- size_t* decrypted_length);
+ size_t* decrypted_length,
+ EncryptionLevel* decrypted_level);
// Returns the full packet number from the truncated
// wire format version and the last seen packet number.
@@ -876,6 +879,9 @@
QuicErrorCode error_;
// Updated by ProcessPacketHeader when it succeeds decrypting a larger packet.
QuicPacketNumber largest_packet_number_;
+ // Largest successfully decrypted packet number per packet number space. Only
+ // used when supports_multiple_packet_number_spaces_ is true.
+ QuicPacketNumber largest_decrypted_packet_numbers_[NUM_PACKET_NUMBER_SPACES];
// Updated by WritePacketHeader.
QuicConnectionId last_serialized_connection_id_;
// The last QUIC version label received.
@@ -938,6 +944,9 @@
// When this is true, QuicFramer will change expected_connection_id_length_
// to the received destination connection ID length of all IETF long headers.
bool should_update_expected_connection_id_length_;
+
+ // Indicates whether this framer supports multiple packet number spaces.
+ bool supports_multiple_packet_number_spaces_;
};
} // namespace quic
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 782ff03..2985059 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -13141,6 +13141,99 @@
QuicPacketNumber(UINT64_C(0x13374233)));
}
+TEST_P(QuicFramerTest, MultiplePacketNumberSpaces) {
+ if (framer_.transport_version() < QUIC_VERSION_46) {
+ return;
+ }
+ framer_.SetShouldUpdateExpectedConnectionIdLength(true);
+ framer_.EnableMultiplePacketNumberSpacesSupport();
+
+ // clang-format off
+ unsigned char long_header_packet[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ unsigned char long_header_packet99[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // long header packet length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ framer_.SetDecrypter(ENCRYPTION_ZERO_RTT, QuicMakeUnique<TestDecrypter>());
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet),
+ QUIC_ARRAYSIZE(long_header_packet), false)));
+ } else {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet99),
+ QUIC_ARRAYSIZE(long_header_packet99), false)));
+ }
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, INITIAL_DATA)
+ .IsInitialized());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, HANDSHAKE_DATA)
+ .IsInitialized());
+ EXPECT_EQ(kPacketNumber, QuicFramerPeer::GetLargestDecryptedPacketNumber(
+ &framer_, APPLICATION_DATA));
+
+ // clang-format off
+ unsigned char short_header_packet[] = {
+ // type (short header, 1 byte packet number)
+ 0x40,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x79,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ QuicEncryptedPacket short_header_encrypted(
+ AsChars(short_header_packet), QUIC_ARRAYSIZE(short_header_packet), false);
+ framer_.SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TestDecrypter>());
+ EXPECT_TRUE(framer_.ProcessPacket(short_header_encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, INITIAL_DATA)
+ .IsInitialized());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, HANDSHAKE_DATA)
+ .IsInitialized());
+ EXPECT_EQ(kPacketNumber + 1, QuicFramerPeer::GetLargestDecryptedPacketNumber(
+ &framer_, APPLICATION_DATA));
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index d12ebeb..63b9b85 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -531,5 +531,23 @@
QuicEndian::NetToHost64(data_bytes[0]));
}
+// static
+PacketNumberSpace QuicUtils::GetPacketNumberSpace(
+ EncryptionLevel encryption_level) {
+ switch (encryption_level) {
+ case ENCRYPTION_INITIAL:
+ return INITIAL_DATA;
+ case ENCRYPTION_HANDSHAKE:
+ return HANDSHAKE_DATA;
+ case ENCRYPTION_ZERO_RTT:
+ case ENCRYPTION_FORWARD_SECURE:
+ return APPLICATION_DATA;
+ default:
+ QUIC_BUG << "Try to get packet number space of encryption level: "
+ << EncryptionLevelToString(encryption_level);
+ return NUM_PACKET_NUMBER_SPACES;
+ }
+}
+
#undef RETURN_STRING_LITERAL // undef for jumbo builds
} // namespace quic
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h
index 709ea64..515056c 100644
--- a/quic/core/quic_utils.h
+++ b/quic/core/quic_utils.h
@@ -179,6 +179,10 @@
// Generates a 128bit stateless reset token based on a connection ID.
static QuicUint128 GenerateStatelessResetToken(
QuicConnectionId connection_id);
+
+ // Determines packet number space from |encryption_level|.
+ static PacketNumberSpace GetPacketNumberSpace(
+ EncryptionLevel encryption_level);
};
} // namespace quic
diff --git a/quic/test_tools/quic_framer_peer.cc b/quic/test_tools/quic_framer_peer.cc
index 9d8ec86..b8b749f 100644
--- a/quic/test_tools/quic_framer_peer.cc
+++ b/quic/test_tools/quic_framer_peer.cc
@@ -354,5 +354,12 @@
expected_connection_id_length;
}
+// static
+QuicPacketNumber QuicFramerPeer::GetLargestDecryptedPacketNumber(
+ QuicFramer* framer,
+ PacketNumberSpace packet_number_space) {
+ return framer->largest_decrypted_packet_numbers_[packet_number_space];
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/test_tools/quic_framer_peer.h b/quic/test_tools/quic_framer_peer.h
index f6de152..6e34be2 100644
--- a/quic/test_tools/quic_framer_peer.h
+++ b/quic/test_tools/quic_framer_peer.h
@@ -167,6 +167,9 @@
static void SetExpectedConnectionIDLength(
QuicFramer* framer,
uint8_t expected_connection_id_length);
+ static QuicPacketNumber GetLargestDecryptedPacketNumber(
+ QuicFramer* framer,
+ PacketNumberSpace packet_number_space);
};
} // namespace test