Implement crypto stats on DTLS transport

Bug: chromium:1018077
Change-Id: I585d4064f39e5f9d268b408ebf6ae13a056c778a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158403
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29628}
diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc
index ca44ec8..13618c7 100644
--- a/pc/jsep_transport.cc
+++ b/pc/jsep_transport.cc
@@ -772,6 +772,7 @@
   } else {
     substats.component = ICE_CANDIDATE_COMPONENT_RTP;
   }
+  dtls_transport->GetSslVersionBytes(&substats.ssl_version_bytes);
   dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite);
   dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite);
   substats.dtls_state = dtls_transport->dtls_state();
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index ab12c65..481e255 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -1721,6 +1721,26 @@
         transport_stats->local_certificate_id = local_certificate_id;
       if (!remote_certificate_id.empty())
         transport_stats->remote_certificate_id = remote_certificate_id;
+      // Crypto information
+      if (channel_stats.ssl_version_bytes) {
+        char bytes[5];
+        snprintf(bytes, sizeof(bytes), "%04X", channel_stats.ssl_version_bytes);
+        transport_stats->tls_version = bytes;
+      }
+      if (channel_stats.ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL &&
+          rtc::SSLStreamAdapter::SslCipherSuiteToName(
+              channel_stats.ssl_cipher_suite)
+              .length()) {
+        transport_stats->dtls_cipher =
+            rtc::SSLStreamAdapter::SslCipherSuiteToName(
+                channel_stats.ssl_cipher_suite);
+      }
+      if (channel_stats.srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE &&
+          rtc::SrtpCryptoSuiteToName(channel_stats.srtp_crypto_suite)
+              .length()) {
+        transport_stats->srtp_cipher =
+            rtc::SrtpCryptoSuiteToName(channel_stats.srtp_crypto_suite);
+      }
       report->AddStats(std::move(transport_stats));
     }
   }
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index ce2d54e..95c2a9b 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -2202,6 +2202,68 @@
       report->Get(expected_rtcp_transport.id())->cast_to<RTCTransportStats>());
 }
 
+TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) {
+  const char kTransportName[] = "transport";
+
+  pc_->AddVoiceChannel("audio", kTransportName);
+
+  std::unique_ptr<cricket::Candidate> rtp_local_candidate =
+      CreateFakeCandidate("42.42.42.42", 42, "protocol", rtc::ADAPTER_TYPE_WIFI,
+                          cricket::LOCAL_PORT_TYPE, 42);
+  std::unique_ptr<cricket::Candidate> rtp_remote_candidate =
+      CreateFakeCandidate("42.42.42.42", 42, "protocol",
+                          rtc::ADAPTER_TYPE_UNKNOWN, cricket::LOCAL_PORT_TYPE,
+                          42);
+  std::unique_ptr<cricket::Candidate> rtcp_local_candidate =
+      CreateFakeCandidate("42.42.42.42", 42, "protocol", rtc::ADAPTER_TYPE_WIFI,
+                          cricket::LOCAL_PORT_TYPE, 42);
+  std::unique_ptr<cricket::Candidate> rtcp_remote_candidate =
+      CreateFakeCandidate("42.42.42.42", 42, "protocol",
+                          rtc::ADAPTER_TYPE_UNKNOWN, cricket::LOCAL_PORT_TYPE,
+                          42);
+
+  cricket::ConnectionInfo rtp_connection_info;
+  rtp_connection_info.best_connection = false;
+  rtp_connection_info.local_candidate = *rtp_local_candidate.get();
+  rtp_connection_info.remote_candidate = *rtp_remote_candidate.get();
+  rtp_connection_info.sent_total_bytes = 42;
+  rtp_connection_info.recv_total_bytes = 1337;
+  cricket::TransportChannelStats rtp_transport_channel_stats;
+  rtp_transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP;
+  rtp_transport_channel_stats.ice_transport_stats.connection_infos.push_back(
+      rtp_connection_info);
+  // The state must be connected in order for crypto parameters to show up.
+  rtp_transport_channel_stats.dtls_state = cricket::DTLS_TRANSPORT_CONNECTED;
+  rtp_transport_channel_stats.ice_transport_stats
+      .selected_candidate_pair_changes = 1;
+  rtp_transport_channel_stats.ssl_version_bytes = 0x0203;
+  // 0x2F is TLS_RSA_WITH_AES_128_CBC_SHA according to IANA
+  rtp_transport_channel_stats.ssl_cipher_suite = 0x2F;
+  rtp_transport_channel_stats.srtp_crypto_suite = rtc::SRTP_AES128_CM_SHA1_80;
+  pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats});
+
+  // Get stats
+  rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
+
+  RTCTransportStats expected_rtp_transport(
+      "RTCTransport_transport_" +
+          rtc::ToString(cricket::ICE_CANDIDATE_COMPONENT_RTP),
+      report->timestamp_us());
+  expected_rtp_transport.bytes_sent = 42;
+  expected_rtp_transport.bytes_received = 1337;
+  expected_rtp_transport.dtls_state = RTCDtlsTransportState::kConnected;
+  expected_rtp_transport.selected_candidate_pair_changes = 1;
+  // Crypto parameters
+  expected_rtp_transport.tls_version = "0203";
+  expected_rtp_transport.dtls_cipher = "TLS_RSA_WITH_AES_128_CBC_SHA";
+  expected_rtp_transport.srtp_cipher = "AES_CM_128_HMAC_SHA1_80";
+
+  ASSERT_TRUE(report->Get(expected_rtp_transport.id()));
+  EXPECT_EQ(
+      expected_rtp_transport,
+      report->Get(expected_rtp_transport.id())->cast_to<RTCTransportStats>());
+}
+
 TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) {
   cricket::VoiceMediaInfo voice_media_info;
 
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc
index 9000ff9..31258a2a 100644
--- a/pc/rtc_stats_integrationtest.cc
+++ b/pc/rtc_stats_integrationtest.cc
@@ -973,6 +973,9 @@
                                      RTCCertificateStats::kType);
     verifier.TestMemberIsIDReference(transport.remote_certificate_id,
                                      RTCCertificateStats::kType);
+    verifier.TestMemberIsDefined(transport.tls_version);
+    verifier.TestMemberIsDefined(transport.dtls_cipher);
+    verifier.TestMemberIsDefined(transport.srtp_cipher);
     verifier.TestMemberIsPositive<uint32_t>(
         transport.selected_candidate_pair_changes);
     return verifier.ExpectAllMembersSuccessfullyTested();
diff --git a/pc/transport_stats.h b/pc/transport_stats.h
index 4f6ce2a..7cb95f4 100644
--- a/pc/transport_stats.h
+++ b/pc/transport_stats.h
@@ -27,6 +27,7 @@
   ~TransportChannelStats();
 
   int component = 0;
+  int ssl_version_bytes = 0;
   int srtp_crypto_suite = rtc::SRTP_INVALID_CRYPTO_SUITE;
   int ssl_cipher_suite = rtc::TLS_NULL_WITH_NULL_NULL;
   DtlsTransportState dtls_state = DTLS_TRANSPORT_NEW;