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