Internal change PiperOrigin-RevId: 444933854
diff --git a/quiche/quic/core/http/quic_spdy_session_test.cc b/quiche/quic/core/http/quic_spdy_session_test.cc index 06b2959..283fdf5 100644 --- a/quiche/quic/core/http/quic_spdy_session_test.cc +++ b/quiche/quic/core/http/quic_spdy_session_test.cc
@@ -195,6 +195,10 @@ void OnConnectionClosed(QuicErrorCode /*error*/, ConnectionCloseSource /*source*/) override {} SSL* GetSsl() const override { return nullptr; } + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override { + return level != ENCRYPTION_ZERO_RTT; + } bool ExportKeyingMaterial(absl::string_view /*label*/, absl::string_view /*context*/,
diff --git a/quiche/quic/core/http/quic_spdy_stream_test.cc b/quiche/quic/core/http/quic_spdy_stream_test.cc index 33b0588..281f359 100644 --- a/quiche/quic/core/http/quic_spdy_stream_test.cc +++ b/quiche/quic/core/http/quic_spdy_stream_test.cc
@@ -187,6 +187,11 @@ SSL* GetSsl() const override { return nullptr; } + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override { + return level != ENCRYPTION_ZERO_RTT; + } + private: using QuicCryptoStream::session;
diff --git a/quiche/quic/core/quic_crypto_client_handshaker.cc b/quiche/quic/core/quic_crypto_client_handshaker.cc index aa963d5..6a9fa6c 100644 --- a/quiche/quic/core/quic_crypto_client_handshaker.cc +++ b/quiche/quic/core/quic_crypto_client_handshaker.cc
@@ -144,6 +144,11 @@ return encryption_established_; } +bool QuicCryptoClientHandshaker::IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel /*level*/) const { + return true; +} + bool QuicCryptoClientHandshaker::one_rtt_keys_available() const { return one_rtt_keys_available_; }
diff --git a/quiche/quic/core/quic_crypto_client_handshaker.h b/quiche/quic/core/quic_crypto_client_handshaker.h index 45d8016..9c19f89 100644 --- a/quiche/quic/core/quic_crypto_client_handshaker.h +++ b/quiche/quic/core/quic_crypto_client_handshaker.h
@@ -43,6 +43,8 @@ int num_scup_messages_received() const override; std::string chlo_hash() const override; bool encryption_established() const override; + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override; bool one_rtt_keys_available() const override; const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override;
diff --git a/quiche/quic/core/quic_crypto_client_stream.cc b/quiche/quic/core/quic_crypto_client_stream.cc index 60d49ed..a241ecb 100644 --- a/quiche/quic/core/quic_crypto_client_stream.cc +++ b/quiche/quic/core/quic_crypto_client_stream.cc
@@ -164,4 +164,9 @@ return tls_handshaker_ == nullptr ? nullptr : tls_handshaker_->ssl(); } +bool QuicCryptoClientStream::IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const { + return handshaker_->IsCryptoFrameExpectedForEncryptionLevel(level); +} + } // namespace quic
diff --git a/quiche/quic/core/quic_crypto_client_stream.h b/quiche/quic/core/quic_crypto_client_stream.h index 96abfec..d3da4db 100644 --- a/quiche/quic/core/quic_crypto_client_stream.h +++ b/quiche/quic/core/quic_crypto_client_stream.h
@@ -169,6 +169,10 @@ // for the connection. virtual bool encryption_established() const = 0; + // Returns true if receiving CRYPTO_FRAME at encryption `level` is expected. + virtual bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const = 0; + // Returns true once 1RTT keys are available. virtual bool one_rtt_keys_available() const = 0; @@ -283,6 +287,8 @@ override; std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override; SSL* GetSsl() const override; + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override; bool ExportKeyingMaterial(absl::string_view label, absl::string_view context, size_t result_len, std::string* result) override; std::string chlo_hash() const;
diff --git a/quiche/quic/core/quic_crypto_server_stream.cc b/quiche/quic/core/quic_crypto_server_stream.cc index cca4890..11a5728 100644 --- a/quiche/quic/core/quic_crypto_server_stream.cc +++ b/quiche/quic/core/quic_crypto_server_stream.cc
@@ -12,6 +12,7 @@ #include "openssl/sha.h" #include "quiche/quic/platform/api/quic_flag_utils.h" #include "quiche/quic/platform/api/quic_testvalue.h" +#include "quiche/common/platform/api/quiche_logging.h" #include "quiche/common/quiche_text_utils.h" namespace quic { @@ -526,4 +527,9 @@ SSL* QuicCryptoServerStream::GetSsl() const { return nullptr; } +bool QuicCryptoServerStream::IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel /*level*/) const { + return true; +} + } // namespace quic
diff --git a/quiche/quic/core/quic_crypto_server_stream.h b/quiche/quic/core/quic_crypto_server_stream.h index 2f7fb46..5bdfb54 100644 --- a/quiche/quic/core/quic_crypto_server_stream.h +++ b/quiche/quic/core/quic_crypto_server_stream.h
@@ -72,6 +72,8 @@ override; std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override; SSL* GetSsl() const override; + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override; // From QuicCryptoHandshaker void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
diff --git a/quiche/quic/core/quic_crypto_stream.cc b/quiche/quic/core/quic_crypto_stream.cc index a669982..33eb543 100644 --- a/quiche/quic/core/quic_crypto_stream.cc +++ b/quiche/quic/core/quic_crypto_stream.cc
@@ -6,10 +6,12 @@ #include <string> +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "quiche/quic/core/crypto/crypto_handshake.h" #include "quiche/quic/core/crypto/crypto_utils.h" +#include "quiche/quic/core/frames/quic_crypto_frame.h" #include "quiche/quic/core/quic_connection.h" #include "quiche/quic/core/quic_session.h" #include "quiche/quic/core/quic_types.h" @@ -74,6 +76,12 @@ !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 shouldn't receive CRYPTO frames"; EncryptionLevel level = session()->connection()->last_decrypted_level(); + if (!IsCryptoFrameExpectedForEncryptionLevel(level)) { + OnUnrecoverableError( + IETF_QUIC_PROTOCOL_VIOLATION, + absl::StrCat("CRYPTO_FRAME is unexpectedly received at level ", level)); + return; + } substreams_[level].sequencer.OnCryptoFrame(frame); EncryptionLevel frame_level = level; if (substreams_[level].sequencer.NumBytesBuffered() >
diff --git a/quiche/quic/core/quic_crypto_stream.h b/quiche/quic/core/quic_crypto_stream.h index 9e9b870..be61a82 100644 --- a/quiche/quic/core/quic_crypto_stream.h +++ b/quiche/quic/core/quic_crypto_stream.h
@@ -240,6 +240,11 @@ return &substreams_[level].sequencer; } + // Called by OnCryptoFrame to check if a CRYPTO frame is received at an + // expected `level`. + virtual bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const = 0; + private: // Data sent and received in CRYPTO frames is sent at multiple encryption // levels. Some of the state for the single logical crypto stream is split
diff --git a/quiche/quic/core/quic_crypto_stream_test.cc b/quiche/quic/core/quic_crypto_stream_test.cc index 6df4983..7e27d77 100644 --- a/quiche/quic/core/quic_crypto_stream_test.cc +++ b/quiche/quic/core/quic_crypto_stream_test.cc
@@ -17,6 +17,7 @@ #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/quic/platform/api/quic_test.h" #include "quiche/quic/test_tools/crypto_test_utils.h" +#include "quiche/quic/test_tools/quic_connection_peer.h" #include "quiche/quic/test_tools/quic_stream_peer.h" #include "quiche/quic/test_tools/quic_test_utils.h" @@ -95,6 +96,11 @@ } SSL* GetSsl() const override { return nullptr; } + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override { + return level != ENCRYPTION_ZERO_RTT; + } + private: quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; std::vector<CryptoHandshakeMessage> messages_; @@ -674,6 +680,20 @@ QuicCryptoFrame(ENCRYPTION_INITIAL, offset, large_frame)); } +TEST_F(QuicCryptoStreamTest, CloseConnectionWithZeroRttCryptoFrame) { + if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { + return; + } + + EXPECT_CALL(*connection_, + CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, _, _)); + + test::QuicConnectionPeer::SetLastDecryptedLevel(connection_, + ENCRYPTION_ZERO_RTT); + QuicStreamOffset offset = 1; + stream_->OnCryptoFrame(QuicCryptoFrame(ENCRYPTION_ZERO_RTT, offset, "data")); +} + TEST_F(QuicCryptoStreamTest, RetransmitCryptoFramesAndPartialWrite) { if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) { return;
diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc index b0f5018..2f8c5c1 100644 --- a/quiche/quic/core/quic_framer.cc +++ b/quiche/quic/core/quic_framer.cc
@@ -3121,6 +3121,8 @@ // static bool QuicFramer::IsIetfFrameTypeExpectedForEncryptionLevel( uint64_t frame_type, EncryptionLevel level) { + // IETF_CRYPTO is allowed for any level here and is separately checked in + // QuicCryptoStream::OnCryptoFrame. switch (level) { case ENCRYPTION_INITIAL: case ENCRYPTION_HANDSHAKE: @@ -3132,7 +3134,7 @@ case ENCRYPTION_ZERO_RTT: return !(frame_type == IETF_ACK || frame_type == IETF_ACK_ECN || frame_type == IETF_ACK_RECEIVE_TIMESTAMPS || - frame_type == IETF_CRYPTO || frame_type == IETF_HANDSHAKE_DONE || + frame_type == IETF_HANDSHAKE_DONE || frame_type == IETF_NEW_TOKEN || frame_type == IETF_PATH_RESPONSE || frame_type == IETF_RETIRE_CONNECTION_ID);
diff --git a/quiche/quic/core/quic_session_test.cc b/quiche/quic/core/quic_session_test.cc index ddda101..ccaa29d 100644 --- a/quiche/quic/core/quic_session_test.cc +++ b/quiche/quic/core/quic_session_test.cc
@@ -181,6 +181,11 @@ SSL* GetSsl() const override { return nullptr; } + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override { + return level != ENCRYPTION_ZERO_RTT; + } + private: using QuicCryptoStream::session;
diff --git a/quiche/quic/core/tls_client_handshaker.cc b/quiche/quic/core/tls_client_handshaker.cc index 90a9d74..fccdb20 100644 --- a/quiche/quic/core/tls_client_handshaker.cc +++ b/quiche/quic/core/tls_client_handshaker.cc
@@ -357,6 +357,11 @@ return encryption_established_; } +bool TlsClientHandshaker::IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const { + return level != ENCRYPTION_ZERO_RTT; +} + bool TlsClientHandshaker::one_rtt_keys_available() const { return state_ >= HANDSHAKE_COMPLETE; }
diff --git a/quiche/quic/core/tls_client_handshaker.h b/quiche/quic/core/tls_client_handshaker.h index 15727e1..d3c6312 100644 --- a/quiche/quic/core/tls_client_handshaker.h +++ b/quiche/quic/core/tls_client_handshaker.h
@@ -54,6 +54,8 @@ // From QuicCryptoClientStream::HandshakerInterface and TlsHandshaker bool encryption_established() const override; + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override; bool one_rtt_keys_available() const override; const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override;
diff --git a/quiche/quic/core/tls_server_handshaker.cc b/quiche/quic/core/tls_server_handshaker.cc index ce63767..7a17acc 100644 --- a/quiche/quic/core/tls_server_handshaker.cc +++ b/quiche/quic/core/tls_server_handshaker.cc
@@ -1134,4 +1134,9 @@ SSL* TlsServerHandshaker::GetSsl() const { return ssl(); } +bool TlsServerHandshaker::IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const { + return level != ENCRYPTION_ZERO_RTT; +} + } // namespace quic
diff --git a/quiche/quic/core/tls_server_handshaker.h b/quiche/quic/core/tls_server_handshaker.h index 6385eda..fde73c6 100644 --- a/quiche/quic/core/tls_server_handshaker.h +++ b/quiche/quic/core/tls_server_handshaker.h
@@ -71,6 +71,8 @@ bool ExportKeyingMaterial(absl::string_view label, absl::string_view context, size_t result_len, std::string* result) override; SSL* GetSsl() const override; + bool IsCryptoFrameExpectedForEncryptionLevel( + EncryptionLevel level) const override; // From QuicCryptoServerStreamBase and TlsHandshaker ssl_early_data_reason_t EarlyDataReason() const override;
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h index e074008..2a3b2b0 100644 --- a/quiche/quic/test_tools/quic_test_utils.h +++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -855,6 +855,10 @@ return false; } SSL* GetSsl() const override { return nullptr; } + bool IsCryptoFrameExpectedForEncryptionLevel( + quic::EncryptionLevel level) const override { + return level != ENCRYPTION_ZERO_RTT; + } private: quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;