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}
This commit is contained in:
Harald Alvestrand 2019-10-28 09:51:17 +01:00 committed by Commit Bot
parent a81e2b4510
commit 5cb7807a36
16 changed files with 143 additions and 7 deletions

View file

@ -600,6 +600,9 @@ class RTC_EXPORT RTCTransportStats final : public RTCStats {
RTCStatsMember<std::string> selected_candidate_pair_id;
RTCStatsMember<std::string> local_certificate_id;
RTCStatsMember<std::string> remote_certificate_id;
RTCStatsMember<std::string> tls_version;
RTCStatsMember<std::string> dtls_cipher;
RTCStatsMember<std::string> srtp_cipher;
RTCStatsMember<uint32_t> selected_candidate_pair_changes;
};

View file

@ -380,6 +380,14 @@ bool DtlsTransport::GetSrtpCryptoSuite(int* cipher) {
return dtls_->GetDtlsSrtpCryptoSuite(cipher);
}
bool DtlsTransport::GetSslVersionBytes(int* version) const {
if (dtls_state() != DTLS_TRANSPORT_CONNECTED) {
return false;
}
return dtls_->GetSslVersionBytes(version);
}
// Called from upper layers to send a media packet.
int DtlsTransport::SendPacket(const char* data,
size_t size,

View file

@ -142,6 +142,8 @@ class DtlsTransport : public DtlsTransportInternal {
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override;
// Find out which TLS version was negotiated
bool GetSslVersionBytes(int* version) const override;
// Find out which DTLS-SRTP cipher was negotiated
bool GetSrtpCryptoSuite(int* cipher) override;

View file

@ -74,6 +74,8 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal {
virtual bool SetDtlsRole(rtc::SSLRole role) = 0;
// Finds out which TLS/DTLS version is running.
virtual bool GetSslVersionBytes(int* version) const = 0;
// Finds out which DTLS-SRTP cipher was negotiated.
// TODO(zhihuang): Remove this once all dependencies implement this.
virtual bool GetSrtpCryptoSuite(int* cipher) = 0;

View file

@ -168,6 +168,13 @@ class FakeDtlsTransport : public DtlsTransportInternal {
remote_cert_ = cert;
}
bool IsDtlsActive() const override { return do_dtls_; }
bool GetSslVersionBytes(int* version) const override {
if (!do_dtls_) {
return false;
}
*version = 0x0102;
return true;
}
bool GetSrtpCryptoSuite(int* crypto_suite) override {
if (!do_dtls_) {
return false;

View file

@ -66,6 +66,9 @@ bool NoOpDtlsTransport::GetDtlsRole(rtc::SSLRole* role) const {
bool NoOpDtlsTransport::SetDtlsRole(rtc::SSLRole role) {
return false;
}
bool NoOpDtlsTransport::GetSslVersionBytes(int* version) const {
return false;
}
bool NoOpDtlsTransport::GetSrtpCryptoSuite(int* cipher) {
return false;
}

View file

@ -64,6 +64,7 @@ class NoOpDtlsTransport : public DtlsTransportInternal {
bool IsDtlsActive() const override;
bool GetDtlsRole(rtc::SSLRole* role) const override;
bool SetDtlsRole(rtc::SSLRole role) override;
bool GetSslVersionBytes(int* version) const override;
bool GetSrtpCryptoSuite(int* cipher) override;
bool GetSslCipherSuite(int* cipher) override;
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;

View file

@ -772,6 +772,7 @@ bool JsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport,
} 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();

View file

@ -1721,6 +1721,26 @@ void RTCStatsCollector::ProduceTransportStats_n(
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));
}
}

View file

@ -2202,6 +2202,68 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) {
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;

View file

@ -973,6 +973,9 @@ class RTCStatsReportVerifier {
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();

View file

@ -27,6 +27,7 @@ struct TransportChannelStats {
~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;

View file

@ -373,9 +373,9 @@ bool OpenSSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) {
return true;
}
int OpenSSLStreamAdapter::GetSslVersion() const {
SSLProtocolVersion OpenSSLStreamAdapter::GetSslVersion() const {
if (state_ != SSL_CONNECTED) {
return -1;
return SSL_PROTOCOL_NOT_GIVEN;
}
int ssl_version = SSL_version(ssl_);
@ -395,7 +395,15 @@ int OpenSSLStreamAdapter::GetSslVersion() const {
}
}
return -1;
return SSL_PROTOCOL_NOT_GIVEN;
}
bool OpenSSLStreamAdapter::GetSslVersionBytes(int* version) const {
if (state_ != SSL_CONNECTED) {
return false;
}
*version = SSL_version(ssl_);
return true;
}
// Key Extractor interface

View file

@ -96,8 +96,8 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter {
bool GetSslCipherSuite(int* cipher) override;
int GetSslVersion() const override;
SSLProtocolVersion GetSslVersion() const override;
bool GetSslVersionBytes(int* version) const override;
// Key Extractor interface
bool ExportKeyingMaterial(const std::string& label,
const uint8_t* context,

View file

@ -91,7 +91,8 @@ bool IsGcmCryptoSuiteName(const std::string& crypto_suite);
enum SSLRole { SSL_CLIENT, SSL_SERVER };
enum SSLMode { SSL_MODE_TLS, SSL_MODE_DTLS };
enum SSLProtocolVersion {
SSL_PROTOCOL_TLS_10,
SSL_PROTOCOL_NOT_GIVEN = -1,
SSL_PROTOCOL_TLS_10 = 0,
SSL_PROTOCOL_TLS_11,
SSL_PROTOCOL_TLS_12,
SSL_PROTOCOL_DTLS_10 = SSL_PROTOCOL_TLS_11,
@ -187,7 +188,12 @@ class SSLStreamAdapter : public StreamAdapterInterface {
// connection (e.g. 0x2F for "TLS_RSA_WITH_AES_128_CBC_SHA").
virtual bool GetSslCipherSuite(int* cipher_suite);
virtual int GetSslVersion() const = 0;
// Retrieves the enum value for SSL version.
// Will return -1 until the version has been negotiated.
virtual SSLProtocolVersion GetSslVersion() const = 0;
// Retrieves the 2-byte version from the TLS protocol.
// Will return false until the version has been negotiated.
virtual bool GetSslVersionBytes(int* version) const = 0;
// Key Exporter interface from RFC 5705
// Arguments are:

View file

@ -883,6 +883,9 @@ WEBRTC_RTCSTATS_IMPL(RTCTransportStats, RTCStats, "transport",
&selected_candidate_pair_id,
&local_certificate_id,
&remote_certificate_id,
&tls_version,
&dtls_cipher,
&srtp_cipher,
&selected_candidate_pair_changes)
// clang-format on
@ -899,6 +902,9 @@ RTCTransportStats::RTCTransportStats(std::string&& id, int64_t timestamp_us)
selected_candidate_pair_id("selectedCandidatePairId"),
local_certificate_id("localCertificateId"),
remote_certificate_id("remoteCertificateId"),
tls_version("tlsVersion"),
dtls_cipher("dtlsCipher"),
srtp_cipher("srtpCipher"),
selected_candidate_pair_changes("selectedCandidatePairChanges") {}
RTCTransportStats::RTCTransportStats(const RTCTransportStats& other)
@ -910,6 +916,9 @@ RTCTransportStats::RTCTransportStats(const RTCTransportStats& other)
selected_candidate_pair_id(other.selected_candidate_pair_id),
local_certificate_id(other.local_certificate_id),
remote_certificate_id(other.remote_certificate_id),
tls_version(other.tls_version),
dtls_cipher(other.dtls_cipher),
srtp_cipher(other.srtp_cipher),
selected_candidate_pair_changes(other.selected_candidate_pair_changes) {}
RTCTransportStats::~RTCTransportStats() {}