Do not queue coalesced undecryptable packets twice
This CL adds QuicFramerVisitorInterface::OnUndecryptablePacket and uses it to send undecryptable packets from QuicFramer to QuicConnection, instead of the previous mechanism which relied on QuicFramer::ProcessPacket returning QUIC_DECRYPTION_FAILURE. The new mechanism has the following advantages:
1) It only sends the current packet, without any subsequent coalesced packets
2) It knows if the decryption failed due to a missing key, which allows us to avoid buffering packets that we know we will never be able to decrypt
This mechanism is enabled for versions that KnowsWhichDecrypterToUse() (which are v47+ || TLS, none of which are currently enabled) and when the new flag quic_framer_uses_undecryptable_upcall is true - the intent being to enable this for all versions once the flag protection process is complete.
This CL also adds QuicDataReader::FullPayload which is required to extract only this packet without further coalesced packets, and associated test.
gfe-relnote: do not queue coalesced undecryptable packets twice, protected by disabled flag gfe2_restart_flag_quic_framer_uses_undecryptable_upcall
PiperOrigin-RevId: 263658152
Change-Id: I66aca2138e353306a5cf4fa9ec259680f4115890
diff --git a/quic/core/chlo_extractor.cc b/quic/core/chlo_extractor.cc
index 0e8e6f5..38e97bd 100644
--- a/quic/core/chlo_extractor.cc
+++ b/quic/core/chlo_extractor.cc
@@ -44,6 +44,9 @@
void OnDecryptedPacket(EncryptionLevel /*level*/) override {}
bool OnPacketHeader(const QuicPacketHeader& header) override;
void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
+ void OnUndecryptablePacket(const QuicEncryptedPacket& packet,
+ EncryptionLevel decryption_level,
+ bool has_decryption_key) override;
bool OnStreamFrame(const QuicStreamFrame& frame) override;
bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
bool OnAckFrameStart(QuicPacketNumber largest_acked,
@@ -142,8 +145,15 @@
bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& /*header*/) {
return true;
}
+
void ChloFramerVisitor::OnCoalescedPacket(
const QuicEncryptedPacket& /*packet*/) {}
+
+void ChloFramerVisitor::OnUndecryptablePacket(
+ const QuicEncryptedPacket& /*packet*/,
+ EncryptionLevel /*decryption_level*/,
+ bool /*has_decryption_key*/) {}
+
bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
if (QuicVersionUsesCryptoFrames(framer_->transport_version())) {
// CHLO will be sent in CRYPTO frames in v47 and above.
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index cd065af..864f652 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -1623,6 +1623,45 @@
QueueCoalescedPacket(packet);
}
+void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet,
+ EncryptionLevel decryption_level,
+ bool has_decryption_key) {
+ QUIC_DVLOG(1) << ENDPOINT << "Received undecryptable packet of length "
+ << packet.length() << " with"
+ << (has_decryption_key ? "" : "out") << " key at level "
+ << QuicUtils::EncryptionLevelToString(decryption_level)
+ << " while connection is at encryption level "
+ << QuicUtils::EncryptionLevelToString(encryption_level_);
+ DCHECK(GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall));
+ QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 1, 7);
+ DCHECK(EncryptionLevelIsValid(decryption_level));
+ ++stats_.undecryptable_packets_received;
+
+ bool should_enqueue = true;
+ if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) {
+ // We do not expect to install any further keys.
+ should_enqueue = false;
+ } else if (undecryptable_packets_.size() >= max_undecryptable_packets_) {
+ // We do not queue more than max_undecryptable_packets_ packets.
+ should_enqueue = false;
+ } else if (has_decryption_key) {
+ // We already have the key for this decryption level, therefore no
+ // future keys will allow it be decrypted.
+ should_enqueue = false;
+ } else if (version().KnowsWhichDecrypterToUse() &&
+ decryption_level <= encryption_level_) {
+ // On versions that know which decrypter to use, we install keys in order
+ // so we will not get newer keys for lower encryption levels.
+ should_enqueue = false;
+ }
+
+ if (should_enqueue) {
+ QueueUndecryptablePacket(packet);
+ } else if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnUndecryptablePacket();
+ }
+}
+
void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
@@ -1682,7 +1721,8 @@
if (!framer_.ProcessPacket(packet)) {
// If we are unable to decrypt this packet, it might be
// because the CHLO or SHLO packet was lost.
- if (framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ if (framer_.error() == QUIC_DECRYPTION_FAILURE &&
+ !GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
++stats_.undecryptable_packets_received;
if (encryption_level_ != ENCRYPTION_FORWARD_SECURE &&
undecryptable_packets_.size() < max_undecryptable_packets_) {
@@ -1690,6 +1730,8 @@
} else if (debug_visitor_ != nullptr) {
debug_visitor_->OnUndecryptablePacket();
}
+ } else if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 2, 7);
}
QUIC_DVLOG(1) << ENDPOINT
<< "Unable to process packet. Last packet processed: "
@@ -2536,6 +2578,9 @@
}
void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) {
+ QUIC_DVLOG(1) << ENDPOINT << "Setting default encryption level from "
+ << QuicUtils::EncryptionLevelToString(encryption_level_)
+ << " to " << QuicUtils::EncryptionLevelToString(level);
if (level != encryption_level_ && packet_generator_.HasPendingFrames()) {
// Flush all queued frames when encryption level changes.
ScopedPacketFlusher flusher(this);
@@ -2591,6 +2636,16 @@
void QuicConnection::QueueUndecryptablePacket(
const QuicEncryptedPacket& packet) {
+ if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 3, 7);
+ for (const auto& saved_packet : undecryptable_packets_) {
+ if (packet.data() == saved_packet->data() &&
+ packet.length() == saved_packet->length()) {
+ QUIC_DVLOG(1) << ENDPOINT << "Not queueing known undecryptable packet";
+ return;
+ }
+ }
+ }
QUIC_DVLOG(1) << ENDPOINT << "Queueing undecryptable packet.";
undecryptable_packets_.push_back(packet.Clone());
}
@@ -2656,7 +2711,8 @@
} else {
// If we are unable to decrypt this packet, it might be
// because the CHLO or SHLO packet was lost.
- if (framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ if (framer_.error() == QUIC_DECRYPTION_FAILURE &&
+ !GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
++stats_.undecryptable_packets_received;
if (encryption_level_ != ENCRYPTION_FORWARD_SECURE &&
undecryptable_packets_.size() < max_undecryptable_packets_) {
@@ -2664,6 +2720,8 @@
} else if (debug_visitor_ != nullptr) {
debug_visitor_->OnUndecryptablePacket();
}
+ } else if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 4, 7);
}
}
}
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 7637584..87d0d81 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -476,6 +476,9 @@
void OnDecryptedPacket(EncryptionLevel level) override;
bool OnPacketHeader(const QuicPacketHeader& header) override;
void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
+ void OnUndecryptablePacket(const QuicEncryptedPacket& packet,
+ EncryptionLevel decryption_level,
+ bool has_decryption_key) override;
bool OnStreamFrame(const QuicStreamFrame& frame) override;
bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
bool OnAckFrameStart(QuicPacketNumber largest_acked,
diff --git a/quic/core/quic_data_reader.cc b/quic/core/quic_data_reader.cc
index d4f28a0..87e9645 100644
--- a/quic/core/quic_data_reader.cc
+++ b/quic/core/quic_data_reader.cc
@@ -192,6 +192,10 @@
return QuicStringPiece(data_ + pos_, len_ - pos_);
}
+QuicStringPiece QuicDataReader::FullPayload() const {
+ return QuicStringPiece(data_, len_);
+}
+
bool QuicDataReader::ReadBytes(void* result, size_t size) {
// Make sure that we have enough data to read.
if (!CanRead(size)) {
diff --git a/quic/core/quic_data_reader.h b/quic/core/quic_data_reader.h
index 72e2d13..74ed226 100644
--- a/quic/core/quic_data_reader.h
+++ b/quic/core/quic_data_reader.h
@@ -110,6 +110,14 @@
// DOES NOT forward the internal iterator.
QuicStringPiece PeekRemainingPayload() const;
+ // Returns the entire payload as a QuicStringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // DOES NOT forward the internal iterator.
+ QuicStringPiece FullPayload() const;
+
// Reads a given number of bytes into the given buffer. The buffer
// must be of adequate size.
// Forwards the internal iterator on success.
diff --git a/quic/core/quic_data_writer_test.cc b/quic/core/quic_data_writer_test.cc
index 301bfa6..64ad5dd 100644
--- a/quic/core/quic_data_writer_test.cc
+++ b/quic/core/quic_data_writer_test.cc
@@ -1232,6 +1232,37 @@
}
}
+TEST_P(QuicDataWriterTest, PayloadReads) {
+ char buffer[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+ char expected_first_read[4] = {1, 2, 3, 4};
+ char expected_remaining[12] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+ QuicDataReader reader(buffer, sizeof(buffer));
+ char first_read_buffer[4] = {};
+ EXPECT_TRUE(reader.ReadBytes(first_read_buffer, sizeof(first_read_buffer)));
+ test::CompareCharArraysWithHexError(
+ "first read", first_read_buffer, sizeof(first_read_buffer),
+ expected_first_read, sizeof(expected_first_read));
+ QuicStringPiece peeked_remaining_payload = reader.PeekRemainingPayload();
+ test::CompareCharArraysWithHexError(
+ "peeked_remaining_payload", peeked_remaining_payload.data(),
+ peeked_remaining_payload.length(), expected_remaining,
+ sizeof(expected_remaining));
+ QuicStringPiece full_payload = reader.FullPayload();
+ test::CompareCharArraysWithHexError("full_payload", full_payload.data(),
+ full_payload.length(), buffer,
+ sizeof(buffer));
+ QuicStringPiece read_remaining_payload = reader.ReadRemainingPayload();
+ test::CompareCharArraysWithHexError(
+ "read_remaining_payload", read_remaining_payload.data(),
+ read_remaining_payload.length(), expected_remaining,
+ sizeof(expected_remaining));
+ EXPECT_TRUE(reader.IsDoneReading());
+ QuicStringPiece full_payload2 = reader.FullPayload();
+ test::CompareCharArraysWithHexError("full_payload2", full_payload2.data(),
+ full_payload2.length(), buffer,
+ sizeof(buffer));
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 93b8575..08593da 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -1843,6 +1843,16 @@
return true;
}
if (hp_removal_failed) {
+ if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 5,
+ 7);
+ const EncryptionLevel decryption_level = GetEncryptionLevel(*header);
+ const bool has_decryption_key =
+ decrypter_[decryption_level] != nullptr;
+ visitor_->OnUndecryptablePacket(
+ QuicEncryptedPacket(encrypted_reader->FullPayload()),
+ decryption_level, has_decryption_key);
+ }
set_detailed_error("Unable to decrypt header protection.");
return RaiseError(QUIC_DECRYPTION_FAILURE);
}
@@ -1901,6 +1911,15 @@
visitor_->OnAuthenticatedIetfStatelessResetPacket(packet);
return true;
}
+ if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 6, 7);
+ const EncryptionLevel decryption_level = GetEncryptionLevel(*header);
+ const bool has_decryption_key = version_.KnowsWhichDecrypterToUse() &&
+ decrypter_[decryption_level] != nullptr;
+ visitor_->OnUndecryptablePacket(
+ QuicEncryptedPacket(encrypted_reader->FullPayload()),
+ decryption_level, has_decryption_key);
+ }
set_detailed_error("Unable to decrypt payload.");
RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE);
return RaiseError(QUIC_DECRYPTION_FAILURE);
@@ -1979,6 +1998,16 @@
EncryptionLevel decrypted_level;
if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer,
buffer_length, &decrypted_length, &decrypted_level)) {
+ if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_framer_uses_undecryptable_upcall, 7, 7);
+ const EncryptionLevel decryption_level = decrypter_level_;
+ // This version uses trial decryption so we always report to our visitor
+ // that we are not certain we have the correct decryption key.
+ const bool has_decryption_key = false;
+ visitor_->OnUndecryptablePacket(
+ QuicEncryptedPacket(encrypted_reader->FullPayload()),
+ decryption_level, has_decryption_key);
+ }
RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE);
set_detailed_error("Unable to decrypt payload.");
return RaiseError(QUIC_DECRYPTION_FAILURE);
@@ -4173,6 +4202,9 @@
DCHECK_EQ(alternative_decrypter_level_, NUM_ENCRYPTION_LEVELS);
DCHECK_GE(level, decrypter_level_);
DCHECK(!version_.KnowsWhichDecrypterToUse());
+ QUIC_DVLOG(1) << ENDPOINT << "Setting decrypter from level "
+ << QuicUtils::EncryptionLevelToString(decrypter_level_)
+ << " to " << QuicUtils::EncryptionLevelToString(level);
decrypter_[decrypter_level_] = nullptr;
decrypter_[level] = std::move(decrypter);
decrypter_level_ = level;
@@ -4184,6 +4216,10 @@
bool latch_once_used) {
DCHECK_NE(level, decrypter_level_);
DCHECK(!version_.KnowsWhichDecrypterToUse());
+ QUIC_DVLOG(1) << ENDPOINT << "Setting alternative decrypter from level "
+ << QuicUtils::EncryptionLevelToString(
+ alternative_decrypter_level_)
+ << " to " << QuicUtils::EncryptionLevelToString(level);
if (alternative_decrypter_level_ != NUM_ENCRYPTION_LEVELS) {
decrypter_[alternative_decrypter_level_] = nullptr;
}
@@ -4195,11 +4231,15 @@
void QuicFramer::InstallDecrypter(EncryptionLevel level,
std::unique_ptr<QuicDecrypter> decrypter) {
DCHECK(version_.KnowsWhichDecrypterToUse());
+ QUIC_DVLOG(1) << ENDPOINT << "Installing decrypter at level "
+ << QuicUtils::EncryptionLevelToString(level);
decrypter_[level] = std::move(decrypter);
}
void QuicFramer::RemoveDecrypter(EncryptionLevel level) {
DCHECK(version_.KnowsWhichDecrypterToUse());
+ QUIC_DVLOG(1) << ENDPOINT << "Removing decrypter at level "
+ << QuicUtils::EncryptionLevelToString(level);
decrypter_[level] = nullptr;
}
@@ -4223,6 +4263,8 @@
std::unique_ptr<QuicEncrypter> encrypter) {
DCHECK_GE(level, 0);
DCHECK_LT(level, NUM_ENCRYPTION_LEVELS);
+ QUIC_DVLOG(1) << ENDPOINT << "Setting encrypter at level "
+ << QuicUtils::EncryptionLevelToString(level);
encrypter_[level] = std::move(encrypter);
}
@@ -4359,8 +4401,9 @@
QuicDecrypter* decrypter = decrypter_[expected_decryption_level].get();
if (decrypter == nullptr) {
QUIC_DVLOG(1)
+ << ENDPOINT
<< "No decrypter available for removing header protection at level "
- << expected_decryption_level;
+ << QuicUtils::EncryptionLevelToString(expected_decryption_level);
return false;
}
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index be5f128..c749f04 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -15,6 +15,7 @@
#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
@@ -127,6 +128,13 @@
// own its internal buffer, the visitor should make a copy of it.
virtual void OnCoalescedPacket(const QuicEncryptedPacket& packet) = 0;
+ // Called when the packet being processed failed to decrypt.
+ // |has_decryption_key| indicates whether the framer knew which decryption
+ // key to use for this packet and already had a suitable key.
+ virtual void OnUndecryptablePacket(const QuicEncryptedPacket& packet,
+ EncryptionLevel decryption_level,
+ bool has_decryption_key) = 0;
+
// Called when a StreamFrame has been parsed.
virtual bool OnStreamFrame(const QuicStreamFrame& frame) = 0;
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 31b5ca5..4eb20df 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -240,12 +240,15 @@
}
void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {
- size_t coalesced_data_length = packet.length();
- char* coalesced_data = new char[coalesced_data_length];
- memcpy(coalesced_data, packet.data(), coalesced_data_length);
- coalesced_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>(
- coalesced_data, coalesced_data_length,
- /*owns_buffer=*/true));
+ coalesced_packets_.push_back(packet.Clone());
+ }
+
+ void OnUndecryptablePacket(const QuicEncryptedPacket& packet,
+ EncryptionLevel decryption_level,
+ bool has_decryption_key) override {
+ undecryptable_packets_.push_back(packet.Clone());
+ undecryptable_decryption_levels_.push_back(decryption_level);
+ undecryptable_has_decryption_keys_.push_back(has_decryption_key);
}
bool OnStreamFrame(const QuicStreamFrame& frame) override {
@@ -421,6 +424,9 @@
std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_;
std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_;
std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_;
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_;
+ std::vector<EncryptionLevel> undecryptable_decryption_levels_;
+ std::vector<bool> undecryptable_has_decryption_keys_;
QuicRstStreamFrame rst_stream_frame_;
QuicConnectionCloseFrame connection_close_frame_;
QuicStopSendingFrame stop_sending_frame_;
@@ -13773,6 +13779,344 @@
CheckStreamFrameData("HELLO_WORLD?", visitor_.stream_frames_[1].get());
}
+TEST_P(QuicFramerTest, UndecryptablePacketWithoutDecrypter) {
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ if (!framer_.version().KnowsWhichDecrypterToUse()) {
+ // We create a bad client decrypter by using initial encryption with a
+ // bogus connection ID; it should fail to decrypt everything.
+ QuicConnectionId bogus_connection_id = TestConnectionId(0xbad);
+ CrypterPair bogus_crypters;
+ CryptoUtils::CreateTlsInitialCrypters(Perspective::IS_CLIENT,
+ framer_.transport_version(),
+ bogus_connection_id, &bogus_crypters);
+ // This removes all other decrypters.
+ framer_.SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ std::move(bogus_crypters.decrypter));
+ }
+
+ const unsigned char type_byte =
+ framer_.transport_version() == QUIC_VERSION_44 ? 0xFC : 0xE3;
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (version included, 8-byte connection ID,
+ // 4-byte packet number)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x00,
+ // padding frames
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ unsigned char packet44[] = {
+ // public flags (long header with packet type HANDSHAKE and
+ // 4-byte packet number)
+ type_byte,
+ // version
+ QUIC_VERSION_BYTES,
+ // connection ID lengths
+ 0x05,
+ // source connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x00,
+ // padding frames
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ unsigned char packet99[] = {
+ // public flags (long header with packet type HANDSHAKE and
+ // 4-byte packet number)
+ 0xE3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x00,
+ // source connection ID length
+ 0x08,
+ // source connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x24,
+ // packet number
+ 0x12, 0x34, 0x56, 0x00,
+ // padding frames
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_length = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_length = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() >= QUIC_VERSION_44) {
+ p = packet44;
+ p_length = QUIC_ARRAYSIZE(packet44);
+ }
+ // First attempt decryption without the handshake crypter.
+ EXPECT_FALSE(
+ framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false)));
+ EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error());
+ if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ ASSERT_EQ(1u, visitor_.undecryptable_packets_.size());
+ ASSERT_EQ(1u, visitor_.undecryptable_decryption_levels_.size());
+ ASSERT_EQ(1u, visitor_.undecryptable_has_decryption_keys_.size());
+ CompareCharArraysWithHexError(
+ "undecryptable packet", visitor_.undecryptable_packets_[0]->data(),
+ visitor_.undecryptable_packets_[0]->length(), AsChars(p), p_length);
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ EXPECT_EQ(ENCRYPTION_HANDSHAKE,
+ visitor_.undecryptable_decryption_levels_[0]);
+ }
+ EXPECT_FALSE(visitor_.undecryptable_has_decryption_keys_[0]);
+ } else {
+ EXPECT_EQ(0u, visitor_.undecryptable_packets_.size());
+ EXPECT_EQ(0u, visitor_.undecryptable_decryption_levels_.size());
+ EXPECT_EQ(0u, visitor_.undecryptable_has_decryption_keys_.size());
+ }
+}
+
+TEST_P(QuicFramerTest, UndecryptablePacketWithDecrypter) {
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ // We create a bad client decrypter by using initial encryption with a
+ // bogus connection ID; it should fail to decrypt everything.
+ QuicConnectionId bogus_connection_id = TestConnectionId(0xbad);
+ CrypterPair bad_handshake_crypters;
+ CryptoUtils::CreateTlsInitialCrypters(
+ Perspective::IS_CLIENT, framer_.transport_version(), bogus_connection_id,
+ &bad_handshake_crypters);
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_HANDSHAKE,
+ std::move(bad_handshake_crypters.decrypter));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_HANDSHAKE,
+ std::move(bad_handshake_crypters.decrypter));
+ }
+
+ const unsigned char type_byte =
+ framer_.transport_version() == QUIC_VERSION_44 ? 0xFC : 0xE3;
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (version included, 8-byte connection ID,
+ // 4-byte packet number)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x00,
+ // padding frames
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ unsigned char packet44[] = {
+ // public flags (long header with packet type HANDSHAKE and
+ // 4-byte packet number)
+ type_byte,
+ // version
+ QUIC_VERSION_BYTES,
+ // connection ID lengths
+ 0x05,
+ // source connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x00,
+ // padding frames
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ unsigned char packet99[] = {
+ // public flags (long header with packet type HANDSHAKE and
+ // 4-byte packet number)
+ 0xE3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x00,
+ // source connection ID length
+ 0x08,
+ // source connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x24,
+ // packet number
+ 0x12, 0x34, 0x56, 0x00,
+ // padding frames
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_length = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_length = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() >= QUIC_VERSION_44) {
+ p = packet44;
+ p_length = QUIC_ARRAYSIZE(packet44);
+ }
+
+ EXPECT_FALSE(
+ framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false)));
+ EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error());
+ if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ ASSERT_EQ(1u, visitor_.undecryptable_packets_.size());
+ ASSERT_EQ(1u, visitor_.undecryptable_decryption_levels_.size());
+ ASSERT_EQ(1u, visitor_.undecryptable_has_decryption_keys_.size());
+ CompareCharArraysWithHexError(
+ "undecryptable packet", visitor_.undecryptable_packets_[0]->data(),
+ visitor_.undecryptable_packets_[0]->length(), AsChars(p), p_length);
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ EXPECT_EQ(ENCRYPTION_HANDSHAKE,
+ visitor_.undecryptable_decryption_levels_[0]);
+ }
+ EXPECT_EQ(framer_.version().KnowsWhichDecrypterToUse(),
+ visitor_.undecryptable_has_decryption_keys_[0]);
+ } else {
+ EXPECT_EQ(0u, visitor_.undecryptable_packets_.size());
+ EXPECT_EQ(0u, visitor_.undecryptable_decryption_levels_.size());
+ EXPECT_EQ(0u, visitor_.undecryptable_has_decryption_keys_.size());
+ }
+}
+
+TEST_P(QuicFramerTest, UndecryptableCoalescedPacket) {
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ return;
+ }
+ ASSERT_TRUE(framer_.version().KnowsWhichDecrypterToUse());
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // We create a bad client decrypter by using initial encryption with a
+ // bogus connection ID; it should fail to decrypt everything.
+ QuicConnectionId bogus_connection_id = TestConnectionId(0xbad);
+ CrypterPair bad_handshake_crypters;
+ CryptoUtils::CreateTlsInitialCrypters(
+ Perspective::IS_CLIENT, framer_.transport_version(), bogus_connection_id,
+ &bad_handshake_crypters);
+ framer_.InstallDecrypter(ENCRYPTION_HANDSHAKE,
+ std::move(bad_handshake_crypters.decrypter));
+ // clang-format off
+ unsigned char packet[] = {
+ // first coalesced packet
+ // public flags (long header with packet type HANDSHAKE and
+ // 4-byte packet number)
+ 0xE3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x08,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // source connection ID length
+ 0x00,
+ // long header packet length
+ 0x1E,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // second coalesced 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
+ 0x08,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // source connection ID length
+ 0x00,
+ // long header packet length
+ 0x1E,
+ // packet number
+ 0x12, 0x34, 0x56, 0x79,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'H', 'E', 'L', 'L',
+ 'O', '_', 'W', 'O',
+ 'R', 'L', 'D', '?',
+ };
+ // clang-format on
+ const size_t length_of_first_coalesced_packet = 46;
+
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error());
+
+ if (GetQuicRestartFlag(quic_framer_uses_undecryptable_upcall)) {
+ ASSERT_EQ(1u, visitor_.undecryptable_packets_.size());
+ ASSERT_EQ(1u, visitor_.undecryptable_decryption_levels_.size());
+ ASSERT_EQ(1u, visitor_.undecryptable_has_decryption_keys_.size());
+ // Make sure we only receive the first undecryptable packet and not the
+ // full packet including the second coalesced packet.
+ CompareCharArraysWithHexError(
+ "undecryptable packet", visitor_.undecryptable_packets_[0]->data(),
+ visitor_.undecryptable_packets_[0]->length(), AsChars(packet),
+ length_of_first_coalesced_packet);
+ EXPECT_EQ(ENCRYPTION_HANDSHAKE,
+ visitor_.undecryptable_decryption_levels_[0]);
+ EXPECT_TRUE(visitor_.undecryptable_has_decryption_keys_[0]);
+ } else {
+ EXPECT_EQ(0u, visitor_.undecryptable_packets_.size());
+ EXPECT_EQ(0u, visitor_.undecryptable_decryption_levels_.size());
+ EXPECT_EQ(0u, visitor_.undecryptable_has_decryption_keys_.size());
+ }
+
+ // Make sure the second coalesced packet is parsed correctly.
+ ASSERT_EQ(visitor_.coalesced_packets_.size(), 1u);
+ EXPECT_TRUE(framer_.ProcessPacket(*visitor_.coalesced_packets_[0].get()));
+
+ ASSERT_TRUE(visitor_.header_.get());
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("HELLO_WORLD?", visitor_.stream_frames_[0].get());
+}
+
TEST_P(QuicFramerTest, MismatchedCoalescedPacket) {
if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
return;
diff --git a/quic/core/quic_ietf_framer_test.cc b/quic/core/quic_ietf_framer_test.cc
index f0872b6..9a2b562 100644
--- a/quic/core/quic_ietf_framer_test.cc
+++ b/quic/core/quic_ietf_framer_test.cc
@@ -122,6 +122,10 @@
void OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) override {}
+ void OnUndecryptablePacket(const QuicEncryptedPacket& /*packet*/,
+ EncryptionLevel /*decryption_level*/,
+ bool /*has_decryption_key*/) override {}
+
bool OnStreamFrame(const QuicStreamFrame& /*frame*/) override { return true; }
bool OnCryptoFrame(const QuicCryptoFrame& /*frame*/) override { return true; }
diff --git a/quic/core/quic_packets.cc b/quic/core/quic_packets.cc
index 03b8420..80336d4 100644
--- a/quic/core/quic_packets.cc
+++ b/quic/core/quic_packets.cc
@@ -276,6 +276,11 @@
QuicData::QuicData(const char* buffer, size_t length, bool owns_buffer)
: buffer_(buffer), length_(length), owns_buffer_(owns_buffer) {}
+QuicData::QuicData(QuicStringPiece packet_data)
+ : buffer_(packet_data.data()),
+ length_(packet_data.length()),
+ owns_buffer_(false) {}
+
QuicData::~QuicData() {
if (owns_buffer_) {
delete[] const_cast<char*>(buffer_);
@@ -330,6 +335,9 @@
bool owns_buffer)
: QuicData(buffer, length, owns_buffer) {}
+QuicEncryptedPacket::QuicEncryptedPacket(QuicStringPiece data)
+ : QuicData(data) {}
+
std::unique_ptr<QuicEncryptedPacket> QuicEncryptedPacket::Clone() const {
char* buffer = new char[this->length()];
memcpy(buffer, this->data(), this->length());
diff --git a/quic/core/quic_packets.h b/quic/core/quic_packets.h
index 3af15ea..5c34b64 100644
--- a/quic/core/quic_packets.h
+++ b/quic/core/quic_packets.h
@@ -204,8 +204,13 @@
class QUIC_EXPORT_PRIVATE QuicData {
public:
+ // Creates a QuicData from a buffer and length. Does not own the buffer.
QuicData(const char* buffer, size_t length);
+ // Creates a QuicData from a buffer and length,
+ // optionally taking ownership of the buffer.
QuicData(const char* buffer, size_t length, bool owns_buffer);
+ // Creates a QuicData from a QuicStringPiece. Does not own the buffer.
+ QuicData(QuicStringPiece data);
QuicData(const QuicData&) = delete;
QuicData& operator=(const QuicData&) = delete;
virtual ~QuicData();
@@ -263,8 +268,16 @@
class QUIC_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData {
public:
+ // Creates a QuicEncryptedPacket from a buffer and length.
+ // Does not own the buffer.
QuicEncryptedPacket(const char* buffer, size_t length);
+ // Creates a QuicEncryptedPacket from a buffer and length,
+ // optionally taking ownership of the buffer.
QuicEncryptedPacket(const char* buffer, size_t length, bool owns_buffer);
+ // Creates a QuicEncryptedPacket from a QuicStringPiece.
+ // Does not own the buffer.
+ QuicEncryptedPacket(QuicStringPiece data);
+
QuicEncryptedPacket(const QuicEncryptedPacket&) = delete;
QuicEncryptedPacket& operator=(const QuicEncryptedPacket&) = delete;