From d3501adf17fda4ad270faee89b6fe4f4a496b147 Mon Sep 17 00:00:00 2001 From: zhihuang Date: Fri, 3 Mar 2017 14:39:06 -0800 Subject: [PATCH] Create the SrtpTransportInterface. Create the SrtpTransportInterface, a subclass of RtpTransportInterface, which allows the user to set the send and receive keys. The functionalities are implemented inside the RtpTransportAdapters on top of BaseChannel. BUG=webrtc:7013 Review-Url: https://codereview.webrtc.org/2714813004 Cr-Commit-Position: refs/heads/master@{#17023} --- webrtc/api/BUILD.gn | 1 + webrtc/api/ortc/ortcfactoryinterface.h | 9 + webrtc/api/ortc/ortcrtpsenderinterface.h | 2 +- webrtc/api/ortc/srtptransportinterface.h | 48 +++ webrtc/api/rtpparameters.h | 2 +- webrtc/ortc/BUILD.gn | 1 + webrtc/ortc/ortcfactory.cc | 51 ++- webrtc/ortc/ortcfactory.h | 6 + webrtc/ortc/ortcfactory_integrationtest.cc | 358 ++++++++++++++----- webrtc/ortc/ortcfactory_unittest.cc | 18 + webrtc/ortc/ortcrtpreceiver_unittest.cc | 2 +- webrtc/ortc/ortcrtpsender_unittest.cc | 2 +- webrtc/ortc/rtpparametersconversion.h | 2 +- webrtc/ortc/rtptransportadapter.cc | 90 ++++- webrtc/ortc/rtptransportadapter.h | 36 +- webrtc/ortc/rtptransportcontrolleradapter.cc | 58 ++- webrtc/ortc/rtptransportcontrolleradapter.h | 23 +- webrtc/ortc/srtptransport_unittest.cc | 167 +++++++++ 18 files changed, 758 insertions(+), 118 deletions(-) create mode 100644 webrtc/api/ortc/srtptransportinterface.h create mode 100644 webrtc/ortc/srtptransport_unittest.cc diff --git a/webrtc/api/BUILD.gn b/webrtc/api/BUILD.gn index 716f8c9c10..3447e924ad 100644 --- a/webrtc/api/BUILD.gn +++ b/webrtc/api/BUILD.gn @@ -94,6 +94,7 @@ rtc_source_set("ortc_api") { "ortc/packettransportinterface.h", "ortc/rtptransportcontrollerinterface.h", "ortc/rtptransportinterface.h", + "ortc/srtptransportinterface.h", "ortc/udptransportinterface.h", ] diff --git a/webrtc/api/ortc/ortcfactoryinterface.h b/webrtc/api/ortc/ortcfactoryinterface.h index 855e3b0b58..62ed90d067 100644 --- a/webrtc/api/ortc/ortcfactoryinterface.h +++ b/webrtc/api/ortc/ortcfactoryinterface.h @@ -23,6 +23,7 @@ #include "webrtc/api/ortc/packettransportinterface.h" #include "webrtc/api/ortc/rtptransportcontrollerinterface.h" #include "webrtc/api/ortc/rtptransportinterface.h" +#include "webrtc/api/ortc/srtptransportinterface.h" #include "webrtc/api/ortc/udptransportinterface.h" #include "webrtc/api/rtcerror.h" #include "webrtc/api/rtpparameters.h" @@ -126,6 +127,14 @@ class OrtcFactoryInterface { PacketTransportInterface* rtcp, RtpTransportControllerInterface* transport_controller) = 0; + // Creates an SrtpTransport which is an RTP transport that uses SRTP. + virtual RTCErrorOr> + CreateSrtpTransport( + const RtcpParameters& rtcp_parameters, + PacketTransportInterface* rtp, + PacketTransportInterface* rtcp, + RtpTransportControllerInterface* transport_controller) = 0; + // Returns the capabilities of an RTP sender of type |kind|. These // capabilities can be used to determine what RtpParameters to use to create // an RtpSender. diff --git a/webrtc/api/ortc/ortcrtpsenderinterface.h b/webrtc/api/ortc/ortcrtpsenderinterface.h index e369b539e4..a0fc208a58 100644 --- a/webrtc/api/ortc/ortcrtpsenderinterface.h +++ b/webrtc/api/ortc/ortcrtpsenderinterface.h @@ -18,8 +18,8 @@ #ifndef WEBRTC_API_ORTC_ORTCRTPSENDERINTERFACE_H_ #define WEBRTC_API_ORTC_ORTCRTPSENDERINTERFACE_H_ -#include "webrtc/api/mediatypes.h" #include "webrtc/api/mediastreaminterface.h" +#include "webrtc/api/mediatypes.h" #include "webrtc/api/ortc/rtptransportinterface.h" #include "webrtc/api/rtcerror.h" #include "webrtc/api/rtpparameters.h" diff --git a/webrtc/api/ortc/srtptransportinterface.h b/webrtc/api/ortc/srtptransportinterface.h new file mode 100644 index 0000000000..f64618c5dc --- /dev/null +++ b/webrtc/api/ortc/srtptransportinterface.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef WEBRTC_API_ORTC_SRTPTRANSPORTINTERFACE_H_ +#define WEBRTC_API_ORTC_SRTPTRANSPORTINTERFACE_H_ + +#include "webrtc/api/ortc/rtptransportinterface.h" +#include "webrtc/api/rtcerror.h" +#include "webrtc/media/base/cryptoparams.h" + +namespace webrtc { + +// The subclass of the RtpTransport which uses SRTP. The keying information +// is explicitly passed in from the application. +// +// If using SDP and SDES (RFC4568) for signaling, then after applying the +// answer, the negotiated keying information from the offer and answer would be +// set and the SRTP would be active. +// +// Note that Edge's implementation of ORTC provides a similar API point, called +// RTCSrtpSdesTransport: +// https://msdn.microsoft.com/en-us/library/mt502527(v=vs.85).aspx +class SrtpTransportInterface : public RtpTransportInterface { + public: + virtual ~SrtpTransportInterface() {} + + // There are some limitations of the current implementation: + // 1. Send and receive keys must use the same crypto suite. + // 2. The keys can't be changed after initially set. + // 3. The keys must be set before creating a sender/receiver using the SRTP + // transport. + // Set the SRTP keying material for sending RTP and RTCP. + virtual RTCError SetSrtpSendKey(const cricket::CryptoParams& params) = 0; + + // Set the SRTP keying material for receiving RTP and RTCP. + virtual RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params) = 0; +}; + +} // namespace webrtc + +#endif // WEBRTC_API_ORTC_SRTPTRANSPORTINTERFACE_H_ diff --git a/webrtc/api/rtpparameters.h b/webrtc/api/rtpparameters.h index e4fe47b845..a428804d54 100644 --- a/webrtc/api/rtpparameters.h +++ b/webrtc/api/rtpparameters.h @@ -16,8 +16,8 @@ #include #include "webrtc/api/mediatypes.h" -#include "webrtc/config.h" #include "webrtc/base/optional.h" +#include "webrtc/config.h" namespace webrtc { diff --git a/webrtc/ortc/BUILD.gn b/webrtc/ortc/BUILD.gn index ebba3b86ed..9e1c18f45a 100644 --- a/webrtc/ortc/BUILD.gn +++ b/webrtc/ortc/BUILD.gn @@ -58,6 +58,7 @@ if (rtc_include_tests) { "rtpparametersconversion_unittest.cc", "rtptransport_unittest.cc", "rtptransportcontroller_unittest.cc", + "srtptransport_unittest.cc", "testrtpparameters.cc", "testrtpparameters.h", ] diff --git a/webrtc/ortc/ortcfactory.cc b/webrtc/ortc/ortcfactory.cc index c0d54d16f0..8af61918af 100644 --- a/webrtc/ortc/ortcfactory.cc +++ b/webrtc/ortc/ortcfactory.cc @@ -11,11 +11,11 @@ #include "webrtc/ortc/ortcfactory.h" #include -#include #include // For std::move. +#include -#include "webrtc/api/proxy.h" #include "webrtc/api/mediastreamtrackproxy.h" +#include "webrtc/api/proxy.h" #include "webrtc/api/rtcerror.h" #include "webrtc/api/videosourceproxy.h" #include "webrtc/base/asyncpacketsocket.h" @@ -33,9 +33,9 @@ #include "webrtc/ortc/rtptransportcontrolleradapter.h" #include "webrtc/p2p/base/basicpacketsocketfactory.h" #include "webrtc/p2p/base/udptransport.h" +#include "webrtc/pc/audiotrack.h" #include "webrtc/pc/channelmanager.h" #include "webrtc/pc/localaudiosource.h" -#include "webrtc/pc/audiotrack.h" #include "webrtc/pc/videocapturertracksource.h" #include "webrtc/pc/videotrack.h" @@ -78,6 +78,14 @@ PROXY_METHOD4(RTCErrorOr>, PacketTransportInterface*, PacketTransportInterface*, RtpTransportControllerInterface*) + +PROXY_METHOD4(RTCErrorOr>, + CreateSrtpTransport, + const RtcpParameters&, + PacketTransportInterface*, + PacketTransportInterface*, + RtpTransportControllerInterface*) + PROXY_CONSTMETHOD1(RtpCapabilities, GetRtpSenderCapabilities, cricket::MediaType) @@ -250,6 +258,43 @@ OrtcFactory::CreateRtpTransport( } } +RTCErrorOr> +OrtcFactory::CreateSrtpTransport( + const RtcpParameters& rtcp_parameters, + PacketTransportInterface* rtp, + PacketTransportInterface* rtcp, + RtpTransportControllerInterface* transport_controller) { + RTC_DCHECK_RUN_ON(signaling_thread_); + RtcpParameters copied_parameters = rtcp_parameters; + if (copied_parameters.cname.empty()) { + copied_parameters.cname = default_cname_; + } + if (transport_controller) { + return transport_controller->GetInternal()->CreateProxiedSrtpTransport( + copied_parameters, rtp, rtcp); + } else { + // If |transport_controller| is null, create one automatically, which the + // returned SrtpTransport will own. + auto controller_result = CreateRtpTransportController(); + if (!controller_result.ok()) { + return controller_result.MoveError(); + } + auto controller = controller_result.MoveValue(); + auto transport_result = + controller->GetInternal()->CreateProxiedSrtpTransport(copied_parameters, + rtp, rtcp); + // If SrtpTransport was successfully created, transfer ownership of + // |rtp_transport_controller|. Otherwise it will go out of scope and be + // deleted automatically. + if (transport_result.ok()) { + transport_result.value() + ->GetInternal() + ->TakeOwnershipOfRtpTransportController(std::move(controller)); + } + return transport_result; + } +} + RtpCapabilities OrtcFactory::GetRtpSenderCapabilities( cricket::MediaType kind) const { RTC_DCHECK_RUN_ON(signaling_thread_); diff --git a/webrtc/ortc/ortcfactory.h b/webrtc/ortc/ortcfactory.h index 71f525d884..8d67a3fa95 100644 --- a/webrtc/ortc/ortcfactory.h +++ b/webrtc/ortc/ortcfactory.h @@ -49,6 +49,12 @@ class OrtcFactory : public OrtcFactoryInterface { PacketTransportInterface* rtcp, RtpTransportControllerInterface* transport_controller) override; + RTCErrorOr> CreateSrtpTransport( + const RtcpParameters& rtcp_parameters, + PacketTransportInterface* rtp, + PacketTransportInterface* rtcp, + RtpTransportControllerInterface* transport_controller) override; + RtpCapabilities GetRtpSenderCapabilities( cricket::MediaType kind) const override; diff --git a/webrtc/ortc/ortcfactory_integrationtest.cc b/webrtc/ortc/ortcfactory_integrationtest.cc index e935f068a3..0f782b2b32 100644 --- a/webrtc/ortc/ortcfactory_integrationtest.cc +++ b/webrtc/ortc/ortcfactory_integrationtest.cc @@ -25,13 +25,38 @@ namespace { -const int kDefaultTimeout = 10000; // 10 seconds. +const int kDefaultTimeout = 10000; // 10 seconds. +const int kReceivingDuration = 1000; // 1 second. // Default number of audio/video frames to wait for before considering a test a // success. const int kDefaultNumFrames = 3; const rtc::IPAddress kIPv4LocalHostAddress = rtc::IPAddress(0x7F000001); // 127.0.0.1 +static const char kTestKeyParams1[] = + "inline:WVNfX19zZW1jdGwgKskgewkyMjA7fQp9CnVubGVz"; +static const char kTestKeyParams2[] = + "inline:PS1uQCVeeCFCanVmcjkpaywjNWhcYD0mXXtxaVBR"; +static const char kTestKeyParams3[] = + "inline:WVNfX19zZW1jdGwgKskgewkyMjA7fQp9CnVubGVa"; +static const char kTestKeyParams4[] = + "inline:WVNfX19zZW1jdGwgKskgewkyMjA7fQp9CnVubGVb"; +static const cricket::CryptoParams kTestCryptoParams1(1, + "AES_CM_128_HMAC_SHA1_80", + kTestKeyParams1, + ""); +static const cricket::CryptoParams kTestCryptoParams2(1, + "AES_CM_128_HMAC_SHA1_80", + kTestKeyParams2, + ""); +static const cricket::CryptoParams kTestCryptoParams3(1, + "AES_CM_128_HMAC_SHA1_80", + kTestKeyParams3, + ""); +static const cricket::CryptoParams kTestCryptoParams4(1, + "AES_CM_128_HMAC_SHA1_80", + kTestKeyParams4, + ""); } // namespace namespace webrtc { @@ -71,6 +96,9 @@ class OrtcFactoryIntegrationTest : public testing::Test { typedef std::pair, std::unique_ptr> RtpTransportPair; + typedef std::pair, + std::unique_ptr> + SrtpTransportPair; typedef std::pair, std::unique_ptr> RtpTransportControllerPair; @@ -115,6 +143,20 @@ class OrtcFactoryIntegrationTest : public testing::Test { return {transport_result1.MoveValue(), transport_result2.MoveValue()}; } + SrtpTransportPair CreateSrtpTransportPair( + const RtcpParameters& rtcp_parameters, + const UdpTransportPair& rtp_udp_transports, + const UdpTransportPair& rtcp_udp_transports, + const RtpTransportControllerPair& transport_controllers) { + auto transport_result1 = ortc_factory1_->CreateSrtpTransport( + rtcp_parameters, rtp_udp_transports.first.get(), + rtcp_udp_transports.first.get(), transport_controllers.first.get()); + auto transport_result2 = ortc_factory2_->CreateSrtpTransport( + rtcp_parameters, rtp_udp_transports.second.get(), + rtcp_udp_transports.second.get(), transport_controllers.second.get()); + return {transport_result1.MoveValue(), transport_result2.MoveValue()}; + } + // For convenience when |rtcp_udp_transports| and |transport_controllers| // aren't needed. RtpTransportPair CreateRtpTransportPair( @@ -125,6 +167,38 @@ class OrtcFactoryIntegrationTest : public testing::Test { RtpTransportControllerPair()); } + SrtpTransportPair CreateSrtpTransportPairAndSetKeys( + const RtcpParameters& rtcp_parameters, + const UdpTransportPair& rtp_udp_transports) { + SrtpTransportPair srtp_transports = CreateSrtpTransportPair( + rtcp_parameters, rtp_udp_transports, UdpTransportPair(), + RtpTransportControllerPair()); + EXPECT_TRUE(srtp_transports.first->SetSrtpSendKey(kTestCryptoParams1).ok()); + EXPECT_TRUE( + srtp_transports.first->SetSrtpReceiveKey(kTestCryptoParams2).ok()); + EXPECT_TRUE( + srtp_transports.second->SetSrtpSendKey(kTestCryptoParams2).ok()); + EXPECT_TRUE( + srtp_transports.second->SetSrtpReceiveKey(kTestCryptoParams1).ok()); + return srtp_transports; + } + + SrtpTransportPair CreateSrtpTransportPairAndSetMismatchingKeys( + const RtcpParameters& rtcp_parameters, + const UdpTransportPair& rtp_udp_transports) { + SrtpTransportPair srtp_transports = CreateSrtpTransportPair( + rtcp_parameters, rtp_udp_transports, UdpTransportPair(), + RtpTransportControllerPair()); + EXPECT_TRUE(srtp_transports.first->SetSrtpSendKey(kTestCryptoParams1).ok()); + EXPECT_TRUE( + srtp_transports.first->SetSrtpReceiveKey(kTestCryptoParams2).ok()); + EXPECT_TRUE( + srtp_transports.second->SetSrtpSendKey(kTestCryptoParams1).ok()); + EXPECT_TRUE( + srtp_transports.second->SetSrtpReceiveKey(kTestCryptoParams2).ok()); + return srtp_transports; + } + // Ends up using fake audio capture module, which was passed into OrtcFactory // on creation. rtc::scoped_refptr CreateLocalAudioTrack( @@ -152,6 +226,110 @@ class OrtcFactoryIntegrationTest : public testing::Test { ortc_factory->CreateVideoTrack(id, source)); } + // Helper function used to test two way RTP senders and receivers with basic + // configurations. + // If |expect_success| is true, waits for kDefaultTimeout for + // kDefaultNumFrames frames to be received by all RtpReceivers. + // If |expect_success| is false, simply waits for |kReceivingDuration|, and + // stores the number of received frames in |received_audio_frame1_| etc. + void BasicTwoWayRtpSendersAndReceiversTest(RtpTransportPair srtp_transports, + bool expect_success) { + received_audio_frames1_ = 0; + received_audio_frames2_ = 0; + rendered_video_frames1_ = 0; + rendered_video_frames2_ = 0; + // Create all the senders and receivers (four per endpoint). + auto audio_sender_result1 = ortc_factory1_->CreateRtpSender( + cricket::MEDIA_TYPE_AUDIO, srtp_transports.first.get()); + auto video_sender_result1 = ortc_factory1_->CreateRtpSender( + cricket::MEDIA_TYPE_VIDEO, srtp_transports.first.get()); + auto audio_receiver_result1 = ortc_factory1_->CreateRtpReceiver( + cricket::MEDIA_TYPE_AUDIO, srtp_transports.first.get()); + auto video_receiver_result1 = ortc_factory1_->CreateRtpReceiver( + cricket::MEDIA_TYPE_VIDEO, srtp_transports.first.get()); + ASSERT_TRUE(audio_sender_result1.ok()); + ASSERT_TRUE(video_sender_result1.ok()); + ASSERT_TRUE(audio_receiver_result1.ok()); + ASSERT_TRUE(video_receiver_result1.ok()); + auto audio_sender1 = audio_sender_result1.MoveValue(); + auto video_sender1 = video_sender_result1.MoveValue(); + auto audio_receiver1 = audio_receiver_result1.MoveValue(); + auto video_receiver1 = video_receiver_result1.MoveValue(); + + auto audio_sender_result2 = ortc_factory2_->CreateRtpSender( + cricket::MEDIA_TYPE_AUDIO, srtp_transports.second.get()); + auto video_sender_result2 = ortc_factory2_->CreateRtpSender( + cricket::MEDIA_TYPE_VIDEO, srtp_transports.second.get()); + auto audio_receiver_result2 = ortc_factory2_->CreateRtpReceiver( + cricket::MEDIA_TYPE_AUDIO, srtp_transports.second.get()); + auto video_receiver_result2 = ortc_factory2_->CreateRtpReceiver( + cricket::MEDIA_TYPE_VIDEO, srtp_transports.second.get()); + ASSERT_TRUE(audio_sender_result2.ok()); + ASSERT_TRUE(video_sender_result2.ok()); + ASSERT_TRUE(audio_receiver_result2.ok()); + ASSERT_TRUE(video_receiver_result2.ok()); + auto audio_sender2 = audio_sender_result2.MoveValue(); + auto video_sender2 = video_sender_result2.MoveValue(); + auto audio_receiver2 = audio_receiver_result2.MoveValue(); + auto video_receiver2 = video_receiver_result2.MoveValue(); + + // Add fake tracks. + RTCError error = audio_sender1->SetTrack( + CreateLocalAudioTrack("audio", ortc_factory1_.get())); + EXPECT_TRUE(error.ok()); + error = video_sender1->SetTrack( + CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory1_.get())); + EXPECT_TRUE(error.ok()); + error = audio_sender2->SetTrack( + CreateLocalAudioTrack("audio", ortc_factory2_.get())); + EXPECT_TRUE(error.ok()); + error = video_sender2->SetTrack( + CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory2_.get())); + EXPECT_TRUE(error.ok()); + + // "sent_X_parameters1" are the parameters that endpoint 1 sends with and + // endpoint 2 receives with. + RtpParameters sent_opus_parameters1 = + MakeMinimalOpusParametersWithSsrc(0xdeadbeef); + RtpParameters sent_vp8_parameters1 = + MakeMinimalVp8ParametersWithSsrc(0xbaadfeed); + RtpParameters sent_opus_parameters2 = + MakeMinimalOpusParametersWithSsrc(0x13333337); + RtpParameters sent_vp8_parameters2 = + MakeMinimalVp8ParametersWithSsrc(0x12345678); + + // Configure the senders' and receivers' parameters. + EXPECT_TRUE(audio_receiver1->Receive(sent_opus_parameters2).ok()); + EXPECT_TRUE(video_receiver1->Receive(sent_vp8_parameters2).ok()); + EXPECT_TRUE(audio_receiver2->Receive(sent_opus_parameters1).ok()); + EXPECT_TRUE(video_receiver2->Receive(sent_vp8_parameters1).ok()); + EXPECT_TRUE(audio_sender1->Send(sent_opus_parameters1).ok()); + EXPECT_TRUE(video_sender1->Send(sent_vp8_parameters1).ok()); + EXPECT_TRUE(audio_sender2->Send(sent_opus_parameters2).ok()); + EXPECT_TRUE(video_sender2->Send(sent_vp8_parameters2).ok()); + + FakeVideoTrackRenderer fake_video_renderer1( + static_cast(video_receiver1->GetTrack().get())); + FakeVideoTrackRenderer fake_video_renderer2( + static_cast(video_receiver2->GetTrack().get())); + + if (expect_success) { + EXPECT_TRUE_WAIT( + fake_audio_capture_module1_->frames_received() > kDefaultNumFrames && + fake_video_renderer1.num_rendered_frames() > kDefaultNumFrames && + fake_audio_capture_module2_->frames_received() > + kDefaultNumFrames && + fake_video_renderer1.num_rendered_frames() > kDefaultNumFrames, + kDefaultTimeout); + } else { + WAIT(false, kReceivingDuration); + rendered_video_frames1_ = fake_video_renderer1.num_rendered_frames(); + rendered_video_frames2_ = fake_video_renderer2.num_rendered_frames(); + received_audio_frames1_ = fake_audio_capture_module1_->frames_received(); + received_audio_frames2_ = fake_audio_capture_module2_->frames_received(); + } + } + rtc::PhysicalSocketServer physical_socket_server_; rtc::VirtualSocketServer virtual_socket_server_; rtc::Thread network_thread_; @@ -162,6 +340,10 @@ class OrtcFactoryIntegrationTest : public testing::Test { std::unique_ptr ortc_factory2_; // Actually owned by video tracks. std::vector fake_video_capturers_; + int received_audio_frames1_ = 0; + int received_audio_frames2_ = 0; + int rendered_video_frames1_ = 0; + int rendered_video_frames2_ = 0; }; // Very basic end-to-end test with a single pair of audio RTP sender and @@ -285,102 +467,75 @@ TEST_F(OrtcFactoryIntegrationTest, auto udp_transports = CreateAndConnectUdpTransportPair(); auto rtp_transports = CreateRtpTransportPair(MakeRtcpMuxParameters(), udp_transports); + bool expect_success = true; + BasicTwoWayRtpSendersAndReceiversTest(std::move(rtp_transports), + expect_success); +} - // Create all the senders and receivers (four per endpoint). - auto audio_sender_result1 = ortc_factory1_->CreateRtpSender( - cricket::MEDIA_TYPE_AUDIO, rtp_transports.first.get()); - auto video_sender_result1 = ortc_factory1_->CreateRtpSender( - cricket::MEDIA_TYPE_VIDEO, rtp_transports.first.get()); - auto audio_receiver_result1 = ortc_factory1_->CreateRtpReceiver( - cricket::MEDIA_TYPE_AUDIO, rtp_transports.first.get()); - auto video_receiver_result1 = ortc_factory1_->CreateRtpReceiver( - cricket::MEDIA_TYPE_VIDEO, rtp_transports.first.get()); - ASSERT_TRUE(audio_sender_result1.ok()); - ASSERT_TRUE(video_sender_result1.ok()); - ASSERT_TRUE(audio_receiver_result1.ok()); - ASSERT_TRUE(video_receiver_result1.ok()); - auto audio_sender1 = audio_sender_result1.MoveValue(); - auto video_sender1 = video_sender_result1.MoveValue(); - auto audio_receiver1 = audio_receiver_result1.MoveValue(); - auto video_receiver1 = video_receiver_result1.MoveValue(); +TEST_F(OrtcFactoryIntegrationTest, + BasicTwoWayAudioVideoSrtpSendersAndReceivers) { + auto udp_transports = CreateAndConnectUdpTransportPair(); + auto srtp_transports = CreateSrtpTransportPairAndSetKeys( + MakeRtcpMuxParameters(), udp_transports); + bool expect_success = true; + BasicTwoWayRtpSendersAndReceiversTest(std::move(srtp_transports), + expect_success); +} - auto audio_sender_result2 = ortc_factory2_->CreateRtpSender( - cricket::MEDIA_TYPE_AUDIO, rtp_transports.second.get()); - auto video_sender_result2 = ortc_factory2_->CreateRtpSender( - cricket::MEDIA_TYPE_VIDEO, rtp_transports.second.get()); - auto audio_receiver_result2 = ortc_factory2_->CreateRtpReceiver( - cricket::MEDIA_TYPE_AUDIO, rtp_transports.second.get()); - auto video_receiver_result2 = ortc_factory2_->CreateRtpReceiver( - cricket::MEDIA_TYPE_VIDEO, rtp_transports.second.get()); - ASSERT_TRUE(audio_sender_result2.ok()); - ASSERT_TRUE(video_sender_result2.ok()); - ASSERT_TRUE(audio_receiver_result2.ok()); - ASSERT_TRUE(video_receiver_result2.ok()); - auto audio_sender2 = audio_sender_result2.MoveValue(); - auto video_sender2 = video_sender_result2.MoveValue(); - auto audio_receiver2 = audio_receiver_result2.MoveValue(); - auto video_receiver2 = video_receiver_result2.MoveValue(); +// Tests that the packets cannot be decoded if the keys are mismatched. +TEST_F(OrtcFactoryIntegrationTest, SrtpSendersAndReceiversWithMismatchingKeys) { + auto udp_transports = CreateAndConnectUdpTransportPair(); + auto srtp_transports = CreateSrtpTransportPairAndSetMismatchingKeys( + MakeRtcpMuxParameters(), udp_transports); + bool expect_success = false; + BasicTwoWayRtpSendersAndReceiversTest(std::move(srtp_transports), + expect_success); + // No frames are expected to be decoded. + EXPECT_TRUE(received_audio_frames1_ == 0 && received_audio_frames2_ == 0 && + rendered_video_frames1_ == 0 && rendered_video_frames2_ == 0); +} - // Add fake tracks. - RTCError error = audio_sender1->SetTrack( - CreateLocalAudioTrack("audio", ortc_factory1_.get())); - EXPECT_TRUE(error.ok()); - error = video_sender1->SetTrack( - CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory1_.get())); - EXPECT_TRUE(error.ok()); - error = audio_sender2->SetTrack( - CreateLocalAudioTrack("audio", ortc_factory2_.get())); - EXPECT_TRUE(error.ok()); - error = video_sender2->SetTrack( - CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory2_.get())); - EXPECT_TRUE(error.ok()); +// Tests that the frames cannot be decoded if only one side uses SRTP. +TEST_F(OrtcFactoryIntegrationTest, OneSideSrtpSenderAndReceiver) { + auto rtcp_parameters = MakeRtcpMuxParameters(); + auto udp_transports = CreateAndConnectUdpTransportPair(); + auto rtcp_udp_transports = UdpTransportPair(); + auto transport_controllers = RtpTransportControllerPair(); + auto transport_result1 = ortc_factory1_->CreateRtpTransport( + rtcp_parameters, udp_transports.first.get(), + rtcp_udp_transports.first.get(), transport_controllers.first.get()); + auto transport_result2 = ortc_factory2_->CreateSrtpTransport( + rtcp_parameters, udp_transports.second.get(), + rtcp_udp_transports.second.get(), transport_controllers.second.get()); - // "sent_X_parameters1" are the parameters that endpoint 1 sends with and - // endpoint 2 receives with. - RtpParameters sent_opus_parameters1 = - MakeMinimalOpusParametersWithSsrc(0xdeadbeef); - RtpParameters sent_vp8_parameters1 = - MakeMinimalVp8ParametersWithSsrc(0xbaadfeed); - RtpParameters sent_opus_parameters2 = - MakeMinimalOpusParametersWithSsrc(0x13333337); - RtpParameters sent_vp8_parameters2 = - MakeMinimalVp8ParametersWithSsrc(0x12345678); + auto rtp_transport = transport_result1.MoveValue(); + auto srtp_transport = transport_result2.MoveValue(); + EXPECT_TRUE(srtp_transport->SetSrtpSendKey(kTestCryptoParams1).ok()); + EXPECT_TRUE(srtp_transport->SetSrtpReceiveKey(kTestCryptoParams2).ok()); + bool expect_success = false; + BasicTwoWayRtpSendersAndReceiversTest( + {std::move(rtp_transport), std::move(srtp_transport)}, expect_success); - // Configure the senders' and receivers' parameters. - EXPECT_TRUE(audio_receiver1->Receive(sent_opus_parameters2).ok()); - EXPECT_TRUE(video_receiver1->Receive(sent_vp8_parameters2).ok()); - EXPECT_TRUE(audio_receiver2->Receive(sent_opus_parameters1).ok()); - EXPECT_TRUE(video_receiver2->Receive(sent_vp8_parameters1).ok()); - EXPECT_TRUE(audio_sender1->Send(sent_opus_parameters1).ok()); - EXPECT_TRUE(video_sender1->Send(sent_vp8_parameters1).ok()); - EXPECT_TRUE(audio_sender2->Send(sent_opus_parameters2).ok()); - EXPECT_TRUE(video_sender2->Send(sent_vp8_parameters2).ok()); - - FakeVideoTrackRenderer fake_video_renderer1( - static_cast(video_receiver1->GetTrack().get())); - FakeVideoTrackRenderer fake_video_renderer2( - static_cast(video_receiver2->GetTrack().get())); - - // Senders and receivers are connected and configured; audio and video frames - // should be able to flow at this point. - EXPECT_TRUE_WAIT( - fake_audio_capture_module1_->frames_received() > kDefaultNumFrames && - fake_video_renderer1.num_rendered_frames() > kDefaultNumFrames && - fake_audio_capture_module2_->frames_received() > kDefaultNumFrames && - fake_video_renderer2.num_rendered_frames() > kDefaultNumFrames, - kDefaultTimeout); + // The SRTP side is not expected to decode any audio or video frames. + // The RTP side is not expected to decode any video frames while it is + // possible that the encrypted audio frames can be accidentally decoded which + // is why received_audio_frames1_ is not validated. + EXPECT_TRUE(received_audio_frames2_ == 0 && rendered_video_frames1_ == 0 && + rendered_video_frames2_ == 0); } // End-to-end test with two pairs of RTP senders and receivers, for audio and // video. Unlike the test above, this attempts to make the parameters as -// complex as possible. +// complex as possible. The senders and receivers use the SRTP transport with +// different keys. // // Uses non-muxed RTCP, with separate audio/video transports, and a full set of // parameters, as would normally be used in a PeerConnection. // // TODO(deadbeef): Update this test as more audio/video features become // supported. -TEST_F(OrtcFactoryIntegrationTest, FullTwoWayAudioVideoRtpSendersAndReceivers) { +TEST_F(OrtcFactoryIntegrationTest, + FullTwoWayAudioVideoSrtpSendersAndReceivers) { // We want four pairs of UDP transports for this test, for audio/video and // RTP/RTCP. auto audio_rtp_udp_transports = CreateAndConnectUdpTransportPair(); @@ -394,26 +549,37 @@ TEST_F(OrtcFactoryIntegrationTest, FullTwoWayAudioVideoRtpSendersAndReceivers) { RtcpParameters audio_rtcp_parameters; audio_rtcp_parameters.mux = false; - auto audio_rtp_transports = - CreateRtpTransportPair(audio_rtcp_parameters, audio_rtp_udp_transports, - audio_rtcp_udp_transports, transport_controllers); + auto audio_srtp_transports = + CreateSrtpTransportPair(audio_rtcp_parameters, audio_rtp_udp_transports, + audio_rtcp_udp_transports, transport_controllers); RtcpParameters video_rtcp_parameters; video_rtcp_parameters.mux = false; video_rtcp_parameters.reduced_size = true; - auto video_rtp_transports = - CreateRtpTransportPair(video_rtcp_parameters, video_rtp_udp_transports, - video_rtcp_udp_transports, transport_controllers); + auto video_srtp_transports = + CreateSrtpTransportPair(video_rtcp_parameters, video_rtp_udp_transports, + video_rtcp_udp_transports, transport_controllers); + + // Set keys for SRTP transports. + audio_srtp_transports.first->SetSrtpSendKey(kTestCryptoParams1); + audio_srtp_transports.first->SetSrtpReceiveKey(kTestCryptoParams2); + video_srtp_transports.first->SetSrtpSendKey(kTestCryptoParams3); + video_srtp_transports.first->SetSrtpReceiveKey(kTestCryptoParams4); + + audio_srtp_transports.second->SetSrtpSendKey(kTestCryptoParams2); + audio_srtp_transports.second->SetSrtpReceiveKey(kTestCryptoParams1); + video_srtp_transports.second->SetSrtpSendKey(kTestCryptoParams4); + video_srtp_transports.second->SetSrtpReceiveKey(kTestCryptoParams3); // Create all the senders and receivers (four per endpoint). auto audio_sender_result1 = ortc_factory1_->CreateRtpSender( - cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.first.get()); + cricket::MEDIA_TYPE_AUDIO, audio_srtp_transports.first.get()); auto video_sender_result1 = ortc_factory1_->CreateRtpSender( - cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.first.get()); + cricket::MEDIA_TYPE_VIDEO, video_srtp_transports.first.get()); auto audio_receiver_result1 = ortc_factory1_->CreateRtpReceiver( - cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.first.get()); + cricket::MEDIA_TYPE_AUDIO, audio_srtp_transports.first.get()); auto video_receiver_result1 = ortc_factory1_->CreateRtpReceiver( - cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.first.get()); + cricket::MEDIA_TYPE_VIDEO, video_srtp_transports.first.get()); ASSERT_TRUE(audio_sender_result1.ok()); ASSERT_TRUE(video_sender_result1.ok()); ASSERT_TRUE(audio_receiver_result1.ok()); @@ -424,13 +590,13 @@ TEST_F(OrtcFactoryIntegrationTest, FullTwoWayAudioVideoRtpSendersAndReceivers) { auto video_receiver1 = video_receiver_result1.MoveValue(); auto audio_sender_result2 = ortc_factory2_->CreateRtpSender( - cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.second.get()); + cricket::MEDIA_TYPE_AUDIO, audio_srtp_transports.second.get()); auto video_sender_result2 = ortc_factory2_->CreateRtpSender( - cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.second.get()); + cricket::MEDIA_TYPE_VIDEO, video_srtp_transports.second.get()); auto audio_receiver_result2 = ortc_factory2_->CreateRtpReceiver( - cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.second.get()); + cricket::MEDIA_TYPE_AUDIO, audio_srtp_transports.second.get()); auto video_receiver_result2 = ortc_factory2_->CreateRtpReceiver( - cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.second.get()); + cricket::MEDIA_TYPE_VIDEO, video_srtp_transports.second.get()); ASSERT_TRUE(audio_sender_result2.ok()); ASSERT_TRUE(video_sender_result2.ok()); ASSERT_TRUE(audio_receiver_result2.ok()); diff --git a/webrtc/ortc/ortcfactory_unittest.cc b/webrtc/ortc/ortcfactory_unittest.cc index 80e679b10e..200939adaa 100644 --- a/webrtc/ortc/ortcfactory_unittest.cc +++ b/webrtc/ortc/ortcfactory_unittest.cc @@ -82,6 +82,24 @@ TEST_F(OrtcFactoryTest, CreateRtpTransportWithAndWithoutMux) { EXPECT_TRUE(result.ok()); } +// Simple test for the successful cases of CreateSrtpTransport. +TEST_F(OrtcFactoryTest, CreateSrtpTransport) { + rtc::FakePacketTransport rtp("rtp"); + rtc::FakePacketTransport rtcp("rtcp"); + // With muxed RTCP. + RtcpParameters rtcp_parameters; + rtcp_parameters.mux = true; + auto result = ortc_factory_->CreateSrtpTransport(rtcp_parameters, &rtp, + nullptr, nullptr); + EXPECT_TRUE(result.ok()); + result.MoveValue().reset(); + // With non-muxed RTCP. + rtcp_parameters.mux = false; + result = + ortc_factory_->CreateSrtpTransport(rtcp_parameters, &rtp, &rtcp, nullptr); + EXPECT_TRUE(result.ok()); +} + // If no CNAME is provided, one should be generated and returned by // GetRtpParameters. TEST_F(OrtcFactoryTest, CreateRtpTransportGeneratesCname) { diff --git a/webrtc/ortc/ortcrtpreceiver_unittest.cc b/webrtc/ortc/ortcrtpreceiver_unittest.cc index 1764af0db5..aa7d81d420 100644 --- a/webrtc/ortc/ortcrtpreceiver_unittest.cc +++ b/webrtc/ortc/ortcrtpreceiver_unittest.cc @@ -12,9 +12,9 @@ #include "webrtc/base/gunit.h" #include "webrtc/media/base/fakemediaengine.h" -#include "webrtc/p2p/base/fakepackettransport.h" #include "webrtc/ortc/ortcfactory.h" #include "webrtc/ortc/testrtpparameters.h" +#include "webrtc/p2p/base/fakepackettransport.h" #include "webrtc/pc/test/fakevideotracksource.h" namespace webrtc { diff --git a/webrtc/ortc/ortcrtpsender_unittest.cc b/webrtc/ortc/ortcrtpsender_unittest.cc index 954b9978a3..47cb337cb5 100644 --- a/webrtc/ortc/ortcrtpsender_unittest.cc +++ b/webrtc/ortc/ortcrtpsender_unittest.cc @@ -12,9 +12,9 @@ #include "webrtc/base/gunit.h" #include "webrtc/media/base/fakemediaengine.h" -#include "webrtc/p2p/base/fakepackettransport.h" #include "webrtc/ortc/ortcfactory.h" #include "webrtc/ortc/testrtpparameters.h" +#include "webrtc/p2p/base/fakepackettransport.h" #include "webrtc/pc/test/fakevideotracksource.h" namespace webrtc { diff --git a/webrtc/ortc/rtpparametersconversion.h b/webrtc/ortc/rtpparametersconversion.h index a1680a205a..a466895bf4 100644 --- a/webrtc/ortc/rtpparametersconversion.h +++ b/webrtc/ortc/rtpparametersconversion.h @@ -17,8 +17,8 @@ #include "webrtc/api/rtcerror.h" #include "webrtc/api/rtpparameters.h" #include "webrtc/base/optional.h" -#include "webrtc/pc/mediasession.h" #include "webrtc/media/base/codec.h" +#include "webrtc/pc/mediasession.h" namespace webrtc { diff --git a/webrtc/ortc/rtptransportadapter.cc b/webrtc/ortc/rtptransportadapter.cc index 439f9a83b4..3982f2d5f7 100644 --- a/webrtc/ortc/rtptransportadapter.cc +++ b/webrtc/ortc/rtptransportadapter.cc @@ -32,6 +32,20 @@ RtpTransportAdapter* GetInternal() override { } END_PROXY_MAP() +BEGIN_OWNED_PROXY_MAP(SrtpTransport) +PROXY_SIGNALING_THREAD_DESTRUCTOR() +PROXY_CONSTMETHOD0(PacketTransportInterface*, GetRtpPacketTransport) +PROXY_CONSTMETHOD0(PacketTransportInterface*, GetRtcpPacketTransport) +PROXY_METHOD1(RTCError, SetRtcpParameters, const RtcpParameters&) +PROXY_CONSTMETHOD0(RtcpParameters, GetRtcpParameters) +PROXY_METHOD1(RTCError, SetSrtpSendKey, const cricket::CryptoParams&) +PROXY_METHOD1(RTCError, SetSrtpReceiveKey, const cricket::CryptoParams&) +protected: +RtpTransportAdapter* GetInternal() override { + return internal(); +} +END_PROXY_MAP() + // static RTCErrorOr> RtpTransportAdapter::CreateProxied( @@ -64,7 +78,43 @@ RtpTransportAdapter::CreateProxied( rtp_transport_controller->signaling_thread(), rtp_transport_controller->worker_thread(), std::unique_ptr(new RtpTransportAdapter( - rtcp_parameters, rtp, rtcp, rtp_transport_controller))); + rtcp_parameters, rtp, rtcp, rtp_transport_controller, + /*is_srtp_transport*/ false))); +} + +RTCErrorOr> +RtpTransportAdapter::CreateSrtpProxied( + const RtcpParameters& rtcp_parameters, + PacketTransportInterface* rtp, + PacketTransportInterface* rtcp, + RtpTransportControllerAdapter* rtp_transport_controller) { + if (!rtp) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Must provide an RTP packet transport."); + } + if (!rtcp_parameters.mux && !rtcp) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "Must provide an RTCP packet transport when RTCP muxing is not used."); + } + if (rtcp_parameters.mux && rtcp) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Creating an RtpTransport with RTCP muxing enabled, " + "with a separate RTCP packet transport?"); + } + if (!rtp_transport_controller) { + // Since OrtcFactory::CreateRtpTransport creates an RtpTransportController + // automatically when one isn't passed in, this should never be reached. + RTC_NOTREACHED(); + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Must provide an RTP transport controller."); + } + return SrtpTransportProxyWithInternal::Create( + rtp_transport_controller->signaling_thread(), + rtp_transport_controller->worker_thread(), + std::unique_ptr(new RtpTransportAdapter( + rtcp_parameters, rtp, rtcp, rtp_transport_controller, + /*is_srtp_transport*/ true))); } void RtpTransportAdapter::TakeOwnershipOfRtpTransportController( @@ -78,11 +128,13 @@ RtpTransportAdapter::RtpTransportAdapter( const RtcpParameters& rtcp_parameters, PacketTransportInterface* rtp, PacketTransportInterface* rtcp, - RtpTransportControllerAdapter* rtp_transport_controller) + RtpTransportControllerAdapter* rtp_transport_controller, + bool is_srtp_transport) : rtp_packet_transport_(rtp), rtcp_packet_transport_(rtcp), rtp_transport_controller_(rtp_transport_controller), - rtcp_parameters_(rtcp_parameters) { + rtcp_parameters_(rtcp_parameters), + is_srtp_transport_(is_srtp_transport) { RTC_DCHECK(rtp_transport_controller); // CNAME should have been filled by OrtcFactory if empty. RTC_DCHECK(!rtcp_parameters_.cname.empty()); @@ -126,4 +178,36 @@ RTCError RtpTransportAdapter::SetRtcpParameters( return RTCError::OK(); } +RTCError RtpTransportAdapter::SetSrtpSendKey( + const cricket::CryptoParams& params) { + if (send_key_) { + LOG_AND_RETURN_ERROR( + webrtc::RTCErrorType::UNSUPPORTED_OPERATION, + "Setting the SRTP send key twice is currently unsupported."); + } + if (receive_key_ && receive_key_->cipher_suite != params.cipher_suite) { + LOG_AND_RETURN_ERROR( + webrtc::RTCErrorType::UNSUPPORTED_OPERATION, + "The send key and receive key must have the same cipher suite."); + } + send_key_ = rtc::Optional(params); + return RTCError::OK(); +} + +RTCError RtpTransportAdapter::SetSrtpReceiveKey( + const cricket::CryptoParams& params) { + if (receive_key_) { + LOG_AND_RETURN_ERROR( + webrtc::RTCErrorType::UNSUPPORTED_OPERATION, + "Setting the SRTP receive key twice is currently unsupported."); + } + if (send_key_ && send_key_->cipher_suite != params.cipher_suite) { + LOG_AND_RETURN_ERROR( + webrtc::RTCErrorType::UNSUPPORTED_OPERATION, + "The send key and receive key must have the same cipher suite."); + } + receive_key_ = rtc::Optional(params); + return RTCError::OK(); +} + } // namespace webrtc diff --git a/webrtc/ortc/rtptransportadapter.h b/webrtc/ortc/rtptransportadapter.h index 169ae613db..59a123efae 100644 --- a/webrtc/ortc/rtptransportadapter.h +++ b/webrtc/ortc/rtptransportadapter.h @@ -14,7 +14,7 @@ #include #include -#include "webrtc/api/ortc/rtptransportinterface.h" +#include "webrtc/api/ortc/srtptransportinterface.h" #include "webrtc/api/rtcerror.h" #include "webrtc/base/constructormagic.h" #include "webrtc/base/sigslot.h" @@ -24,13 +24,14 @@ namespace webrtc { -// Implementation of RtpTransportInterface to be used with RtpSenderAdapter, -// RtpReceiverAdapter, and RtpTransportControllerAdapter classes. +// Implementation of SrtpTransportInterface to be used with RtpSenderAdapter, +// RtpReceiverAdapter, and RtpTransportControllerAdapter classes. This class +// is used to implement both a secure and insecure RTP transport. // // TODO(deadbeef): When BaseChannel is split apart into separate // "RtpTransport"/"RtpTransceiver"/"RtpSender"/"RtpReceiver" objects, this // adapter object can be removed. -class RtpTransportAdapter : public RtpTransportInterface { +class RtpTransportAdapter : public SrtpTransportInterface { public: // |rtp| can't be null. |rtcp| can if RTCP muxing is used immediately (meaning // |rtcp_parameters.mux| is also true). @@ -39,6 +40,13 @@ class RtpTransportAdapter : public RtpTransportInterface { PacketTransportInterface* rtp, PacketTransportInterface* rtcp, RtpTransportControllerAdapter* rtp_transport_controller); + + static RTCErrorOr> CreateSrtpProxied( + const RtcpParameters& rtcp_parameters, + PacketTransportInterface* rtp, + PacketTransportInterface* rtcp, + RtpTransportControllerAdapter* rtp_transport_controller); + ~RtpTransportAdapter() override; // RtpTransportInterface implementation. @@ -47,6 +55,10 @@ class RtpTransportAdapter : public RtpTransportInterface { RTCError SetRtcpParameters(const RtcpParameters& parameters) override; RtcpParameters GetRtcpParameters() const override { return rtcp_parameters_; } + // SRTP specific implementation. + RTCError SetSrtpSendKey(const cricket::CryptoParams& params) override; + RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params) override; + // Methods used internally by OrtcFactory. RtpTransportControllerAdapter* rtp_transport_controller() { return rtp_transport_controller_; @@ -58,6 +70,14 @@ class RtpTransportAdapter : public RtpTransportInterface { // returning this transport from GetTransports(). sigslot::signal1 SignalDestroyed; + // Used by the RtpTransportControllerAdapter to tell if an rtp sender or + // receiver can be created. + bool is_srtp_transport() { return is_srtp_transport_; } + // Used by the RtpTransportControllerAdapter to set keys for senders and + // receivers. + rtc::Optional send_key() { return send_key_; } + rtc::Optional receive_key() { return receive_key_; } + protected: RtpTransportAdapter* GetInternal() override { return this; } @@ -65,7 +85,8 @@ class RtpTransportAdapter : public RtpTransportInterface { RtpTransportAdapter(const RtcpParameters& rtcp_parameters, PacketTransportInterface* rtp, PacketTransportInterface* rtcp, - RtpTransportControllerAdapter* rtp_transport_controller); + RtpTransportControllerAdapter* rtp_transport_controller, + bool is_srtp_transport); PacketTransportInterface* rtp_packet_transport_; PacketTransportInterface* rtcp_packet_transport_; @@ -75,6 +96,11 @@ class RtpTransportAdapter : public RtpTransportInterface { owned_rtp_transport_controller_; RtcpParameters rtcp_parameters_; + // SRTP specific members. + rtc::Optional send_key_; + rtc::Optional receive_key_; + bool is_srtp_transport_; + RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtpTransportAdapter); }; diff --git a/webrtc/ortc/rtptransportcontrolleradapter.cc b/webrtc/ortc/rtptransportcontrolleradapter.cc index 08e943a200..ad5d8b5d68 100644 --- a/webrtc/ortc/rtptransportcontrolleradapter.cc +++ b/webrtc/ortc/rtptransportcontrolleradapter.cc @@ -11,8 +11,8 @@ #include "webrtc/ortc/rtptransportcontrolleradapter.h" #include // For "remove", "find". -#include #include +#include #include #include // For std::move. @@ -138,6 +138,21 @@ RtpTransportControllerAdapter::CreateProxiedRtpTransport( return result; } +RTCErrorOr> +RtpTransportControllerAdapter::CreateProxiedSrtpTransport( + const RtcpParameters& rtcp_parameters, + PacketTransportInterface* rtp, + PacketTransportInterface* rtcp) { + auto result = + RtpTransportAdapter::CreateSrtpProxied(rtcp_parameters, rtp, rtcp, this); + if (result.ok()) { + transport_proxies_.push_back(result.value().get()); + transport_proxies_.back()->GetInternal()->SignalDestroyed.connect( + this, &RtpTransportControllerAdapter::OnRtpTransportDestroyed); + } + return result; +} + RTCErrorOr> RtpTransportControllerAdapter::CreateProxiedRtpSender( cricket::MediaType kind, @@ -605,6 +620,11 @@ RTCError RtpTransportControllerAdapter::AttachAudioSender( "RtpSender and RtpReceiver is not currently " "supported."); } + RTCError err = MaybeSetCryptos(inner_transport, &local_audio_description_, + &remote_audio_description_); + if (!err.ok()) { + return err; + } // If setting new transport, extract its RTCP parameters and create voice // channel. if (!inner_audio_transport_) { @@ -635,6 +655,11 @@ RTCError RtpTransportControllerAdapter::AttachVideoSender( "RtpSender and RtpReceiver is not currently " "supported."); } + RTCError err = MaybeSetCryptos(inner_transport, &local_video_description_, + &remote_video_description_); + if (!err.ok()) { + return err; + } // If setting new transport, extract its RTCP parameters and create video // channel. if (!inner_video_transport_) { @@ -665,6 +690,11 @@ RTCError RtpTransportControllerAdapter::AttachAudioReceiver( "RtpReceiver and RtpReceiver is not currently " "supported."); } + RTCError err = MaybeSetCryptos(inner_transport, &local_audio_description_, + &remote_audio_description_); + if (!err.ok()) { + return err; + } // If setting new transport, extract its RTCP parameters and create voice // channel. if (!inner_audio_transport_) { @@ -695,6 +725,11 @@ RTCError RtpTransportControllerAdapter::AttachVideoReceiver( "RtpReceiver and RtpReceiver is not currently " "supported."); } + RTCError err = MaybeSetCryptos(inner_transport, &local_video_description_, + &remote_video_description_); + if (!err.ok()) { + return err; + } // If setting new transport, extract its RTCP parameters and create video // channel. if (!inner_video_transport_) { @@ -896,4 +931,25 @@ RtpTransportControllerAdapter::MakeSendStreamParamsVec( return result; } +RTCError RtpTransportControllerAdapter::MaybeSetCryptos( + RtpTransportInterface* rtp_transport, + cricket::MediaContentDescription* local_description, + cricket::MediaContentDescription* remote_description) { + if (rtp_transport->GetInternal()->is_srtp_transport()) { + if (!rtp_transport->GetInternal()->send_key() || + !rtp_transport->GetInternal()->receive_key()) { + LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::UNSUPPORTED_PARAMETER, + "The SRTP send key or receive key is not set.") + } + std::vector cryptos; + cryptos.push_back(*(rtp_transport->GetInternal()->receive_key())); + local_description->set_cryptos(cryptos); + + cryptos.clear(); + cryptos.push_back(*(rtp_transport->GetInternal()->send_key())); + remote_description->set_cryptos(cryptos); + } + return RTCError::OK(); +} + } // namespace webrtc diff --git a/webrtc/ortc/rtptransportcontrolleradapter.h b/webrtc/ortc/rtptransportcontrolleradapter.h index 4e02b95955..902ec01c51 100644 --- a/webrtc/ortc/rtptransportcontrolleradapter.h +++ b/webrtc/ortc/rtptransportcontrolleradapter.h @@ -16,18 +16,18 @@ #include #include +#include "webrtc/api/ortc/ortcrtpreceiverinterface.h" +#include "webrtc/api/ortc/ortcrtpsenderinterface.h" +#include "webrtc/api/ortc/rtptransportcontrollerinterface.h" +#include "webrtc/api/ortc/srtptransportinterface.h" #include "webrtc/base/constructormagic.h" #include "webrtc/base/sigslot.h" #include "webrtc/base/thread.h" #include "webrtc/call/call.h" #include "webrtc/logging/rtc_event_log/rtc_event_log.h" -#include "webrtc/api/ortc/ortcrtpreceiverinterface.h" -#include "webrtc/api/ortc/ortcrtpsenderinterface.h" -#include "webrtc/api/ortc/rtptransportcontrollerinterface.h" -#include "webrtc/api/ortc/rtptransportinterface.h" +#include "webrtc/media/base/mediachannel.h" // For MediaConfig. #include "webrtc/pc/channelmanager.h" #include "webrtc/pc/mediacontroller.h" -#include "webrtc/media/base/mediachannel.h" // For MediaConfig. namespace webrtc { @@ -81,6 +81,12 @@ class RtpTransportControllerAdapter : public RtpTransportControllerInterface, const RtcpParameters& rtcp_parameters, PacketTransportInterface* rtp, PacketTransportInterface* rtcp); + + RTCErrorOr> + CreateProxiedSrtpTransport(const RtcpParameters& rtcp_parameters, + PacketTransportInterface* rtp, + PacketTransportInterface* rtcp); + // |transport_proxy| needs to be a proxy to a transport because the // application may call GetTransport() on the returned sender or receiver, // and expects it to return a thread-safe transport proxy. @@ -170,6 +176,13 @@ class RtpTransportControllerAdapter : public RtpTransportControllerInterface, const std::string& cname, const cricket::MediaContentDescription& description) const; + // If the |rtp_transport| is a SrtpTransport, set the cryptos of the + // audio/video content descriptions. + RTCError MaybeSetCryptos( + RtpTransportInterface* rtp_transport, + cricket::MediaContentDescription* local_description, + cricket::MediaContentDescription* remote_description); + rtc::Thread* signaling_thread_; rtc::Thread* worker_thread_; // |transport_proxies_| and |inner_audio_transport_|/|inner_audio_transport_| diff --git a/webrtc/ortc/srtptransport_unittest.cc b/webrtc/ortc/srtptransport_unittest.cc new file mode 100644 index 0000000000..1287a0257c --- /dev/null +++ b/webrtc/ortc/srtptransport_unittest.cc @@ -0,0 +1,167 @@ +/* + * 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 + +#include "webrtc/base/gunit.h" +#include "webrtc/media/base/fakemediaengine.h" +#include "webrtc/ortc/ortcfactory.h" +#include "webrtc/ortc/testrtpparameters.h" +#include "webrtc/p2p/base/fakepackettransport.h" + +namespace webrtc { + +static const char kTestSha1KeyParams1[] = + "inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz"; +static const char kTestSha1KeyParams2[] = + "inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR"; +static const char kTestGcmKeyParams3[] = + "inline:e166KFlKzJsGW0d5apX+rrI05vxbrvMJEzFI14aTDCa63IRTlLK4iH66uOI="; + +static const cricket::CryptoParams kTestSha1CryptoParams1( + 1, + "AES_CM_128_HMAC_SHA1_80", + kTestSha1KeyParams1, + ""); +static const cricket::CryptoParams kTestSha1CryptoParams2( + 1, + "AES_CM_128_HMAC_SHA1_80", + kTestSha1KeyParams2, + ""); +static const cricket::CryptoParams kTestGcmCryptoParams3(1, + "AEAD_AES_256_GCM", + kTestGcmKeyParams3, + ""); + +// This test uses fake packet transports and a fake media engine, in order to +// test the SrtpTransport at only an API level. Any end-to-end test should go in +// ortcfactory_integrationtest.cc instead. +class SrtpTransportTest : public testing::Test { + public: + SrtpTransportTest() { + fake_media_engine_ = new cricket::FakeMediaEngine(); + // Note: This doesn't need to use fake network classes, since it uses + // FakePacketTransports. + auto result = OrtcFactory::Create( + nullptr, nullptr, nullptr, nullptr, nullptr, + std::unique_ptr(fake_media_engine_)); + ortc_factory_ = result.MoveValue(); + rtp_transport_controller_ = + ortc_factory_->CreateRtpTransportController().MoveValue(); + + fake_packet_transport_.reset(new rtc::FakePacketTransport("fake")); + auto srtp_transport_result = ortc_factory_->CreateSrtpTransport( + rtcp_parameters_, fake_packet_transport_.get(), nullptr, + rtp_transport_controller_.get()); + srtp_transport_ = srtp_transport_result.MoveValue(); + } + + protected: + // Owned by |ortc_factory_|. + cricket::FakeMediaEngine* fake_media_engine_; + std::unique_ptr ortc_factory_; + std::unique_ptr rtp_transport_controller_; + std::unique_ptr srtp_transport_; + RtcpParameters rtcp_parameters_; + std::unique_ptr fake_packet_transport_; +}; + +// Tests that setting the SRTP send/receive key succeeds. +TEST_F(SrtpTransportTest, SetSrtpSendAndReceiveKey) { + EXPECT_TRUE(srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams1).ok()); + EXPECT_TRUE(srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams2).ok()); + auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO, + srtp_transport_.get()); + EXPECT_TRUE(sender_result.ok()); + auto receiver_result = ortc_factory_->CreateRtpReceiver( + cricket::MEDIA_TYPE_AUDIO, srtp_transport_.get()); + EXPECT_TRUE(receiver_result.ok()); +} + +// Tests that setting the SRTP send/receive key twice is not supported. +TEST_F(SrtpTransportTest, SetSrtpSendAndReceiveKeyTwice) { + EXPECT_TRUE(srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams1).ok()); + EXPECT_TRUE(srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams2).ok()); + EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION, + srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams2).type()); + EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION, + srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams1).type()); + // Ensure that the senders and receivers can be created despite the previous + // errors. + auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO, + srtp_transport_.get()); + EXPECT_TRUE(sender_result.ok()); + auto receiver_result = ortc_factory_->CreateRtpReceiver( + cricket::MEDIA_TYPE_AUDIO, srtp_transport_.get()); + EXPECT_TRUE(receiver_result.ok()); +} + +// Test that the SRTP send key and receive key must have the same cipher suite. +TEST_F(SrtpTransportTest, SetSrtpSendAndReceiveKeyDifferentCipherSuite) { + EXPECT_TRUE(srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams1).ok()); + EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION, + srtp_transport_->SetSrtpReceiveKey(kTestGcmCryptoParams3).type()); + EXPECT_TRUE(srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams2).ok()); + // Ensure that the senders and receivers can be created despite the previous + // error. + auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO, + srtp_transport_.get()); + EXPECT_TRUE(sender_result.ok()); + auto receiver_result = ortc_factory_->CreateRtpReceiver( + cricket::MEDIA_TYPE_AUDIO, srtp_transport_.get()); + EXPECT_TRUE(receiver_result.ok()); +} + +class SrtpTransportTestWithMediaType + : public SrtpTransportTest, + public ::testing::WithParamInterface {}; + +// Tests that the senders cannot be created before setting the keys. +TEST_P(SrtpTransportTestWithMediaType, CreateSenderBeforeSettingKeys) { + auto sender_result = + ortc_factory_->CreateRtpSender(GetParam(), srtp_transport_.get()); + EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, sender_result.error().type()); + EXPECT_TRUE(srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams1).ok()); + sender_result = + ortc_factory_->CreateRtpSender(GetParam(), srtp_transport_.get()); + EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, sender_result.error().type()); + EXPECT_TRUE(srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams2).ok()); + // Ensure that after the keys are set, a sender can be created, despite the + // previous errors. + sender_result = + ortc_factory_->CreateRtpSender(GetParam(), srtp_transport_.get()); + EXPECT_TRUE(sender_result.ok()); +} + +// Tests that the receivers cannot be created before setting the keys. +TEST_P(SrtpTransportTestWithMediaType, CreateReceiverBeforeSettingKeys) { + auto receiver_result = + ortc_factory_->CreateRtpReceiver(GetParam(), srtp_transport_.get()); + EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, + receiver_result.error().type()); + EXPECT_TRUE(srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams1).ok()); + receiver_result = + ortc_factory_->CreateRtpReceiver(GetParam(), srtp_transport_.get()); + EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, + receiver_result.error().type()); + EXPECT_TRUE(srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams2).ok()); + // Ensure that after the keys are set, a receiver can be created, despite the + // previous errors. + receiver_result = + ortc_factory_->CreateRtpReceiver(GetParam(), srtp_transport_.get()); + EXPECT_TRUE(receiver_result.ok()); +} + +INSTANTIATE_TEST_CASE_P(SenderCreatationTest, + SrtpTransportTestWithMediaType, + ::testing::Values(cricket::MEDIA_TYPE_AUDIO, + cricket::MEDIA_TYPE_VIDEO)); + +} // namespace webrtc