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;
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc index f03f06b..ed8c551 100644 --- a/quic/test_tools/quic_test_utils.cc +++ b/quic/test_tools/quic_test_utils.cc
@@ -259,6 +259,11 @@ void NoOpFramerVisitor::OnCoalescedPacket( const QuicEncryptedPacket& /*packet*/) {} +void NoOpFramerVisitor::OnUndecryptablePacket( + const QuicEncryptedPacket& /*packet*/, + EncryptionLevel /*decryption_level*/, + bool /*has_decryption_key*/) {} + bool NoOpFramerVisitor::OnStreamFrame(const QuicStreamFrame& /*frame*/) { return true; }
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index 03adff2..84374ea 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -261,6 +261,10 @@ MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level)); MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header)); MOCK_METHOD1(OnCoalescedPacket, void(const QuicEncryptedPacket& packet)); + MOCK_METHOD3(OnUndecryptablePacket, + void(const QuicEncryptedPacket& packet, + EncryptionLevel decryption_level, + bool has_decryption_key)); MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame)); MOCK_METHOD1(OnCryptoFrame, bool(const QuicCryptoFrame& frame)); MOCK_METHOD2(OnAckFrameStart, bool(QuicPacketNumber, QuicTime::Delta)); @@ -314,6 +318,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/test_tools/simple_quic_framer.cc b/quic/test_tools/simple_quic_framer.cc index 547f6d2..ad7e7bb 100644 --- a/quic/test_tools/simple_quic_framer.cc +++ b/quic/test_tools/simple_quic_framer.cc
@@ -60,6 +60,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 { // Save a copy of the data so it is valid after the packet is processed. std::string* string_data =
diff --git a/quic/tools/quic_packet_printer_bin.cc b/quic/tools/quic_packet_printer_bin.cc index a87f10e..7738ea7 100644 --- a/quic/tools/quic_packet_printer_bin.cc +++ b/quic/tools/quic_packet_printer_bin.cc
@@ -88,6 +88,11 @@ void OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) override { std::cerr << "OnCoalescedPacket\n"; } + void OnUndecryptablePacket(const QuicEncryptedPacket& /*packet*/, + EncryptionLevel /*decryption_level*/, + bool /*has_decryption_key*/) override { + std::cerr << "OnUndecryptablePacket\n"; + } bool OnStreamFrame(const QuicStreamFrame& frame) override { std::cerr << "OnStreamFrame: " << frame; std::cerr << " data: { "