Add method to get reason why 0-RTT was accepted or rejected in QUIC This provides additional detail compared to QuicCryptoClientStreamBase::EarlyDataAccepted to indicate why early data was rejected (if it was). This change only adds client-side logging information - it doesn't change the behavior of a client. PiperOrigin-RevId: 330774692 Change-Id: I9f59ee5a4a64a5617b5ff9de5aba34156b88538f
diff --git a/quic/core/quic_crypto_client_handshaker.cc b/quic/core/quic_crypto_client_handshaker.cc index 7276937..b60b026 100644 --- a/quic/core/quic_crypto_client_handshaker.cc +++ b/quic/core/quic_crypto_client_handshaker.cc
@@ -128,6 +128,10 @@ return num_client_hellos_ == 1; } +ssl_early_data_reason_t QuicCryptoClientHandshaker::EarlyDataReason() const { + return early_data_reason_; +} + bool QuicCryptoClientHandshaker::ReceivedInchoateReject() const { QUIC_BUG_IF(!one_rtt_keys_available_); return num_client_hellos_ >= 3; @@ -290,9 +294,16 @@ // inchoate or subsequent hello. session()->config()->ToHandshakeMessage(&out, session()->transport_version()); - if (!cached->IsComplete(session()->connection()->clock()->WallNow()) || - session()->config()->HasClientRequestedIndependentOption( - kQNZR, session()->perspective())) { + bool fill_inchoate_client_hello = false; + if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { + early_data_reason_ = ssl_early_data_no_session_offered; + fill_inchoate_client_hello = true; + } else if (session()->config()->HasClientRequestedIndependentOption( + kQNZR, session()->perspective())) { + early_data_reason_ = ssl_early_data_disabled; + fill_inchoate_client_hello = true; + } + if (fill_inchoate_client_hello) { crypto_config_->FillInchoateClientHello( server_id_, session()->supported_versions().front(), cached, session()->connection()->random_generator(), @@ -356,6 +367,9 @@ /*latch_once_used=*/true); encryption_established_ = true; delegate_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + if (early_data_reason_ == ssl_early_data_unknown && num_client_hellos_ > 1) { + early_data_reason_ = ssl_early_data_peer_declined; + } } void QuicCryptoClientHandshaker::DoReceiveREJ( @@ -531,6 +545,9 @@ "unencrypted SHLO message"); return; } + if (num_client_hellos_ == 1) { + early_data_reason_ = ssl_early_data_accepted; + } std::string error_details; QuicErrorCode error = crypto_config_->ProcessServerHello(
diff --git a/quic/core/quic_crypto_client_handshaker.h b/quic/core/quic_crypto_client_handshaker.h index 90f011d..605318e 100644 --- a/quic/core/quic_crypto_client_handshaker.h +++ b/quic/core/quic_crypto_client_handshaker.h
@@ -40,6 +40,7 @@ int num_sent_client_hellos() const override; bool IsResumption() const override; bool EarlyDataAccepted() const override; + ssl_early_data_reason_t EarlyDataReason() const override; bool ReceivedInchoateReject() const override; int num_scup_messages_received() const override; std::string chlo_hash() const override; @@ -153,6 +154,8 @@ // connection has sent. int num_client_hellos_; + ssl_early_data_reason_t early_data_reason_ = ssl_early_data_unknown; + QuicCryptoClientConfig* const crypto_config_; // SHA-256 hash of the most recently sent CHLO.
diff --git a/quic/core/quic_crypto_client_stream.cc b/quic/core/quic_crypto_client_stream.cc index 62a261d..67a9a11 100644 --- a/quic/core/quic_crypto_client_stream.cc +++ b/quic/core/quic_crypto_client_stream.cc
@@ -71,6 +71,10 @@ return handshaker_->EarlyDataAccepted(); } +ssl_early_data_reason_t QuicCryptoClientStream::EarlyDataReason() const { + return handshaker_->EarlyDataReason(); +} + bool QuicCryptoClientStream::ReceivedInchoateReject() const { return handshaker_->ReceivedInchoateReject(); }
diff --git a/quic/core/quic_crypto_client_stream.h b/quic/core/quic_crypto_client_stream.h index be99fb2..4100a50 100644 --- a/quic/core/quic_crypto_client_stream.h +++ b/quic/core/quic_crypto_client_stream.h
@@ -9,6 +9,7 @@ #include <memory> #include <string> +#include "third_party/boringssl/src/include/openssl/ssl.h" #include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h" #include "net/third_party/quiche/src/quic/core/quic_config.h" @@ -53,6 +54,10 @@ // Returns true if early data (0-RTT) was accepted in the connection. virtual bool EarlyDataAccepted() const = 0; + // Returns the ssl_early_data_reason_t describing why 0-RTT was accepted or + // rejected. + virtual ssl_early_data_reason_t EarlyDataReason() const = 0; + // Returns true if the client received an inchoate REJ during the handshake, // extending the handshake by one round trip. This only applies for QUIC // crypto handshakes. The equivalent feature in IETF QUIC is a Retry packet, @@ -116,6 +121,10 @@ // Returns true if early data (0-RTT) was accepted in the connection. virtual bool EarlyDataAccepted() const = 0; + // Returns the ssl_early_data_reason_t describing why 0-RTT was accepted or + // rejected. + virtual ssl_early_data_reason_t EarlyDataReason() const = 0; + // Returns true if the client received an inchoate REJ during the handshake, // extending the handshake by one round trip. This only applies for QUIC // crypto handshakes. The equivalent feature in IETF QUIC is a Retry packet, @@ -203,6 +212,7 @@ int num_sent_client_hellos() const override; bool IsResumption() const override; bool EarlyDataAccepted() const override; + ssl_early_data_reason_t EarlyDataReason() const override; bool ReceivedInchoateReject() const override; int num_scup_messages_received() const override;
diff --git a/quic/core/quic_crypto_client_stream_test.cc b/quic/core/quic_crypto_client_stream_test.cc index 7f6d777..52791b3 100644 --- a/quic/core/quic_crypto_client_stream_test.cc +++ b/quic/core/quic_crypto_client_stream_test.cc
@@ -107,6 +107,7 @@ EXPECT_TRUE(stream()->encryption_established()); EXPECT_TRUE(stream()->one_rtt_keys_available()); EXPECT_FALSE(stream()->IsResumption()); + EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_no_session_offered); } TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { @@ -179,6 +180,7 @@ // Check that a client hello was sent. ASSERT_EQ(1u, connection_->encrypted_packets_.size()); EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); + EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_disabled); } TEST_F(QuicCryptoClientStreamTest, ClockSkew) {
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc index 46bf285..fa19005 100644 --- a/quic/core/tls_client_handshaker.cc +++ b/quic/core/tls_client_handshaker.cc
@@ -295,6 +295,10 @@ return SSL_early_data_accepted(ssl()) == 1; } +ssl_early_data_reason_t TlsClientHandshaker::EarlyDataReason() const { + return SSL_get_early_data_reason(ssl()); +} + bool TlsClientHandshaker::ReceivedInchoateReject() const { QUIC_BUG_IF(!one_rtt_keys_available_); // REJ messages are a QUIC crypto feature, so TLS always returns false.
diff --git a/quic/core/tls_client_handshaker.h b/quic/core/tls_client_handshaker.h index bf05ca8..2a09fb8 100644 --- a/quic/core/tls_client_handshaker.h +++ b/quic/core/tls_client_handshaker.h
@@ -46,6 +46,7 @@ int num_sent_client_hellos() const override; bool IsResumption() const override; bool EarlyDataAccepted() const override; + ssl_early_data_reason_t EarlyDataReason() const override; bool ReceivedInchoateReject() const override; int num_scup_messages_received() const override; std::string chlo_hash() const override;
diff --git a/quic/core/tls_client_handshaker_test.cc b/quic/core/tls_client_handshaker_test.cc index a0a6ea7..a4bd87e 100644 --- a/quic/core/tls_client_handshaker_test.cc +++ b/quic/core/tls_client_handshaker_test.cc
@@ -376,6 +376,8 @@ EXPECT_TRUE(stream()->one_rtt_keys_available()); EXPECT_FALSE(stream()->IsResumption()); EXPECT_FALSE(stream()->EarlyDataAccepted()); + EXPECT_EQ(stream()->EarlyDataReason(), + ssl_early_data_unsupported_for_session); } TEST_P(TlsClientHandshakerTest, ZeroRttResumption) { @@ -413,6 +415,7 @@ EXPECT_TRUE(stream()->one_rtt_keys_available()); EXPECT_TRUE(stream()->IsResumption()); EXPECT_TRUE(stream()->EarlyDataAccepted()); + EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_accepted); } TEST_P(TlsClientHandshakerTest, ZeroRttRejection) { @@ -461,6 +464,7 @@ EXPECT_TRUE(stream()->one_rtt_keys_available()); EXPECT_TRUE(stream()->IsResumption()); EXPECT_FALSE(stream()->EarlyDataAccepted()); + EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_peer_declined); } TEST_P(TlsClientHandshakerTest, ZeroRttAndResumptionRejection) { @@ -509,6 +513,7 @@ EXPECT_TRUE(stream()->one_rtt_keys_available()); EXPECT_FALSE(stream()->IsResumption()); EXPECT_FALSE(stream()->EarlyDataAccepted()); + EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_session_not_resumed); } TEST_P(TlsClientHandshakerTest, ClientSendsNoSNI) { @@ -606,6 +611,7 @@ EXPECT_TRUE(stream()->encryption_established()); EXPECT_TRUE(stream()->one_rtt_keys_available()); EXPECT_FALSE(stream()->EarlyDataAccepted()); + EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_alpn_mismatch); } TEST_P(TlsClientHandshakerTest, InvalidSNI) {