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_;