Expose more TLS session properties in QuicCryptoStream and preserve them across handshaker resets.

PiperOrigin-RevId: 922120567
diff --git a/quiche/quic/core/quic_crypto_stream.cc b/quiche/quic/core/quic_crypto_stream.cc
index e8f54bb..aaf399f 100644
--- a/quiche/quic/core/quic_crypto_stream.cc
+++ b/quiche/quic/core/quic_crypto_stream.cc
@@ -617,7 +617,7 @@
   QUICHE_CODE_COUNT(quic_crypto_stream_reset_crypto_substreams);
 }
 
-absl::string_view QuicCryptoStream::sni() const {
+absl::string_view QuicCryptoStream::Sni() const {
   if (!VersionIsIetfQuic(session()->transport_version())) {
     return {};
   }
@@ -632,7 +632,7 @@
   return {};
 }
 
-const SSL_CIPHER* absl_nullable QuicCryptoStream::ciphersuite() const {
+const SSL_CIPHER* absl_nullable QuicCryptoStream::Ciphersuite() const {
   QUICHE_DCHECK(VersionIsIetfQuic(session()->transport_version()));
   SSL* ssl = GetSsl();
   if (ssl == nullptr) {
@@ -641,7 +641,24 @@
   return SSL_get_current_cipher(ssl);
 }
 
-absl::string_view QuicCryptoStream::alpn() const {
+uint16_t QuicCryptoStream::CiphersuiteId() const {
+  const SSL_CIPHER* cipher = Ciphersuite();
+  if (cipher == nullptr) {
+    return 0xffff;
+  }
+  return static_cast<uint16_t>(SSL_CIPHER_get_id(cipher));
+}
+
+absl::string_view QuicCryptoStream::CiphersuiteString() const {
+  const SSL_CIPHER* cipher = Ciphersuite();
+  if (cipher == nullptr) {
+    return {};
+  }
+
+  return SSL_CIPHER_get_name(cipher);
+}
+
+absl::string_view QuicCryptoStream::Alpn() const {
   QUICHE_DCHECK(VersionIsIetfQuic(session()->transport_version()));
   SSL* ssl = GetSsl();
   if (ssl == nullptr) {
@@ -656,5 +673,27 @@
   return absl::string_view(reinterpret_cast<const char*>(data), data_len);
 }
 
+uint16_t QuicCryptoStream::TlsGroupId() const {
+  QUICHE_DCHECK(VersionIsIetfQuic(session()->transport_version()));
+  SSL* ssl = GetSsl();
+  if (ssl == nullptr) {
+    return 0;
+  }
+  return SSL_get_group_id(ssl);
+}
+
+absl::string_view QuicCryptoStream::TlsGroupString() const {
+  const char* group = SSL_get_group_name(TlsGroupId());
+  if (group == nullptr) {
+    return {};
+  }
+  return group;
+}
+
+// IETF QUIC only uses TLS 1.3.
+absl::string_view QuicCryptoStream::TlsVersion() const {
+  return "TLS_VERSION_1_3";
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/quiche/quic/core/quic_crypto_stream.h b/quiche/quic/core/quic_crypto_stream.h
index ab93451..0576e3d 100644
--- a/quiche/quic/core/quic_crypto_stream.h
+++ b/quiche/quic/core/quic_crypto_stream.h
@@ -178,13 +178,23 @@
   // Note this method may return a nullptr after the TLS handshake is completed.
   virtual SSL* GetSsl() const = 0;
 
-  virtual absl::string_view sni() const;
+  virtual absl::string_view Sni() const;
 
   // These methods should only be called with IETF QUIC.
   // Returns the cipher suite in use.
-  virtual const SSL_CIPHER* absl_nullable ciphersuite() const;
+  virtual const SSL_CIPHER* absl_nullable Ciphersuite() const;
   // Returns the ALPN in use.
-  virtual absl::string_view alpn() const;
+  virtual absl::string_view Alpn() const;
+  // Returns the TLS group ID in use.
+  virtual uint16_t TlsGroupId() const;
+  // Returns the ciphersuite ID in use.
+  uint16_t CiphersuiteId() const;
+  // Returns the ciphersuite string in use.
+  absl::string_view CiphersuiteString() const;
+  // Returns the TLS group string in use.
+  absl::string_view TlsGroupString() const;
+  // Returns the TLS version in use.
+  absl::string_view TlsVersion() const;
 
   // Called to cancel retransmission of unencrypted crypto stream data.
   void NeuterUnencryptedStreamData();
diff --git a/quiche/quic/core/tls_client_handshaker_test.cc b/quiche/quic/core/tls_client_handshaker_test.cc
index 226c9cd..6d5c114 100644
--- a/quiche/quic/core/tls_client_handshaker_test.cc
+++ b/quiche/quic/core/tls_client_handshaker_test.cc
@@ -318,9 +318,12 @@
   EXPECT_FALSE(stream()->MatchedTrustAnchorIdForTesting());
   EXPECT_TRUE(stream()->one_rtt_keys_available());
   EXPECT_FALSE(stream()->IsResumption());
-  EXPECT_EQ(stream()->sni(), kServerHostname);
-  EXPECT_EQ(stream()->alpn(), AlpnForVersion(stream()->version()));
-  EXPECT_NE(stream()->ciphersuite(), nullptr);
+  EXPECT_EQ(stream()->Sni(), kServerHostname);
+  EXPECT_EQ(stream()->Alpn(), AlpnForVersion(stream()->version()));
+  EXPECT_NE(stream()->Ciphersuite(), nullptr);
+  EXPECT_NE(stream()->TlsGroupId(), 0);
+  EXPECT_FALSE(stream()->TlsGroupString().empty());
+  EXPECT_EQ(stream()->TlsVersion(), "TLS_VERSION_1_3");
 }
 
 // Test that the connection succeeds when the client sends a server padding
diff --git a/quiche/quic/core/tls_server_handshaker.cc b/quiche/quic/core/tls_server_handshaker.cc
index 8294a63..065edbe 100644
--- a/quiche/quic/core/tls_server_handshaker.cc
+++ b/quiche/quic/core/tls_server_handshaker.cc
@@ -1320,13 +1320,44 @@
   cached_ssl_info_.emplace(CachedSSLInfo{
       .is_resumption = IsResumption(),
       .is_zero_rtt = IsZeroRtt(),
+      .tls_group_id = TlsGroupId(),
       .early_data_reason = EarlyDataReason(),
       .cipher = GetCipher(),
+      .alpn = std::string(Alpn()),
+      .sni = std::string(Sni()),
   });
   tls_connection_.ResetSsl();
   ResetCryptoSubstreams();
 }
 
+absl::string_view TlsServerHandshaker::Sni() const {
+  if (cached_ssl_info_.has_value()) {
+    return cached_ssl_info_->sni;
+  }
+  return QuicCryptoStream::Sni();
+}
+
+const SSL_CIPHER* TlsServerHandshaker::Ciphersuite() const {
+  if (cached_ssl_info_.has_value()) {
+    return cached_ssl_info_->cipher;
+  }
+  return QuicCryptoStream::Ciphersuite();
+}
+
+absl::string_view TlsServerHandshaker::Alpn() const {
+  if (cached_ssl_info_.has_value()) {
+    return cached_ssl_info_->alpn;
+  }
+  return QuicCryptoStream::Alpn();
+}
+
+uint16_t TlsServerHandshaker::TlsGroupId() const {
+  if (cached_ssl_info_.has_value()) {
+    return cached_ssl_info_->tls_group_id;
+  }
+  return QuicCryptoStream::TlsGroupId();
+}
+
 bool TlsServerHandshaker::IsCryptoFrameExpectedForEncryptionLevel(
     EncryptionLevel level) const {
   return level != ENCRYPTION_ZERO_RTT;
diff --git a/quiche/quic/core/tls_server_handshaker.h b/quiche/quic/core/tls_server_handshaker.h
index aece463..01ff093 100644
--- a/quiche/quic/core/tls_server_handshaker.h
+++ b/quiche/quic/core/tls_server_handshaker.h
@@ -97,6 +97,12 @@
   EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
       PacketNumberSpace space) const override;
 
+  // Overrides to support cached info after ResetSsl is called in QUIC session.
+  absl::string_view Sni() const override;
+  const SSL_CIPHER* Ciphersuite() const override;
+  absl::string_view Alpn() const override;
+  uint16_t TlsGroupId() const override;
+
   // From QuicCryptoServerStreamBase and TlsHandshaker
   ssl_early_data_reason_t EarlyDataReason() const override;
   bool encryption_established() const override;
@@ -412,10 +418,13 @@
   struct CachedSSLInfo {
     bool is_resumption = false;
     bool is_zero_rtt = false;
+    uint16_t tls_group_id = 0;
     ssl_early_data_reason_t early_data_reason = ssl_early_data_unknown;
     // Note SSL_get_current_cipher returns a static allocated pointer and as a
     // result it is safe to cache a raw pointer here.
     const SSL_CIPHER* cipher = nullptr;
+    std::string alpn;
+    std::string sni;
   };
   std::optional<CachedSSLInfo> cached_ssl_info_;
 
diff --git a/quiche/quic/core/tls_server_handshaker_test.cc b/quiche/quic/core/tls_server_handshaker_test.cc
index 31521ce..7eed9de 100644
--- a/quiche/quic/core/tls_server_handshaker_test.cc
+++ b/quiche/quic/core/tls_server_handshaker_test.cc
@@ -72,6 +72,11 @@
 
 const char kServerHostname[] = "test.example.com";
 const uint16_t kServerPort = 443;
+constexpr uint16_t kCipherId = 0x1301;
+constexpr absl::string_view kCipherString = "TLS_AES_128_GCM_SHA256";
+constexpr uint16_t kTlsGroupId = 29;
+constexpr absl::string_view kTlsGroupString = "X25519";
+constexpr absl::string_view kTlsVersion = "TLS_VERSION_1_3";
 
 struct TestParams {
   ParsedQuicVersion version;
@@ -556,10 +561,10 @@
   EXPECT_TRUE(server_stream()->version().IsIetfQuic());
   ExpectHandshakeSuccessful();
 
-  EXPECT_EQ(server_stream()->sni(), kServerHostname);
-  EXPECT_EQ(server_stream()->alpn(),
+  EXPECT_EQ(server_stream()->Sni(), kServerHostname);
+  EXPECT_EQ(server_stream()->Alpn(),
             AlpnForVersion(server_stream()->version()));
-  EXPECT_NE(server_stream()->ciphersuite(), nullptr);
+  EXPECT_NE(server_stream()->Ciphersuite(), nullptr);
 }
 
 TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertSuccess) {
@@ -1756,6 +1761,40 @@
   ASSERT_TRUE(credential_ex_data_verified);
 }
 
+TEST_P(TlsServerHandshakerTest, CachedSslInfoAfterResetSsl) {
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+
+  EXPECT_EQ(server_stream()->Sni(), kServerHostname);
+  EXPECT_EQ(server_stream()->Alpn(),
+            AlpnForVersion(server_stream()->version()));
+  const SSL_CIPHER* cipher = server_stream()->Ciphersuite();
+  EXPECT_NE(cipher, nullptr);
+
+  EXPECT_EQ(server_stream()->Ciphersuite(), cipher);
+  EXPECT_EQ(server_stream()->CiphersuiteId(), kCipherId);
+  EXPECT_EQ(server_stream()->CiphersuiteString(), kCipherString);
+  EXPECT_EQ(server_stream()->TlsGroupId(), kTlsGroupId);
+  EXPECT_EQ(server_stream()->TlsGroupString(), kTlsGroupString);
+  EXPECT_EQ(server_stream()->TlsVersion(), kTlsVersion);
+
+  // Call ResetSsl. This should cache the SSL info.
+  TlsServerHandshaker* handshaker =
+      static_cast<TlsServerHandshaker*>(server_stream());
+  handshaker->ResetSsl();
+
+  // Verify that the info is still available from the cache.
+  EXPECT_EQ(server_stream()->Sni(), kServerHostname);
+  EXPECT_EQ(server_stream()->Alpn(),
+            AlpnForVersion(server_stream()->version()));
+  EXPECT_EQ(server_stream()->Ciphersuite(), cipher);
+  EXPECT_EQ(server_stream()->CiphersuiteId(), kCipherId);
+  EXPECT_EQ(server_stream()->CiphersuiteString(), kCipherString);
+  EXPECT_EQ(server_stream()->TlsGroupId(), kTlsGroupId);
+  EXPECT_EQ(server_stream()->TlsGroupString(), kTlsGroupString);
+  EXPECT_EQ(server_stream()->TlsVersion(), kTlsVersion);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic