diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc | 570 |
1 files changed, 570 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..5c4eac0050 --- /dev/null +++ b/third_party/libwebrtc/pc/dtls_srtp_transport_unittest.cc @@ -0,0 +1,570 @@ +/* + * 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_->SignalRtcpPacketReceived.connect( + &transport_observer1_, + &webrtc::TransportObserver::OnRtcpPacketReceived); + dtls_srtp_transport1_->SignalReadyToSend.connect( + &transport_observer1_, &webrtc::TransportObserver::OnReadyToSend); + + dtls_srtp_transport2_->SignalRtcpPacketReceived.connect( + &transport_observer2_, + &webrtc::TransportObserver::OnRtcpPacketReceived); + dtls_srtp_transport2_->SignalReadyToSend.connect( + &transport_observer2_, &webrtc::TransportObserver::OnReadyToSend); + 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(); +} |