blob: d87d0b0d1e700a7ee4a54e2b9d4b495de9eb4aa0 [file] [log] [blame]
Steve Anton6b63cd52017-10-06 11:20:31 -07001/*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Harald Alvestrandc24a2182022-02-23 13:44:59 +000011#include <stddef.h>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020012
Harald Alvestrandc24a2182022-02-23 13:44:59 +000013#include <memory>
14#include <ostream>
15#include <string>
16#include <tuple>
17#include <type_traits>
18#include <utility>
19#include <vector>
20
21#include "absl/types/optional.h"
22#include "api/audio/audio_mixer.h"
Karl Wiberg1b0eae32017-10-17 14:48:54 +020023#include "api/audio_codecs/builtin_audio_decoder_factory.h"
24#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010025#include "api/create_peerconnection_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000026#include "api/crypto/crypto_options.h"
27#include "api/crypto_params.h"
28#include "api/jsep.h"
29#include "api/peer_connection_interface.h"
30#include "api/scoped_refptr.h"
Anders Carlsson67537952018-05-03 11:28:29 +020031#include "api/video_codecs/builtin_video_decoder_factory.h"
32#include "api/video_codecs/builtin_video_encoder_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000033#include "modules/audio_device/include/audio_device.h"
34#include "modules/audio_processing/include/audio_processing.h"
Steve Anton10542f22019-01-11 09:11:00 -080035#include "p2p/base/fake_port_allocator.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000036#include "p2p/base/port_allocator.h"
37#include "p2p/base/transport_description.h"
38#include "p2p/base/transport_info.h"
39#include "pc/media_protocol_names.h"
Steve Anton10542f22019-01-11 09:11:00 -080040#include "pc/media_session.h"
41#include "pc/peer_connection_wrapper.h"
42#include "pc/sdp_utils.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000043#include "pc/session_description.h"
44#include "pc/test/mock_peer_connection_observers.h"
45#include "rtc_base/checks.h"
46#include "rtc_base/ref_counted_object.h"
47#include "rtc_base/rtc_certificate.h"
48#include "rtc_base/rtc_certificate_generator.h"
49#include "rtc_base/ssl_fingerprint.h"
50#include "rtc_base/thread.h"
51#include "test/gtest.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070052#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080053#include "pc/test/android_test_initializer.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070054#endif
Steve Anton10542f22019-01-11 09:11:00 -080055#include "pc/test/fake_audio_capture_module.h"
56#include "pc/test/fake_rtc_certificate_generator.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070057#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080058#include "rtc_base/virtual_socket_server.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070059
60namespace webrtc {
61
62using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 13:08:53 -070063using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 11:20:31 -070064using ::testing::Combine;
Jonas Olssona4d87372019-07-05 19:08:33 +020065using ::testing::Values;
Steve Anton6b63cd52017-10-06 11:20:31 -070066
67constexpr int kGenerateCertTimeout = 1000;
68
Steve Anton71182f42018-01-19 14:59:54 -080069class PeerConnectionCryptoBaseTest : public ::testing::Test {
Steve Anton6b63cd52017-10-06 11:20:31 -070070 protected:
71 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
72
Steve Anton71182f42018-01-19 14:59:54 -080073 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
74 : vss_(new rtc::VirtualSocketServer()),
75 main_(vss_.get()),
76 sdp_semantics_(sdp_semantics) {
Steve Anton6b63cd52017-10-06 11:20:31 -070077#ifdef WEBRTC_ANDROID
78 InitializeAndroidObjects();
79#endif
80 pc_factory_ = CreatePeerConnectionFactory(
81 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020082 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
Anders Carlsson67537952018-05-03 11:28:29 +020083 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
84 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
85 nullptr /* audio_processing */);
Steve Anton6b63cd52017-10-06 11:20:31 -070086 }
87
Steve Anton8a63f782017-10-23 13:08:53 -070088 WrapperPtr CreatePeerConnection() {
89 return CreatePeerConnection(RTCConfiguration());
90 }
91
Steve Anton6b63cd52017-10-06 11:20:31 -070092 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
93 return CreatePeerConnection(config, nullptr);
94 }
95
96 WrapperPtr CreatePeerConnection(
97 const RTCConfiguration& config,
98 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020099 auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
Steve Anton6b63cd52017-10-06 11:20:31 -0700100 rtc::Thread::Current(), nullptr);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200101 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton71182f42018-01-19 14:59:54 -0800102 RTCConfiguration modified_config = config;
103 modified_config.sdp_semantics = sdp_semantics_;
Florent Castelli72424402022-04-06 03:45:10 +0200104 PeerConnectionDependencies pc_dependencies(observer.get());
105 pc_dependencies.allocator = std::move(fake_port_allocator);
106 pc_dependencies.cert_generator = std::move(cert_gen);
107 auto result = pc_factory_->CreatePeerConnectionOrError(
108 modified_config, std::move(pc_dependencies));
109 if (!result.ok()) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700110 return nullptr;
111 }
112
Niels Möllerafb246b2022-04-20 14:26:50 +0200113 observer->SetPeerConnectionInterface(result.value().get());
Florent Castelli72424402022-04-06 03:45:10 +0200114 return std::make_unique<PeerConnectionWrapper>(
115 pc_factory_, result.MoveValue(), std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -0700116 }
117
118 // Accepts the same arguments as CreatePeerConnection and adds default audio
119 // and video tracks.
120 template <typename... Args>
121 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
122 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
123 if (!wrapper) {
124 return nullptr;
125 }
Steve Anton8d3444d2017-10-20 15:30:51 -0700126 wrapper->AddAudioTrack("a");
127 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -0700128 return wrapper;
129 }
130
Steve Anton8a63f782017-10-23 13:08:53 -0700131 cricket::ConnectionRole& AudioConnectionRole(
132 cricket::SessionDescription* desc) {
133 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
134 }
135
136 cricket::ConnectionRole& VideoConnectionRole(
137 cricket::SessionDescription* desc) {
138 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
139 }
140
141 cricket::ConnectionRole& ConnectionRoleFromContent(
142 cricket::SessionDescription* desc,
143 cricket::ContentInfo* content) {
144 RTC_DCHECK(content);
145 auto* transport_info = desc->GetTransportInfoByName(content->name);
146 RTC_DCHECK(transport_info);
147 return transport_info->description.connection_role;
148 }
149
Steve Anton6b63cd52017-10-06 11:20:31 -0700150 std::unique_ptr<rtc::VirtualSocketServer> vss_;
151 rtc::AutoSocketServerThread main_;
152 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800153 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700154};
155
156SdpContentPredicate HaveDtlsFingerprint() {
157 return [](const cricket::ContentInfo* content,
158 const cricket::TransportInfo* transport) {
159 return transport->description.identity_fingerprint != nullptr;
160 };
161}
162
Harald Alvestrand0d018412021-11-04 13:52:31 +0000163SdpContentPredicate HaveSdesCryptos() {
164 return [](const cricket::ContentInfo* content,
165 const cricket::TransportInfo* transport) {
166 return !content->media_description()->cryptos().empty();
167 };
168}
169
Steve Anton6b63cd52017-10-06 11:20:31 -0700170SdpContentPredicate HaveProtocol(const std::string& protocol) {
171 return [protocol](const cricket::ContentInfo* content,
172 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800173 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700174 };
175}
176
Harald Alvestrand0d018412021-11-04 13:52:31 +0000177SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
178 return [num_crypto_suites](const cricket::ContentInfo* content,
179 const cricket::TransportInfo* transport) {
180 const auto& cryptos = content->media_description()->cryptos();
181 if (cryptos.size() != num_crypto_suites) {
182 return false;
183 }
184 for (size_t i = 0; i < cryptos.size(); ++i) {
185 if (cryptos[i].key_params.size() == 67U &&
186 cryptos[i].cipher_suite == "AEAD_AES_256_GCM")
187 return true;
188 }
189 return false;
190 };
191}
192
Steve Anton71182f42018-01-19 14:59:54 -0800193class PeerConnectionCryptoTest
194 : public PeerConnectionCryptoBaseTest,
195 public ::testing::WithParamInterface<SdpSemantics> {
196 protected:
197 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
198};
199
Harald Alvestrand0d018412021-11-04 13:52:31 +0000200SdpContentMutator RemoveSdesCryptos() {
201 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
202 content->media_description()->set_cryptos({});
203 };
204}
205
Steve Anton6b63cd52017-10-06 11:20:31 -0700206SdpContentMutator RemoveDtlsFingerprint() {
207 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
208 transport->description.identity_fingerprint.reset();
209 };
210}
211
Harald Alvestrand0d018412021-11-04 13:52:31 +0000212// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
213// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800214TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700215 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700216 auto caller = CreatePeerConnectionWithAudioVideo(config);
217
218 auto offer = caller->CreateOffer();
219 ASSERT_TRUE(offer);
220
221 ASSERT_FALSE(offer->description()->contents().empty());
222 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000223 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700224 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
225 offer->description()));
226}
Steve Anton71182f42018-01-19 14:59:54 -0800227TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700228 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700229 auto caller = CreatePeerConnectionWithAudioVideo(config);
230 auto callee = CreatePeerConnectionWithAudioVideo(config);
231
232 callee->SetRemoteDescription(caller->CreateOffer());
233 auto answer = callee->CreateAnswer();
234 ASSERT_TRUE(answer);
235
236 ASSERT_FALSE(answer->description()->contents().empty());
237 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000238 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700239 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
240 answer->description()));
241}
242
Harald Alvestrandca327932022-04-04 15:37:31 +0000243#if defined(WEBRTC_FUCHSIA)
Harald Alvestrand50b95522021-11-18 10:01:06 +0000244// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
245// should not have a DTLS fingerprint.
246TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
247 RTCConfiguration config;
248 config.enable_dtls_srtp.emplace(false);
249 auto caller = CreatePeerConnectionWithAudioVideo(config);
250
251 auto offer = caller->CreateOffer();
252 ASSERT_TRUE(offer);
253
254 ASSERT_FALSE(offer->description()->contents().empty());
255 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
256 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
257 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
258 offer->description()));
259}
Harald Alvestrandca327932022-04-04 15:37:31 +0000260
Harald Alvestrand50b95522021-11-18 10:01:06 +0000261TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
262 RTCConfiguration config;
263 config.enable_dtls_srtp.emplace(false);
264 auto caller = CreatePeerConnectionWithAudioVideo(config);
265 auto callee = CreatePeerConnectionWithAudioVideo(config);
266
267 callee->SetRemoteDescription(caller->CreateOffer());
268 auto answer = callee->CreateAnswer();
269 ASSERT_TRUE(answer);
270
271 ASSERT_FALSE(answer->description()->contents().empty());
272 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
273 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
274 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
275 answer->description()));
276}
277
Steve Anton6b63cd52017-10-06 11:20:31 -0700278// When encryption is disabled, the SDP offer/answer should have neither a DTLS
279// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800280TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700281 PeerConnectionFactoryInterface::Options options;
282 options.disable_encryption = true;
283 pc_factory_->SetOptions(options);
284
285 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000286 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700287 auto caller = CreatePeerConnectionWithAudioVideo(config);
288
289 auto offer = caller->CreateOffer();
290 ASSERT_TRUE(offer);
291
292 ASSERT_FALSE(offer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000293 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700294 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
295 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
296 offer->description()));
297}
Harald Alvestrandca327932022-04-04 15:37:31 +0000298
Steve Anton71182f42018-01-19 14:59:54 -0800299TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700300 PeerConnectionFactoryInterface::Options options;
301 options.disable_encryption = true;
302 pc_factory_->SetOptions(options);
303
304 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000305 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700306 auto caller = CreatePeerConnectionWithAudioVideo(config);
307 auto callee = CreatePeerConnectionWithAudioVideo(config);
308
309 callee->SetRemoteDescription(caller->CreateOffer());
310 auto answer = callee->CreateAnswer();
311 ASSERT_TRUE(answer);
312
313 ASSERT_FALSE(answer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000314 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700315 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
316 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
317 answer->description()));
318}
319
Harald Alvestrand50b95522021-11-18 10:01:06 +0000320// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
321// set in the configuration it should overrite the settings set in the factory.
322TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
323 PeerConnectionFactoryInterface::Options options;
324 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
325 pc_factory_->SetOptions(options);
326
327 RTCConfiguration config;
328 config.enable_dtls_srtp.emplace(false);
329 CryptoOptions crypto_options;
330 crypto_options.srtp.enable_gcm_crypto_suites = false;
331 config.crypto_options = crypto_options;
332 auto caller = CreatePeerConnectionWithAudioVideo(config);
333
334 auto offer = caller->CreateOffer();
335 ASSERT_TRUE(offer);
336
337 ASSERT_FALSE(offer->description()->contents().empty());
338 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
339 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
340}
341
342// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
343// should have the correct ciphers in the SDES crypto options.
344// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
345// in the answer.
346TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
347 PeerConnectionFactoryInterface::Options options;
348 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
349 pc_factory_->SetOptions(options);
350
351 RTCConfiguration config;
352 config.enable_dtls_srtp.emplace(false);
353 auto caller = CreatePeerConnectionWithAudioVideo(config);
354
355 auto offer = caller->CreateOffer();
356 ASSERT_TRUE(offer);
357
358 ASSERT_FALSE(offer->description()->contents().empty());
359 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
360}
361
362TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
363 PeerConnectionFactoryInterface::Options options;
364 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
365 pc_factory_->SetOptions(options);
366
367 RTCConfiguration config;
368 config.enable_dtls_srtp.emplace(false);
369 auto caller = CreatePeerConnectionWithAudioVideo(config);
370 auto callee = CreatePeerConnectionWithAudioVideo(config);
371
372 auto offer = caller->CreateOffer();
373 for (cricket::ContentInfo& content : offer->description()->contents()) {
374 auto cryptos = content.media_description()->cryptos();
375 cryptos.erase(cryptos.begin()); // Assumes that non-GCM is the default.
376 content.media_description()->set_cryptos(cryptos);
377 }
378
379 callee->SetRemoteDescription(std::move(offer));
380 auto answer = callee->CreateAnswer();
381 ASSERT_TRUE(answer);
382
383 ASSERT_FALSE(answer->description()->contents().empty());
384 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
385}
386
Steve Anton71182f42018-01-19 14:59:54 -0800387TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700388 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700389 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700390 pc_factory_->SetOptions(options);
391
392 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000393 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700394 auto caller = CreatePeerConnectionWithAudioVideo(config);
395 auto callee = CreatePeerConnectionWithAudioVideo(config);
396
397 auto offer = caller->CreateOffer();
398 ASSERT_TRUE(offer);
399 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
400
401 auto answer = callee->CreateAnswer();
402 ASSERT_TRUE(answer);
403 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
Harald Alvestrand50b95522021-11-18 10:01:06 +0000404}
405
406// The following group tests that two PeerConnections can successfully exchange
407// an offer/answer when DTLS is off and that they will refuse any offer/answer
408// applied locally/remotely if it does not include SDES cryptos.
409TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
410 RTCConfiguration config;
411 config.enable_dtls_srtp.emplace(false);
412 auto caller = CreatePeerConnectionWithAudioVideo(config);
413 auto callee = CreatePeerConnectionWithAudioVideo(config);
414
415 auto offer = caller->CreateOfferAndSetAsLocal();
416 ASSERT_TRUE(offer);
417 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
418
419 auto answer = callee->CreateAnswerAndSetAsLocal();
420 ASSERT_TRUE(answer);
421 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
422}
423TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
424 RTCConfiguration config;
425 config.enable_dtls_srtp.emplace(false);
426 auto caller = CreatePeerConnectionWithAudioVideo(config);
427
428 auto offer = caller->CreateOffer();
429 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
430
431 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
432}
433TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
434 RTCConfiguration config;
435 config.enable_dtls_srtp.emplace(false);
436 auto caller = CreatePeerConnectionWithAudioVideo(config);
437 auto callee = CreatePeerConnectionWithAudioVideo(config);
438
439 auto offer = caller->CreateOffer();
440 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
441
442 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
443}
444TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
445 RTCConfiguration config;
446 config.enable_dtls_srtp.emplace(false);
447 auto caller = CreatePeerConnectionWithAudioVideo(config);
448 auto callee = CreatePeerConnectionWithAudioVideo(config);
449
450 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
451 auto answer = callee->CreateAnswer();
452 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
453
454 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
455}
456TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
457 RTCConfiguration config;
458 config.enable_dtls_srtp.emplace(false);
459 auto caller = CreatePeerConnectionWithAudioVideo(config);
460 auto callee = CreatePeerConnectionWithAudioVideo(config);
461
462 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
463 auto answer = callee->CreateAnswerAndSetAsLocal();
464 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
465
466 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
Steve Anton6b63cd52017-10-06 11:20:31 -0700467}
Harald Alvestrandca327932022-04-04 15:37:31 +0000468#endif
Steve Anton6b63cd52017-10-06 11:20:31 -0700469
470// The following group tests that two PeerConnections can successfully exchange
471// an offer/answer when DTLS is on and that they will refuse any offer/answer
472// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800473TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700474 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700475 auto caller = CreatePeerConnectionWithAudioVideo(config);
476 auto callee = CreatePeerConnectionWithAudioVideo(config);
477
478 auto offer = caller->CreateOfferAndSetAsLocal();
479 ASSERT_TRUE(offer);
480 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
481
482 auto answer = callee->CreateAnswerAndSetAsLocal();
483 ASSERT_TRUE(answer);
484 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
485}
Steve Anton71182f42018-01-19 14:59:54 -0800486TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700487 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
488 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700489 auto caller = CreatePeerConnectionWithAudioVideo(config);
490
491 auto offer = caller->CreateOffer();
492 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
493
494 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
495}
Steve Anton71182f42018-01-19 14:59:54 -0800496TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700497 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
498 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700499 auto caller = CreatePeerConnectionWithAudioVideo(config);
500 auto callee = CreatePeerConnectionWithAudioVideo(config);
501
502 auto offer = caller->CreateOffer();
503 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
504
505 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
506}
Steve Anton71182f42018-01-19 14:59:54 -0800507TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700508 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
509 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700510 auto caller = CreatePeerConnectionWithAudioVideo(config);
511 auto callee = CreatePeerConnectionWithAudioVideo(config);
512
513 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
514 auto answer = callee->CreateAnswer();
515 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
516}
Steve Anton71182f42018-01-19 14:59:54 -0800517TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700518 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
519 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700520 auto caller = CreatePeerConnectionWithAudioVideo(config);
521 auto callee = CreatePeerConnectionWithAudioVideo(config);
522
523 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
524 auto answer = callee->CreateAnswerAndSetAsLocal();
525 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
526
527 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
528}
529
Harald Alvestrandca327932022-04-04 15:37:31 +0000530#if defined(WEBRTC_FUCHSIA)
Steve Anton6b63cd52017-10-06 11:20:31 -0700531// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800532TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700533 PeerConnectionFactoryInterface::Options options;
534 options.disable_encryption = true;
535 pc_factory_->SetOptions(options);
536
537 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000538 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700539 auto caller = CreatePeerConnectionWithAudioVideo(config);
540 auto callee = CreatePeerConnectionWithAudioVideo(config);
541
542 auto offer = caller->CreateOfferAndSetAsLocal();
543 ASSERT_TRUE(offer);
544 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
545
546 auto answer = callee->CreateAnswerAndSetAsLocal();
547 ASSERT_TRUE(answer);
548 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
549}
Harald Alvestrandca327932022-04-04 15:37:31 +0000550#endif
Steve Anton6b63cd52017-10-06 11:20:31 -0700551
552// Tests that a DTLS call can be established when the certificate is specified
553// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800554TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700555 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
556 RTCConfiguration caller_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700557 caller_config.certificates.push_back(
558 FakeRTCCertificateGenerator::GenerateCertificate());
559 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
560
561 RTCConfiguration callee_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700562 callee_config.certificates.push_back(
563 FakeRTCCertificateGenerator::GenerateCertificate());
564 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
565
566 auto offer = caller->CreateOfferAndSetAsLocal();
567 ASSERT_TRUE(offer);
568 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
569
570 auto answer = callee->CreateAnswerAndSetAsLocal();
571 ASSERT_TRUE(answer);
572 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
573}
574
575// The following parameterized test verifies that CreateOffer/CreateAnswer
576// returns successfully (or with failure if the underlying certificate generator
577// fails) no matter when the DTLS certificate is generated. If multiple
578// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
579// they all finish after the certificate is generated.
580
Steve Anton6b63cd52017-10-06 11:20:31 -0700581// Whether the certificate will be generated before calling CreateOffer or
582// while CreateOffer is executing.
583enum class CertGenTime { kBefore, kDuring };
584std::ostream& operator<<(std::ostream& out, CertGenTime value) {
585 switch (value) {
586 case CertGenTime::kBefore:
587 return out << "before";
588 case CertGenTime::kDuring:
589 return out << "during";
590 default:
591 return out << "unknown";
592 }
593}
594
595// Whether the fake certificate generator will produce a certificate or fail.
596enum class CertGenResult { kSucceed, kFail };
597std::ostream& operator<<(std::ostream& out, CertGenResult value) {
598 switch (value) {
599 case CertGenResult::kSucceed:
600 return out << "succeed";
601 case CertGenResult::kFail:
602 return out << "fail";
603 default:
604 return out << "unknown";
605 }
606}
607
Steve Anton71182f42018-01-19 14:59:54 -0800608class PeerConnectionCryptoDtlsCertGenTest
609 : public PeerConnectionCryptoBaseTest,
610 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
611 SdpType,
612 CertGenTime,
613 CertGenResult,
614 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700615 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800616 PeerConnectionCryptoDtlsCertGenTest()
617 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
618 sdp_type_ = std::get<1>(GetParam());
619 cert_gen_time_ = std::get<2>(GetParam());
620 cert_gen_result_ = std::get<3>(GetParam());
621 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700622 }
623
624 SdpType sdp_type_;
625 CertGenTime cert_gen_time_;
626 CertGenResult cert_gen_result_;
627 size_t concurrent_calls_;
628};
629
Steve Anton71182f42018-01-19 14:59:54 -0800630TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700631 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700632 auto owned_fake_certificate_generator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200633 std::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700634 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
635 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
636 CertGenResult::kFail);
637 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
638 CertGenTime::kDuring);
639 WrapperPtr pc;
640 if (sdp_type_ == SdpType::kOffer) {
641 pc = CreatePeerConnectionWithAudioVideo(
642 config, std::move(owned_fake_certificate_generator));
643 } else {
644 auto caller = CreatePeerConnectionWithAudioVideo(config);
645 pc = CreatePeerConnectionWithAudioVideo(
646 config, std::move(owned_fake_certificate_generator));
647 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
648 }
649 if (cert_gen_time_ == CertGenTime::kBefore) {
650 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
651 fake_certificate_generator->generated_failures() >
652 0,
653 kGenerateCertTimeout);
654 } else {
655 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
656 fake_certificate_generator->set_should_wait(false);
657 }
658 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
659 observers;
660 for (size_t i = 0; i < concurrent_calls_; i++) {
661 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
Tommi87f70902021-04-27 14:43:08 +0200662 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700663 observers.push_back(observer);
664 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerafb246b2022-04-20 14:26:50 +0200665 pc->pc()->CreateOffer(observer.get(),
Niels Möllerf06f9232018-08-07 12:32:18 +0200666 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700667 } else {
Niels Möllerafb246b2022-04-20 14:26:50 +0200668 pc->pc()->CreateAnswer(observer.get(),
Niels Möllerf06f9232018-08-07 12:32:18 +0200669 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700670 }
671 }
672 for (auto& observer : observers) {
673 EXPECT_TRUE_WAIT(observer->called(), 1000);
674 if (cert_gen_result_ == CertGenResult::kSucceed) {
675 EXPECT_TRUE(observer->result());
676 } else {
677 EXPECT_FALSE(observer->result());
678 }
679 }
680}
681
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100682INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800683 PeerConnectionCryptoTest,
684 PeerConnectionCryptoDtlsCertGenTest,
Florent Castelli15a38de2022-04-06 00:38:21 +0200685 Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
Steve Anton71182f42018-01-19 14:59:54 -0800686 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700687 Values(CertGenTime::kBefore, CertGenTime::kDuring),
688 Values(CertGenResult::kSucceed, CertGenResult::kFail),
689 Values(1, 3)));
690
Steve Anton8a63f782017-10-23 13:08:53 -0700691// Test that we can create and set an answer correctly when different
692// SSL roles have been negotiated for different transports.
693// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800694TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700695 auto caller = CreatePeerConnectionWithAudioVideo();
696 auto callee = CreatePeerConnectionWithAudioVideo();
697
698 RTCOfferAnswerOptions options_no_bundle;
699 options_no_bundle.use_rtp_mux = false;
700
701 // First, negotiate different SSL roles for audio and video.
702 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
703 auto answer = callee->CreateAnswer(options_no_bundle);
704
705 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
706 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
707
708 ASSERT_TRUE(
709 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
710 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
711
712 // Now create an offer in the reverse direction, and ensure the initial
713 // offerer responds with an answer with the correct SSL roles.
714 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
715 answer = caller->CreateAnswer(options_no_bundle);
716
717 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
718 AudioConnectionRole(answer->description()));
719 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
720 VideoConnectionRole(answer->description()));
721
722 ASSERT_TRUE(
723 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
724 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
725
726 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
727 // audio is transferred over to video in the answer that completes the BUNDLE
728 // negotiation.
729 RTCOfferAnswerOptions options_bundle;
730 options_bundle.use_rtp_mux = true;
731
732 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
733 answer = caller->CreateAnswer(options_bundle);
734
735 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
736 AudioConnectionRole(answer->description()));
737 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
738 VideoConnectionRole(answer->description()));
739
740 ASSERT_TRUE(
741 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
742 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
743}
744
Steve Anton80dd7b52018-02-16 17:08:42 -0800745// Tests that if the DTLS fingerprint is invalid then all future calls to
746// SetLocalDescription and SetRemoteDescription will fail due to a session
747// error.
748// This is a regression test for crbug.com/800775
749TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
750 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
751 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
752
753 auto caller = CreatePeerConnectionWithAudioVideo();
754 RTCConfiguration callee_config;
Steve Anton80dd7b52018-02-16 17:08:42 -0800755 callee_config.certificates.push_back(callee_certificate);
756 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
757
758 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
759
760 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 10:40:48 -0700761 auto valid_answer = callee->CreateAnswer();
762 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-16 17:08:42 -0800763 auto* audio_content =
764 cricket::GetFirstAudioContent(invalid_answer->description());
765 ASSERT_TRUE(audio_content);
766 auto* audio_transport_info =
767 invalid_answer->description()->GetTransportInfoByName(
768 audio_content->name);
769 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700770 audio_transport_info->description.identity_fingerprint =
771 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800772
773 // Set the invalid answer and expect a fingerprint error.
774 std::string error;
775 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
776 EXPECT_PRED_FORMAT2(AssertStringContains, error,
777 "Local fingerprint does not match identity.");
778
779 // Make sure that setting a valid remote offer or local answer also fails now.
780 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
781 EXPECT_PRED_FORMAT2(AssertStringContains, error,
782 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 10:40:48 -0700783 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-16 17:08:42 -0800784 EXPECT_PRED_FORMAT2(AssertStringContains, error,
785 "Session error code: ERROR_CONTENT.");
786}
787
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100788INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
789 PeerConnectionCryptoTest,
Florent Castelli15a38de2022-04-06 00:38:21 +0200790 Values(SdpSemantics::kPlanB_DEPRECATED,
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100791 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 14:59:54 -0800792
Steve Anton6b63cd52017-10-06 11:20:31 -0700793} // namespace webrtc