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) {