Reject CHLOs without server nonce in Google QUIC (technically disable 0-RTT) by changing --gfe2_reloadable_flag_quic_require_handshake_confirmation from external default false flag to a regular feature flag and let it go through the feature flag roll out process.
Protected by FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation.
PiperOrigin-RevId: 542324361
diff --git a/quiche/quic/core/crypto/crypto_server_test.cc b/quiche/quic/core/crypto/crypto_server_test.cc
index 56f7283..e987190 100644
--- a/quiche/quic/core/crypto/crypto_server_test.cc
+++ b/quiche/quic/core/crypto/crypto_server_test.cc
@@ -78,7 +78,8 @@
std::vector<TestParams> params;
// Start with all versions, remove highest on each iteration.
- ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ ParsedQuicVersionVector supported_versions =
+ AllSupportedVersionsWithQuicCrypto();
while (!supported_versions.empty()) {
params.push_back({supported_versions});
supported_versions.erase(supported_versions.begin());
@@ -566,8 +567,12 @@
CheckRejectTag();
const HandshakeFailureReason kRejectReasons1[] = {
- CLIENT_NONCE_INVALID_FAILURE};
- CheckRejectReasons(kRejectReasons1, ABSL_ARRAYSIZE(kRejectReasons1));
+ CLIENT_NONCE_INVALID_FAILURE, SERVER_NONCE_REQUIRED_FAILURE};
+ CheckRejectReasons(
+ kRejectReasons1,
+ (GetQuicReloadableFlag(quic_require_handshake_confirmation)
+ ? ABSL_ARRAYSIZE(kRejectReasons1)
+ : 1));
}
}
@@ -658,7 +663,14 @@
CheckRejectTag();
const HandshakeFailureReason kRejectReasons[] = {
SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE};
- CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ const HandshakeFailureReason kRejectReasons1[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, SERVER_NONCE_REQUIRED_FAILURE};
+ CheckRejectReasons((GetQuicReloadableFlag(quic_require_handshake_confirmation)
+ ? kRejectReasons1
+ : kRejectReasons),
+ (GetQuicReloadableFlag(quic_require_handshake_confirmation)
+ ? ABSL_ARRAYSIZE(kRejectReasons1)
+ : ABSL_ARRAYSIZE(kRejectReasons)));
}
TEST_P(CryptoServerTest, CorruptSourceAddressTokenIsStillAccepted) {
@@ -678,6 +690,16 @@
config_.set_validate_source_address_token(false);
ShouldSucceed(msg);
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_NONCE_REQUIRED_FAILURE};
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ absl::string_view server_nonce;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &server_nonce));
+ msg.SetStringPiece(kServerNonceTag, server_nonce);
+ ShouldSucceed(msg);
+ }
EXPECT_EQ(kSHLO, out_.tag());
}
@@ -699,7 +721,15 @@
CheckRejectTag();
const HandshakeFailureReason kRejectReasons[] = {
SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE};
- CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ const HandshakeFailureReason kRejectReasons1[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE,
+ SERVER_NONCE_REQUIRED_FAILURE};
+ CheckRejectReasons((GetQuicReloadableFlag(quic_require_handshake_confirmation)
+ ? kRejectReasons1
+ : kRejectReasons),
+ (GetQuicReloadableFlag(quic_require_handshake_confirmation)
+ ? ABSL_ARRAYSIZE(kRejectReasons1)
+ : ABSL_ARRAYSIZE(kRejectReasons)));
}
TEST_P(CryptoServerTest, CorruptMultipleTags) {
@@ -727,8 +757,7 @@
}
TEST_P(CryptoServerTest, NoServerNonce) {
- // When no server nonce is present and no strike register is configured,
- // the CHLO should be rejected.
+ // When no server nonce is present the CHLO should be rejected.
CryptoHandshakeMessage msg =
crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
{"AEAD", "AESG"},
@@ -744,10 +773,14 @@
ShouldSucceed(msg);
- // Even without a server nonce, this ClientHello should be accepted in
- // version 33.
- ASSERT_EQ(kSHLO, out_.tag());
- CheckServerHello(out_);
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ CheckRejectTag();
+ } else {
+ // Even without a server nonce, this ClientHello should be accepted in
+ // version 33.
+ ASSERT_EQ(kSHLO, out_.tag());
+ CheckServerHello(out_);
+ }
}
TEST_P(CryptoServerTest, ProofForSuppliedServerConfig) {
@@ -772,7 +805,15 @@
CheckRejectTag();
const HandshakeFailureReason kRejectReasons[] = {
SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE};
- CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ const HandshakeFailureReason kRejectReasons1[] = {
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ SERVER_NONCE_REQUIRED_FAILURE};
+ CheckRejectReasons((GetQuicReloadableFlag(quic_require_handshake_confirmation)
+ ? kRejectReasons1
+ : kRejectReasons),
+ (GetQuicReloadableFlag(quic_require_handshake_confirmation)
+ ? ABSL_ARRAYSIZE(kRejectReasons1)
+ : ABSL_ARRAYSIZE(kRejectReasons)));
absl::string_view cert, proof, scfg_str;
EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
@@ -829,10 +870,23 @@
ShouldSucceed(msg);
- const HandshakeFailureReason kRejectReasons[] = {
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_NONCE_REQUIRED_FAILURE};
+
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+
+ absl::string_view server_nonce;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &server_nonce));
+ msg.SetStringPiece(kServerNonceTag, server_nonce);
+
+ ShouldSucceed(msg);
+ }
+
+ const HandshakeFailureReason kRejectReasons1[] = {
INVALID_EXPECTED_LEAF_CERTIFICATE};
- CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+ CheckRejectReasons(kRejectReasons1, ABSL_ARRAYSIZE(kRejectReasons1));
}
TEST_P(CryptoServerTest, ValidXlct) {
@@ -854,6 +908,20 @@
config_.set_replay_protection(false);
ShouldSucceed(msg);
+
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_NONCE_REQUIRED_FAILURE};
+
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+
+ absl::string_view server_nonce;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &server_nonce));
+ msg.SetStringPiece(kServerNonceTag, server_nonce);
+
+ ShouldSucceed(msg);
+ }
+
EXPECT_EQ(kSHLO, out_.tag());
}
@@ -876,9 +944,21 @@
config_.set_replay_protection(false);
ShouldSucceed(msg);
+ absl::string_view nonce;
+
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_NONCE_REQUIRED_FAILURE};
+
+ CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
+
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &nonce));
+ msg.SetStringPiece(kServerNonceTag, nonce);
+
+ ShouldSucceed(msg);
+ }
EXPECT_EQ(kSHLO, out_.tag());
- absl::string_view nonce;
EXPECT_TRUE(out_.GetStringPiece(kServerNonceTag, &nonce));
}
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 6c9451a..f9a89d7 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -1895,6 +1895,10 @@
// Send a request and then disconnect. This prepares the client to attempt
// a 0-RTT handshake for the next request.
ASSERT_TRUE(Initialize());
+ if (!version_.UsesTls() &&
+ GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ return;
+ }
std::string body(20480, 'a');
Http2HeaderBlock headers;
@@ -1950,6 +1954,10 @@
// Regression test for b/168020146.
TEST_P(EndToEndTest, MultipleZeroRtt) {
ASSERT_TRUE(Initialize());
+ if (!version_.UsesTls() &&
+ GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ return;
+ }
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
QuicSpdyClientSession* client_session = GetClientSession();
@@ -1991,6 +1999,10 @@
// Send a request and then disconnect. This prepares the client to attempt
// a 0-RTT handshake for the next request.
ASSERT_TRUE(Initialize());
+ if (!version_.UsesTls() &&
+ GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ return;
+ }
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
QuicSpdyClientSession* client_session = GetClientSession();
@@ -2067,8 +2079,12 @@
client_session = GetClientSession();
ASSERT_TRUE(client_session);
- EXPECT_TRUE(client_session->EarlyDataAccepted());
- EXPECT_TRUE(client_->client()->EarlyDataAccepted());
+ EXPECT_EQ((version_.UsesTls() ||
+ !GetQuicReloadableFlag(quic_require_handshake_confirmation)),
+ client_session->EarlyDataAccepted());
+ EXPECT_EQ((version_.UsesTls() ||
+ !GetQuicReloadableFlag(quic_require_handshake_confirmation)),
+ client_->client()->EarlyDataAccepted());
client_->Disconnect();
@@ -5788,6 +5804,10 @@
TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) {
ASSERT_TRUE(Initialize());
+ if (!version_.UsesTls() &&
+ GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ return;
+ }
// Finish one request to make sure handshake established.
client_->SendSynchronousRequest("/foo");
// Disconnect for next 0-rtt request.
diff --git a/quiche/quic/core/quic_crypto_server_stream_test.cc b/quiche/quic/core/quic_crypto_server_stream_test.cc
index 6f743d9..516a25f 100644
--- a/quiche/quic/core/quic_crypto_server_stream_test.cc
+++ b/quiche/quic/core/quic_crypto_server_stream_test.cc
@@ -210,6 +210,10 @@
InitializeFakeClient();
AdvanceHandshakeWithFakeClient();
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 0,
+ server_connection_, server_stream(), 0);
+ }
EXPECT_TRUE(server_stream()->encryption_established());
EXPECT_TRUE(server_stream()->one_rtt_keys_available());
EXPECT_EQ(ENCRYPTION_FORWARD_SECURE,
@@ -243,7 +247,9 @@
crypto_test_utils::CommunicateHandshakeMessages(
client_connection_, client_stream(), server_connection_, server_stream());
- EXPECT_EQ(1, client_stream()->num_sent_client_hellos());
+ EXPECT_EQ(
+ (GetQuicReloadableFlag(quic_require_handshake_confirmation) ? 2 : 1),
+ client_stream()->num_sent_client_hellos());
EXPECT_TRUE(server_stream()->ResumptionAttempted());
}
@@ -301,6 +307,10 @@
InitializeServer();
InitializeFakeClient();
AdvanceHandshakeWithFakeClient();
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation)) {
+ crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 0,
+ server_connection_, server_stream(), 0);
+ }
// Send a SCUP message and ensure that the client was able to verify it.
EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0);
diff --git a/quiche/quic/test_tools/crypto_test_utils_test.cc b/quiche/quic/test_tools/crypto_test_utils_test.cc
index 0522175..51bcc9e 100644
--- a/quiche/quic/test_tools/crypto_test_utils_test.cc
+++ b/quiche/quic/test_tools/crypto_test_utils_test.cc
@@ -54,6 +54,9 @@
return std::make_unique<ValidateClientHelloCallback>(this);
}
+ absl::string_view server_nonce() { return server_nonce_; }
+ bool chlo_accepted() const { return chlo_accepted_; }
+
private:
void ValidateClientHelloDone(
const quiche::QuicheReferenceCountedPointer<
@@ -89,9 +92,16 @@
}
void ProcessClientHelloDone(std::unique_ptr<CryptoHandshakeMessage> message) {
- // Verify output is a SHLO.
- EXPECT_EQ(message->tag(), kSHLO)
- << "Fail to pass validation. Get " << message->DebugString();
+ if (message->tag() == kSHLO) {
+ chlo_accepted_ = true;
+ } else {
+ QUIC_LOG(INFO) << "Fail to pass validation. Get "
+ << message->DebugString();
+ chlo_accepted_ = false;
+ EXPECT_EQ(1u, result_->info.reject_reasons.size());
+ EXPECT_EQ(SERVER_NONCE_REQUIRED_FAILURE, result_->info.reject_reasons[0]);
+ server_nonce_ = result_->info.server_nonce;
+ }
}
QuicCryptoServerConfig* crypto_config_;
@@ -107,6 +117,8 @@
result_;
const ParsedQuicVersion version_;
+ bool chlo_accepted_ = false;
+ absl::string_view server_nonce_;
};
class CryptoTestUtilsTest : public QuicTest {};
@@ -181,6 +193,21 @@
crypto_config.ValidateClientHello(
full_chlo, client_addr, server_addr, transport_version, &clock,
signed_config, shlo_verifier.GetValidateClientHelloCallback());
+ ASSERT_EQ(shlo_verifier.chlo_accepted(),
+ !GetQuicReloadableFlag(quic_require_handshake_confirmation));
+ if (!shlo_verifier.chlo_accepted()) {
+ ShloVerifier shlo_verifier2(
+ &crypto_config, server_addr, client_addr, &clock, signed_config,
+ &compressed_certs_cache,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, transport_version));
+ full_chlo.SetStringPiece(
+ kServerNonceTag,
+ "#" + absl::BytesToHexString(shlo_verifier.server_nonce()));
+ crypto_config.ValidateClientHello(
+ full_chlo, client_addr, server_addr, transport_version, &clock,
+ signed_config, shlo_verifier2.GetValidateClientHelloCallback());
+ EXPECT_TRUE(shlo_verifier2.chlo_accepted()) << full_chlo.DebugString();
+ }
}
} // namespace test