summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc')
-rw-r--r--third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc576
1 files changed, 576 insertions, 0 deletions
diff --git a/third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc b/third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc
new file mode 100644
index 0000000000..bf0676c324
--- /dev/null
+++ b/third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc
@@ -0,0 +1,576 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "pc/dtls_srtp_transport.h"
+
+#include <string.h>
+
+#include <cstdint>
+#include <memory>
+
+#include "call/rtp_demuxer.h"
+#include "media/base/fake_rtp.h"
+#include "p2p/base/dtls_transport_internal.h"
+#include "p2p/base/fake_dtls_transport.h"
+#include "p2p/base/fake_ice_transport.h"
+#include "p2p/base/p2p_constants.h"
+#include "pc/rtp_transport.h"
+#include "pc/test/rtp_transport_test_util.h"
+#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/byte_order.h"
+#include "rtc_base/containers/flat_set.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/rtc_certificate.h"
+#include "rtc_base/ssl_identity.h"
+#include "rtc_base/third_party/sigslot/sigslot.h"
+#include "test/gtest.h"
+#include "test/scoped_key_value_config.h"
+
+using cricket::FakeDtlsTransport;
+using cricket::FakeIceTransport;
+using webrtc::DtlsSrtpTransport;
+using webrtc::RtpTransport;
+using webrtc::SrtpTransport;
+
+const int kRtpAuthTagLen = 10;
+
+class DtlsSrtpTransportTest : public ::testing::Test,
+ public sigslot::has_slots<> {
+ protected:
+ DtlsSrtpTransportTest() {}
+
+ ~DtlsSrtpTransportTest() {
+ if (dtls_srtp_transport1_) {
+ dtls_srtp_transport1_->UnregisterRtpDemuxerSink(&transport_observer1_);
+ }
+ if (dtls_srtp_transport2_) {
+ dtls_srtp_transport2_->UnregisterRtpDemuxerSink(&transport_observer2_);
+ }
+ }
+
+ std::unique_ptr<DtlsSrtpTransport> MakeDtlsSrtpTransport(
+ FakeDtlsTransport* rtp_dtls,
+ FakeDtlsTransport* rtcp_dtls,
+ bool rtcp_mux_enabled) {
+ auto dtls_srtp_transport =
+ std::make_unique<DtlsSrtpTransport>(rtcp_mux_enabled, field_trials_);
+
+ dtls_srtp_transport->SetDtlsTransports(rtp_dtls, rtcp_dtls);
+
+ return dtls_srtp_transport;
+ }
+
+ void MakeDtlsSrtpTransports(FakeDtlsTransport* rtp_dtls1,
+ FakeDtlsTransport* rtcp_dtls1,
+ FakeDtlsTransport* rtp_dtls2,
+ FakeDtlsTransport* rtcp_dtls2,
+ bool rtcp_mux_enabled) {
+ dtls_srtp_transport1_ =
+ MakeDtlsSrtpTransport(rtp_dtls1, rtcp_dtls1, rtcp_mux_enabled);
+ dtls_srtp_transport2_ =
+ MakeDtlsSrtpTransport(rtp_dtls2, rtcp_dtls2, rtcp_mux_enabled);
+
+ dtls_srtp_transport1_->SubscribeRtcpPacketReceived(
+ &transport_observer1_,
+ [this](rtc::CopyOnWriteBuffer* buffer, int64_t packet_time_ms) {
+ transport_observer1_.OnRtcpPacketReceived(buffer, packet_time_ms);
+ });
+ dtls_srtp_transport1_->SubscribeReadyToSend(
+ &transport_observer1_,
+ [this](bool ready) { transport_observer1_.OnReadyToSend(ready); });
+
+ dtls_srtp_transport2_->SubscribeRtcpPacketReceived(
+ &transport_observer2_,
+ [this](rtc::CopyOnWriteBuffer* buffer, int64_t packet_time_ms) {
+ transport_observer2_.OnRtcpPacketReceived(buffer, packet_time_ms);
+ });
+ dtls_srtp_transport2_->SubscribeReadyToSend(
+ &transport_observer2_,
+ [this](bool ready) { transport_observer2_.OnReadyToSend(ready); });
+ webrtc::RtpDemuxerCriteria demuxer_criteria;
+ // 0x00 is the payload type used in kPcmuFrame.
+ demuxer_criteria.payload_types() = {0x00};
+ dtls_srtp_transport1_->RegisterRtpDemuxerSink(demuxer_criteria,
+ &transport_observer1_);
+ dtls_srtp_transport2_->RegisterRtpDemuxerSink(demuxer_criteria,
+ &transport_observer2_);
+ }
+
+ void CompleteDtlsHandshake(FakeDtlsTransport* fake_dtls1,
+ FakeDtlsTransport* fake_dtls2) {
+ auto cert1 = rtc::RTCCertificate::Create(
+ rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
+ fake_dtls1->SetLocalCertificate(cert1);
+ auto cert2 = rtc::RTCCertificate::Create(
+ rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
+ fake_dtls2->SetLocalCertificate(cert2);
+ fake_dtls1->SetDestination(fake_dtls2);
+ }
+
+ void SendRecvRtpPackets() {
+ ASSERT_TRUE(dtls_srtp_transport1_);
+ ASSERT_TRUE(dtls_srtp_transport2_);
+ ASSERT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
+ ASSERT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
+
+ size_t rtp_len = sizeof(kPcmuFrame);
+ size_t packet_size = rtp_len + kRtpAuthTagLen;
+ rtc::Buffer rtp_packet_buffer(packet_size);
+ char* rtp_packet_data = rtp_packet_buffer.data<char>();
+ memcpy(rtp_packet_data, kPcmuFrame, rtp_len);
+ // In order to be able to run this test function multiple times we can not
+ // use the same sequence number twice. Increase the sequence number by one.
+ rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_data) + 2,
+ ++sequence_number_);
+ rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
+ packet_size);
+ rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
+ packet_size);
+
+ rtc::PacketOptions options;
+ // Send a packet from `srtp_transport1_` to `srtp_transport2_` and verify
+ // that the packet can be successfully received and decrypted.
+ int prev_received_packets = transport_observer2_.rtp_count();
+ ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
+ cricket::PF_SRTP_BYPASS));
+ ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data());
+ EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(),
+ kPcmuFrame, rtp_len));
+ EXPECT_EQ(prev_received_packets + 1, transport_observer2_.rtp_count());
+
+ prev_received_packets = transport_observer1_.rtp_count();
+ ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
+ cricket::PF_SRTP_BYPASS));
+ ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data());
+ EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(),
+ kPcmuFrame, rtp_len));
+ EXPECT_EQ(prev_received_packets + 1, transport_observer1_.rtp_count());
+ }
+
+ void SendRecvRtcpPackets() {
+ size_t rtcp_len = sizeof(kRtcpReport);
+ size_t packet_size = rtcp_len + 4 + kRtpAuthTagLen;
+ rtc::Buffer rtcp_packet_buffer(packet_size);
+
+ // TODO(zhihuang): Remove the extra copy when the SendRtpPacket method
+ // doesn't take the CopyOnWriteBuffer by pointer.
+ rtc::CopyOnWriteBuffer rtcp_packet1to2(kRtcpReport, rtcp_len, packet_size);
+ rtc::CopyOnWriteBuffer rtcp_packet2to1(kRtcpReport, rtcp_len, packet_size);
+
+ rtc::PacketOptions options;
+ // Send a packet from `srtp_transport1_` to `srtp_transport2_` and verify
+ // that the packet can be successfully received and decrypted.
+ int prev_received_packets = transport_observer2_.rtcp_count();
+ ASSERT_TRUE(dtls_srtp_transport1_->SendRtcpPacket(&rtcp_packet1to2, options,
+ cricket::PF_SRTP_BYPASS));
+ ASSERT_TRUE(transport_observer2_.last_recv_rtcp_packet().data());
+ EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtcp_packet().data(),
+ kRtcpReport, rtcp_len));
+ EXPECT_EQ(prev_received_packets + 1, transport_observer2_.rtcp_count());
+
+ // Do the same thing in the opposite direction;
+ prev_received_packets = transport_observer1_.rtcp_count();
+ ASSERT_TRUE(dtls_srtp_transport2_->SendRtcpPacket(&rtcp_packet2to1, options,
+ cricket::PF_SRTP_BYPASS));
+ ASSERT_TRUE(transport_observer1_.last_recv_rtcp_packet().data());
+ EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtcp_packet().data(),
+ kRtcpReport, rtcp_len));
+ EXPECT_EQ(prev_received_packets + 1, transport_observer1_.rtcp_count());
+ }
+
+ void SendRecvRtpPacketsWithHeaderExtension(
+ const std::vector<int>& encrypted_header_ids) {
+ ASSERT_TRUE(dtls_srtp_transport1_);
+ ASSERT_TRUE(dtls_srtp_transport2_);
+ ASSERT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
+ ASSERT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
+
+ size_t rtp_len = sizeof(kPcmuFrameWithExtensions);
+ size_t packet_size = rtp_len + kRtpAuthTagLen;
+ rtc::Buffer rtp_packet_buffer(packet_size);
+ char* rtp_packet_data = rtp_packet_buffer.data<char>();
+ memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len);
+ // In order to be able to run this test function multiple times we can not
+ // use the same sequence number twice. Increase the sequence number by one.
+ rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_data) + 2,
+ ++sequence_number_);
+ rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
+ packet_size);
+ rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
+ packet_size);
+
+ char original_rtp_data[sizeof(kPcmuFrameWithExtensions)];
+ memcpy(original_rtp_data, rtp_packet_data, rtp_len);
+
+ rtc::PacketOptions options;
+ // Send a packet from `srtp_transport1_` to `srtp_transport2_` and verify
+ // that the packet can be successfully received and decrypted.
+ ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
+ cricket::PF_SRTP_BYPASS));
+ ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data());
+ EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(),
+ original_rtp_data, rtp_len));
+ // Get the encrypted packet from underneath packet transport and verify the
+ // data and header extension are actually encrypted.
+ auto fake_dtls_transport = static_cast<FakeDtlsTransport*>(
+ dtls_srtp_transport1_->rtp_packet_transport());
+ auto fake_ice_transport =
+ static_cast<FakeIceTransport*>(fake_dtls_transport->ice_transport());
+ EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(),
+ original_rtp_data, rtp_len));
+ CompareHeaderExtensions(reinterpret_cast<const char*>(
+ fake_ice_transport->last_sent_packet().data()),
+ fake_ice_transport->last_sent_packet().size(),
+ original_rtp_data, rtp_len, encrypted_header_ids,
+ false);
+
+ // Do the same thing in the opposite direction.
+ ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
+ cricket::PF_SRTP_BYPASS));
+ ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data());
+ EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(),
+ original_rtp_data, rtp_len));
+ // Get the encrypted packet from underneath packet transport and verify the
+ // data and header extension are actually encrypted.
+ fake_dtls_transport = static_cast<FakeDtlsTransport*>(
+ dtls_srtp_transport2_->rtp_packet_transport());
+ fake_ice_transport =
+ static_cast<FakeIceTransport*>(fake_dtls_transport->ice_transport());
+ EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(),
+ original_rtp_data, rtp_len));
+ CompareHeaderExtensions(reinterpret_cast<const char*>(
+ fake_ice_transport->last_sent_packet().data()),
+ fake_ice_transport->last_sent_packet().size(),
+ original_rtp_data, rtp_len, encrypted_header_ids,
+ false);
+ }
+
+ void SendRecvPackets() {
+ SendRecvRtpPackets();
+ SendRecvRtcpPackets();
+ }
+
+ rtc::AutoThread main_thread_;
+ std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport1_;
+ std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport2_;
+ webrtc::TransportObserver transport_observer1_;
+ webrtc::TransportObserver transport_observer2_;
+
+ int sequence_number_ = 0;
+ webrtc::test::ScopedKeyValueConfig field_trials_;
+};
+
+// Tests that if RTCP muxing is enabled and transports are set after RTP
+// transport finished the handshake, SRTP is set up.
+TEST_F(DtlsSrtpTransportTest, SetTransportsAfterHandshakeCompleteWithRtcpMux) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+ /*rtcp_mux_enabled=*/true);
+
+ auto rtp_dtls3 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtp_dtls4 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+ CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
+
+ dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr);
+ dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr);
+
+ SendRecvPackets();
+}
+
+// Tests that if RTCP muxing is not enabled and transports are set after both
+// RTP and RTCP transports finished the handshake, SRTP is set up.
+TEST_F(DtlsSrtpTransportTest,
+ SetTransportsAfterHandshakeCompleteWithoutRtcpMux) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "video", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "video", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+ rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+ auto rtp_dtls3 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls3 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls4 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls4 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
+ CompleteDtlsHandshake(rtcp_dtls3.get(), rtcp_dtls4.get());
+
+ dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), rtcp_dtls3.get());
+ dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), rtcp_dtls4.get());
+
+ SendRecvPackets();
+}
+
+// Tests if RTCP muxing is enabled, SRTP is set up as soon as the RTP DTLS
+// handshake is finished.
+TEST_F(DtlsSrtpTransportTest, SetTransportsBeforeHandshakeCompleteWithRtcpMux) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+ rtcp_dtls2.get(),
+ /*rtcp_mux_enabled=*/false);
+
+ dtls_srtp_transport1_->SetRtcpMuxEnabled(true);
+ dtls_srtp_transport2_->SetRtcpMuxEnabled(true);
+ CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+ SendRecvPackets();
+}
+
+// Tests if RTCP muxing is not enabled, SRTP is set up when both the RTP and
+// RTCP DTLS handshake are finished.
+TEST_F(DtlsSrtpTransportTest,
+ SetTransportsBeforeHandshakeCompleteWithoutRtcpMux) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+ rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+ CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+ EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
+ EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
+ CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
+ SendRecvPackets();
+}
+
+// Tests that if the DtlsTransport underneath is changed, the previous DTLS-SRTP
+// context will be reset and will be re-setup once the new transports' handshake
+// complete.
+TEST_F(DtlsSrtpTransportTest, DtlsSrtpResetAfterDtlsTransportChange) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+ /*rtcp_mux_enabled=*/true);
+
+ CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+ EXPECT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
+ EXPECT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
+
+ auto rtp_dtls3 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtp_dtls4 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+ // The previous context is reset.
+ dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr);
+ dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr);
+ EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
+ EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
+
+ // Re-setup.
+ CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
+ SendRecvPackets();
+}
+
+// Tests if only the RTP DTLS handshake complete, and then RTCP muxing is
+// enabled, SRTP is set up.
+TEST_F(DtlsSrtpTransportTest,
+ RtcpMuxEnabledAfterRtpTransportHandshakeComplete) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+ rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+ CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+ // Inactive because the RTCP transport handshake didn't complete.
+ EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
+ EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
+
+ dtls_srtp_transport1_->SetRtcpMuxEnabled(true);
+ dtls_srtp_transport2_->SetRtcpMuxEnabled(true);
+ // The transports should be active and be able to send packets when the
+ // RTCP muxing is enabled.
+ SendRecvPackets();
+}
+
+// Tests that when SetSend/RecvEncryptedHeaderExtensionIds is called, the SRTP
+// sessions are updated with new encryped header extension IDs immediately.
+TEST_F(DtlsSrtpTransportTest, EncryptedHeaderExtensionIdUpdated) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+ /*rtcp_mux_enabled=*/true);
+ CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+
+ std::vector<int> encrypted_headers;
+ encrypted_headers.push_back(kHeaderExtensionIDs[0]);
+ encrypted_headers.push_back(kHeaderExtensionIDs[1]);
+
+ dtls_srtp_transport1_->UpdateSendEncryptedHeaderExtensionIds(
+ encrypted_headers);
+ dtls_srtp_transport1_->UpdateRecvEncryptedHeaderExtensionIds(
+ encrypted_headers);
+ dtls_srtp_transport2_->UpdateSendEncryptedHeaderExtensionIds(
+ encrypted_headers);
+ dtls_srtp_transport2_->UpdateRecvEncryptedHeaderExtensionIds(
+ encrypted_headers);
+}
+
+// Tests if RTCP muxing is enabled. DtlsSrtpTransport is ready to send once the
+// RTP DtlsTransport is ready.
+TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithRtcpMux) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+ /*rtcp_mux_enabled=*/true);
+
+ rtp_dtls1->SetDestination(rtp_dtls2.get());
+ EXPECT_TRUE(transport_observer1_.ready_to_send());
+ EXPECT_TRUE(transport_observer2_.ready_to_send());
+}
+
+// Tests if RTCP muxing is not enabled. DtlsSrtpTransport is ready to send once
+// both the RTP and RTCP DtlsTransport are ready.
+TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithoutRtcpMux) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+ rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+ rtp_dtls1->SetDestination(rtp_dtls2.get());
+ EXPECT_FALSE(transport_observer1_.ready_to_send());
+ EXPECT_FALSE(transport_observer2_.ready_to_send());
+
+ rtcp_dtls1->SetDestination(rtcp_dtls2.get());
+ EXPECT_TRUE(transport_observer1_.ready_to_send());
+ EXPECT_TRUE(transport_observer2_.ready_to_send());
+}
+
+// Test that if an endpoint "fully" enables RTCP mux, setting the RTCP
+// transport to null, it *doesn't* reset its SRTP context. That would cause the
+// ROC and SRTCP index to be reset, causing replay detection and other errors
+// when attempting to unprotect packets.
+// Regression test for bugs.webrtc.org/8996
+TEST_F(DtlsSrtpTransportTest, SrtpSessionNotResetWhenRtcpTransportRemoved) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+ rtcp_dtls2.get(), /*rtcp_mux_enabled=*/true);
+ CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+ CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
+
+ // Send some RTCP packets, causing the SRTCP index to be incremented.
+ SendRecvRtcpPackets();
+
+ // Set RTCP transport to null, which previously would trigger this problem.
+ dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls1.get(), nullptr);
+
+ // Attempt to send more RTCP packets. If the issue occurred, one side would
+ // reset its context while the other would not, causing replay detection
+ // errors when a packet with a duplicate SRTCP index is received.
+ SendRecvRtcpPackets();
+}
+
+// Tests that RTCP packets can be sent and received if both sides actively reset
+// the SRTP parameters with the `active_reset_srtp_params_` flag.
+TEST_F(DtlsSrtpTransportTest, ActivelyResetSrtpParams) {
+ auto rtp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls1 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+ auto rtp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ auto rtcp_dtls2 = std::make_unique<FakeDtlsTransport>(
+ "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+ MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+ rtcp_dtls2.get(), /*rtcp_mux_enabled=*/true);
+ CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+ CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
+
+ // Send some RTCP packets, causing the SRTCP index to be incremented.
+ SendRecvRtcpPackets();
+
+ // Only set the `active_reset_srtp_params_` flag to be true one side.
+ dtls_srtp_transport1_->SetActiveResetSrtpParams(true);
+ // Set RTCP transport to null to trigger the SRTP parameters update.
+ dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls1.get(), nullptr);
+ dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls2.get(), nullptr);
+
+ // Sending some RTCP packets.
+ size_t rtcp_len = sizeof(kRtcpReport);
+ size_t packet_size = rtcp_len + 4 + kRtpAuthTagLen;
+ rtc::Buffer rtcp_packet_buffer(packet_size);
+ rtc::CopyOnWriteBuffer rtcp_packet(kRtcpReport, rtcp_len, packet_size);
+ int prev_received_packets = transport_observer2_.rtcp_count();
+ ASSERT_TRUE(dtls_srtp_transport1_->SendRtcpPacket(
+ &rtcp_packet, rtc::PacketOptions(), cricket::PF_SRTP_BYPASS));
+ // The RTCP packet is not exepected to be received because the SRTP parameters
+ // are only reset on one side and the SRTCP index is out of sync.
+ EXPECT_EQ(prev_received_packets, transport_observer2_.rtcp_count());
+
+ // Set the flag to be true on the other side.
+ dtls_srtp_transport2_->SetActiveResetSrtpParams(true);
+ // Set RTCP transport to null to trigger the SRTP parameters update.
+ dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls1.get(), nullptr);
+ dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls2.get(), nullptr);
+
+ // RTCP packets flow is expected to work just fine.
+ SendRecvRtcpPackets();
+}