Unify TLS cipher suite name handling

Move it away from the "proprietary" SSL_CIPHER_get_id and looking up the cipher based on that towards SSL_CIPHER_standard_name.

SSL_CIPHER_get_id and the associated GetSslCipherSuite API is kept around for
  WebRTC.PeerConnection.SslCipherSuite.*
UMA metrics and metrics compability (despite not yielding the IANA ids it promises).

BUG=None

Change-Id: Iaa357e3e31dc90abea688cf6ca10c0b40582ef38
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/363202
Reviewed-by: David Benjamin <davidben@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@meta.com>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43097}
This commit is contained in:
Philipp Hancke 2024-09-27 09:04:43 -07:00 committed by WebRTC LUCI CQ
parent 20e4ce51df
commit 1831184330
14 changed files with 33 additions and 119 deletions

View File

@ -233,6 +233,13 @@ bool DtlsTransport::GetSslCipherSuite(int* cipher) {
return dtls_->GetSslCipherSuite(cipher);
}
std::optional<absl::string_view> DtlsTransport::GetTlsCipherSuiteName() const {
if (dtls_state() != webrtc::DtlsTransportState::kConnected) {
return std::nullopt;
}
return dtls_->GetTlsCipherSuiteName();
}
webrtc::RTCError DtlsTransport::SetRemoteParameters(
absl::string_view digest_alg,
const uint8_t* digest,

View File

@ -169,6 +169,7 @@ class DtlsTransport : public DtlsTransportInternal {
// Find out which DTLS cipher was negotiated
bool GetSslCipherSuite(int* cipher) override;
std::optional<absl::string_view> GetTlsCipherSuiteName() const override;
// Once DTLS has been established, this method retrieves the certificate
// chain in use by the remote peer, for use in external identity

View File

@ -70,6 +70,7 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal {
// Finds out which DTLS cipher was negotiated.
// TODO(zhihuang): Remove this once all dependencies implement this.
virtual bool GetSslCipherSuite(int* cipher) = 0;
virtual std::optional<absl::string_view> GetTlsCipherSuiteName() const = 0;
// Find out which signature algorithm was used by the peer. Returns values
// from

View File

@ -213,6 +213,10 @@ class FakeDtlsTransport : public DtlsTransportInternal {
void SetSslCipherSuite(std::optional<int> cipher_suite) {
ssl_cipher_suite_ = cipher_suite;
}
std::optional<absl::string_view> GetTlsCipherSuiteName() const override {
return "FakeTlsCipherSuite";
}
uint16_t GetSslPeerSignatureAlgorithm() const override { return 0; }
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override {
return local_cert_;

View File

@ -642,6 +642,7 @@ bool JsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport,
dtls_transport->GetSslVersionBytes(&substats.ssl_version_bytes);
dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite);
dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite);
substats.tls_cipher_suite_name = dtls_transport->GetTlsCipherSuiteName();
substats.dtls_state = dtls_transport->dtls_state();
rtc::SSLRole dtls_role;
if (dtls_transport->GetDtlsRole(&dtls_role)) {

View File

@ -1012,13 +1012,10 @@ void LegacyStatsCollector::ExtractSessionInfo_s(SessionStats& session_stats) {
StatsReport::kStatsValueNameSrtpCipher,
rtc::SrtpCryptoSuiteToName(srtp_crypto_suite));
}
int ssl_cipher_suite = channel_iter.ssl_cipher_suite;
if (ssl_cipher_suite != rtc::kTlsNullWithNullNull &&
rtc::SSLStreamAdapter::SslCipherSuiteToName(ssl_cipher_suite)
.length()) {
if (channel_iter.tls_cipher_suite_name) {
channel_report->AddString(
StatsReport::kStatsValueNameDtlsCipher,
rtc::SSLStreamAdapter::SslCipherSuiteToName(ssl_cipher_suite));
std::string(*channel_iter.tls_cipher_suite_name));
}
// Collect stats for non-pooled candidates. Note that the reports

View File

@ -67,11 +67,6 @@ using ::testing::UnorderedElementsAre;
namespace webrtc {
namespace internal {
// This value comes from openssl/tls1.h
static const int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014;
} // namespace internal
// Error return values
const char kNotFound[] = "NOT FOUND";
@ -674,8 +669,7 @@ class LegacyStatsCollectorTest : public ::testing::Test {
TransportChannelStats channel_stats;
channel_stats.component = 1;
channel_stats.srtp_crypto_suite = rtc::kSrtpAes128CmSha1_80;
channel_stats.ssl_cipher_suite =
internal::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA;
channel_stats.tls_cipher_suite_name = "cipher_suite_for_test";
pc->SetTransportStats(kTransportName, channel_stats);
// Fake certificate to report.
@ -722,9 +716,7 @@ class LegacyStatsCollectorTest : public ::testing::Test {
std::string dtls_cipher_suite =
ExtractStatsValue(StatsReport::kStatsReportTypeComponent, reports,
StatsReport::kStatsValueNameDtlsCipher);
EXPECT_EQ(rtc::SSLStreamAdapter::SslCipherSuiteToName(
internal::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA),
dtls_cipher_suite);
EXPECT_EQ(dtls_cipher_suite, "cipher_suite_for_test");
std::string srtp_crypto_suite =
ExtractStatsValue(StatsReport::kStatsReportTypeComponent, reports,
StatsReport::kStatsValueNameSrtpCipher);

View File

@ -1961,14 +1961,7 @@ void RTCStatsCollector::ProduceTransportStats_n(
transport_stats->dtls_role = "unknown";
}
if (channel_stats.ssl_cipher_suite != rtc::kTlsNullWithNullNull &&
rtc::SSLStreamAdapter::SslCipherSuiteToName(
channel_stats.ssl_cipher_suite)
.length()) {
transport_stats->dtls_cipher =
rtc::SSLStreamAdapter::SslCipherSuiteToName(
channel_stats.ssl_cipher_suite);
}
transport_stats->dtls_cipher = channel_stats.tls_cipher_suite_name;
if (channel_stats.srtp_crypto_suite != rtc::kSrtpInvalidCryptoSuite &&
rtc::SrtpCryptoSuiteToName(channel_stats.srtp_crypto_suite)
.length()) {

View File

@ -2942,8 +2942,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) {
"thelocalufrag";
rtp_transport_channel_stats.ice_transport_stats.ice_state =
IceTransportState::kConnected;
// 0x2F is TLS_RSA_WITH_AES_128_CBC_SHA according to IANA
rtp_transport_channel_stats.ssl_cipher_suite = 0x2F;
rtp_transport_channel_stats.tls_cipher_suite_name =
"TLS_RSA_WITH_AES_128_CBC_SHA";
rtp_transport_channel_stats.srtp_crypto_suite = rtc::kSrtpAes128CmSha1_80;
pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats});

View File

@ -31,6 +31,7 @@ struct TransportChannelStats {
int ssl_version_bytes = 0;
int srtp_crypto_suite = rtc::kSrtpInvalidCryptoSuite;
int ssl_cipher_suite = rtc::kTlsNullWithNullNull;
std::optional<absl::string_view> tls_cipher_suite_name;
std::optional<rtc::SSLRole> dtls_role;
webrtc::DtlsTransportState dtls_state = webrtc::DtlsTransportState::kNew;
IceTransportStats ice_transport_stats;

View File

@ -63,12 +63,6 @@
#error "webrtc requires at least OpenSSL version 1.1.0, to support DTLS-SRTP"
#endif
// Defines for the TLS Cipher Suite Map.
#define DEFINE_CIPHER_ENTRY_SSL3(name) \
{ SSL3_CK_##name, "TLS_" #name }
#define DEFINE_CIPHER_ENTRY_TLS1(name) \
{ TLS1_CK_##name, "TLS_" #name }
namespace rtc {
namespace {
using ::webrtc::SafeTask;
@ -93,68 +87,6 @@ constexpr SrtpCipherMapEntry kSrtpCipherMap[] = {
{"SRTP_AEAD_AES_128_GCM", kSrtpAeadAes128Gcm},
{"SRTP_AEAD_AES_256_GCM", kSrtpAeadAes256Gcm}};
#ifndef OPENSSL_IS_BORINGSSL
// The "SSL_CIPHER_standard_name" function is only available in OpenSSL when
// compiled with tracing, so we need to define the mapping manually here.
constexpr SslCipherMapEntry kSslCipherMap[] = {
// TLS v1.0 ciphersuites from RFC2246.
DEFINE_CIPHER_ENTRY_SSL3(RSA_RC4_128_SHA),
{SSL3_CK_RSA_DES_192_CBC3_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
// AES ciphersuites from RFC3268.
{TLS1_CK_RSA_WITH_AES_128_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"},
{TLS1_CK_DHE_RSA_WITH_AES_128_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"},
{TLS1_CK_RSA_WITH_AES_256_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"},
{TLS1_CK_DHE_RSA_WITH_AES_256_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"},
// ECC ciphersuites from RFC4492.
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_RC4_128_SHA),
{TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA,
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"},
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_RC4_128_SHA),
{TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"},
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_CBC_SHA),
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_CBC_SHA),
// TLS v1.2 ciphersuites.
{TLS1_CK_RSA_WITH_AES_128_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"},
{TLS1_CK_RSA_WITH_AES_256_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"},
{TLS1_CK_DHE_RSA_WITH_AES_128_SHA256,
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"},
{TLS1_CK_DHE_RSA_WITH_AES_256_SHA256,
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"},
// TLS v1.2 GCM ciphersuites from RFC5288.
DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_128_GCM_SHA256),
DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_256_GCM_SHA384),
DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_128_GCM_SHA256),
DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_256_GCM_SHA384),
DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_128_GCM_SHA256),
DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_256_GCM_SHA384),
// ECDH HMAC based ciphersuites from RFC5289.
{TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"},
{TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"},
{TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"},
{TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
// ECDH GCM based ciphersuites from RFC5289.
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_GCM_SHA256),
DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_GCM_SHA384),
{0, nullptr}};
#endif // #ifndef OPENSSL_IS_BORINGSSL
#ifdef OPENSSL_IS_BORINGSSL
// Enabled by EnableTimeCallbackForTesting. Should never be set in production
// code.
@ -388,23 +320,14 @@ bool OpenSSLStreamAdapter::SetPeerCertificateDigest(
return true;
}
std::string OpenSSLStreamAdapter::SslCipherSuiteToName(int cipher_suite) {
#ifdef OPENSSL_IS_BORINGSSL
const SSL_CIPHER* ssl_cipher = SSL_get_cipher_by_value(cipher_suite);
if (!ssl_cipher) {
return std::string();
std::optional<absl::string_view> OpenSSLStreamAdapter::GetTlsCipherSuiteName()
const {
if (state_ != SSL_CONNECTED) {
return std::nullopt;
}
return SSL_CIPHER_standard_name(ssl_cipher);
#else
const int openssl_cipher_id = 0x03000000L | cipher_suite;
for (const SslCipherMapEntry* entry = kSslCipherMap; entry->rfc_name;
++entry) {
if (openssl_cipher_id == static_cast<int>(entry->openssl_id)) {
return entry->rfc_name;
}
}
return std::string();
#endif
const SSL_CIPHER* current_cipher = SSL_get_current_cipher(ssl_);
return SSL_CIPHER_standard_name(current_cipher);
}
bool OpenSSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) {

View File

@ -103,8 +103,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter,
void Close() override;
StreamState GetState() const override;
// TODO(guoweis): Move this away from a static class method.
static std::string SslCipherSuiteToName(int crypto_suite);
std::optional<absl::string_view> GetTlsCipherSuiteName() const override;
bool GetSslCipherSuite(int* cipher) override;
[[deprecated("Use GetSslVersionBytes")]] SSLProtocolVersion GetSslVersion()

View File

@ -119,9 +119,6 @@ bool SSLStreamAdapter::IsAcceptableCipher(absl::string_view cipher,
KeyType key_type) {
return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type);
}
std::string SSLStreamAdapter::SslCipherSuiteToName(int cipher_suite) {
return OpenSSLStreamAdapter::SslCipherSuiteToName(cipher_suite);
}
///////////////////////////////////////////////////////////////////////////////
// Test only settings

View File

@ -185,6 +185,9 @@ class SSLStreamAdapter : public StreamInterface {
// Retrieves the IANA registration id of the cipher suite used for the
// connection (e.g. 0x2F for "TLS_RSA_WITH_AES_128_CBC_SHA").
virtual bool GetSslCipherSuite(int* cipher_suite);
// Returns the name of the cipher suite used for the DTLS transport,
// as defined in the "Description" column of the IANA cipher suite registry.
virtual std::optional<absl::string_view> GetTlsCipherSuiteName() const = 0;
// Retrieves the enum value for SSL version.
// Will return -1 until the version has been negotiated.
@ -236,11 +239,6 @@ class SSLStreamAdapter : public StreamInterface {
static bool IsAcceptableCipher(int cipher, KeyType key_type);
static bool IsAcceptableCipher(absl::string_view cipher, KeyType key_type);
// TODO(guoweis): Move this away from a static class method. Currently this is
// introduced such that any caller could depend on sslstreamadapter.h without
// depending on specific SSL implementation.
static std::string SslCipherSuiteToName(int cipher_suite);
////////////////////////////////////////////////////////////////////////////
// Testing only member functions
////////////////////////////////////////////////////////////////////////////