blob: 5ed93431a3c03b31948db18dc7520437cada6cc3 [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"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000046#include "rtc_base/rtc_certificate.h"
47#include "rtc_base/rtc_certificate_generator.h"
48#include "rtc_base/ssl_fingerprint.h"
49#include "rtc_base/thread.h"
50#include "test/gtest.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070051#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080052#include "pc/test/android_test_initializer.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070053#endif
Steve Anton10542f22019-01-11 09:11:00 -080054#include "pc/test/fake_audio_capture_module.h"
55#include "pc/test/fake_rtc_certificate_generator.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070056#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080057#include "rtc_base/virtual_socket_server.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070058
59namespace webrtc {
60
61using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 13:08:53 -070062using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 11:20:31 -070063using ::testing::Combine;
Jonas Olssona4d87372019-07-05 19:08:33 +020064using ::testing::Values;
Steve Anton6b63cd52017-10-06 11:20:31 -070065
66constexpr int kGenerateCertTimeout = 1000;
67
Steve Anton71182f42018-01-19 14:59:54 -080068class PeerConnectionCryptoBaseTest : public ::testing::Test {
Steve Anton6b63cd52017-10-06 11:20:31 -070069 protected:
70 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
71
Steve Anton71182f42018-01-19 14:59:54 -080072 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
73 : vss_(new rtc::VirtualSocketServer()),
74 main_(vss_.get()),
75 sdp_semantics_(sdp_semantics) {
Steve Anton6b63cd52017-10-06 11:20:31 -070076#ifdef WEBRTC_ANDROID
77 InitializeAndroidObjects();
78#endif
79 pc_factory_ = CreatePeerConnectionFactory(
80 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020081 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
Anders Carlsson67537952018-05-03 11:28:29 +020082 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
83 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
84 nullptr /* audio_processing */);
Steve Anton6b63cd52017-10-06 11:20:31 -070085 }
86
Steve Anton8a63f782017-10-23 13:08:53 -070087 WrapperPtr CreatePeerConnection() {
88 return CreatePeerConnection(RTCConfiguration());
89 }
90
Steve Anton6b63cd52017-10-06 11:20:31 -070091 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
92 return CreatePeerConnection(config, nullptr);
93 }
94
95 WrapperPtr CreatePeerConnection(
96 const RTCConfiguration& config,
97 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020098 auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
Steve Anton6b63cd52017-10-06 11:20:31 -070099 rtc::Thread::Current(), nullptr);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200100 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton71182f42018-01-19 14:59:54 -0800101 RTCConfiguration modified_config = config;
102 modified_config.sdp_semantics = sdp_semantics_;
Florent Castelli72424402022-04-06 03:45:10 +0200103 PeerConnectionDependencies pc_dependencies(observer.get());
104 pc_dependencies.allocator = std::move(fake_port_allocator);
105 pc_dependencies.cert_generator = std::move(cert_gen);
106 auto result = pc_factory_->CreatePeerConnectionOrError(
107 modified_config, std::move(pc_dependencies));
108 if (!result.ok()) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700109 return nullptr;
110 }
111
Niels Möllerafb246b2022-04-20 14:26:50 +0200112 observer->SetPeerConnectionInterface(result.value().get());
Florent Castelli72424402022-04-06 03:45:10 +0200113 return std::make_unique<PeerConnectionWrapper>(
114 pc_factory_, result.MoveValue(), std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -0700115 }
116
117 // Accepts the same arguments as CreatePeerConnection and adds default audio
118 // and video tracks.
119 template <typename... Args>
120 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
121 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
122 if (!wrapper) {
123 return nullptr;
124 }
Steve Anton8d3444d2017-10-20 15:30:51 -0700125 wrapper->AddAudioTrack("a");
126 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -0700127 return wrapper;
128 }
129
Steve Anton8a63f782017-10-23 13:08:53 -0700130 cricket::ConnectionRole& AudioConnectionRole(
131 cricket::SessionDescription* desc) {
132 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
133 }
134
135 cricket::ConnectionRole& VideoConnectionRole(
136 cricket::SessionDescription* desc) {
137 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
138 }
139
140 cricket::ConnectionRole& ConnectionRoleFromContent(
141 cricket::SessionDescription* desc,
142 cricket::ContentInfo* content) {
143 RTC_DCHECK(content);
144 auto* transport_info = desc->GetTransportInfoByName(content->name);
145 RTC_DCHECK(transport_info);
146 return transport_info->description.connection_role;
147 }
148
Steve Anton6b63cd52017-10-06 11:20:31 -0700149 std::unique_ptr<rtc::VirtualSocketServer> vss_;
150 rtc::AutoSocketServerThread main_;
151 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800152 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700153};
154
155SdpContentPredicate HaveDtlsFingerprint() {
156 return [](const cricket::ContentInfo* content,
157 const cricket::TransportInfo* transport) {
158 return transport->description.identity_fingerprint != nullptr;
159 };
160}
161
Harald Alvestrand0d018412021-11-04 13:52:31 +0000162SdpContentPredicate HaveSdesCryptos() {
163 return [](const cricket::ContentInfo* content,
164 const cricket::TransportInfo* transport) {
165 return !content->media_description()->cryptos().empty();
166 };
167}
168
Steve Anton6b63cd52017-10-06 11:20:31 -0700169SdpContentPredicate HaveProtocol(const std::string& protocol) {
170 return [protocol](const cricket::ContentInfo* content,
171 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800172 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700173 };
174}
175
Harald Alvestrand0d018412021-11-04 13:52:31 +0000176SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
177 return [num_crypto_suites](const cricket::ContentInfo* content,
178 const cricket::TransportInfo* transport) {
179 const auto& cryptos = content->media_description()->cryptos();
180 if (cryptos.size() != num_crypto_suites) {
181 return false;
182 }
183 for (size_t i = 0; i < cryptos.size(); ++i) {
184 if (cryptos[i].key_params.size() == 67U &&
185 cryptos[i].cipher_suite == "AEAD_AES_256_GCM")
186 return true;
187 }
188 return false;
189 };
190}
191
Steve Anton71182f42018-01-19 14:59:54 -0800192class PeerConnectionCryptoTest
193 : public PeerConnectionCryptoBaseTest,
194 public ::testing::WithParamInterface<SdpSemantics> {
195 protected:
196 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
197};
198
Harald Alvestrand0d018412021-11-04 13:52:31 +0000199SdpContentMutator RemoveSdesCryptos() {
200 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
201 content->media_description()->set_cryptos({});
202 };
203}
204
Steve Anton6b63cd52017-10-06 11:20:31 -0700205SdpContentMutator RemoveDtlsFingerprint() {
206 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
207 transport->description.identity_fingerprint.reset();
208 };
209}
210
Harald Alvestrand0d018412021-11-04 13:52:31 +0000211// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
212// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800213TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700214 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700215 auto caller = CreatePeerConnectionWithAudioVideo(config);
216
217 auto offer = caller->CreateOffer();
218 ASSERT_TRUE(offer);
219
220 ASSERT_FALSE(offer->description()->contents().empty());
221 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000222 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700223 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
224 offer->description()));
225}
Steve Anton71182f42018-01-19 14:59:54 -0800226TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700227 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700228 auto caller = CreatePeerConnectionWithAudioVideo(config);
229 auto callee = CreatePeerConnectionWithAudioVideo(config);
230
231 callee->SetRemoteDescription(caller->CreateOffer());
232 auto answer = callee->CreateAnswer();
233 ASSERT_TRUE(answer);
234
235 ASSERT_FALSE(answer->description()->contents().empty());
236 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000237 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700238 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
239 answer->description()));
240}
241
Harald Alvestrandca327932022-04-04 15:37:31 +0000242#if defined(WEBRTC_FUCHSIA)
Harald Alvestrand50b95522021-11-18 10:01:06 +0000243// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
244// should not have a DTLS fingerprint.
245TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
246 RTCConfiguration config;
247 config.enable_dtls_srtp.emplace(false);
248 auto caller = CreatePeerConnectionWithAudioVideo(config);
249
250 auto offer = caller->CreateOffer();
251 ASSERT_TRUE(offer);
252
253 ASSERT_FALSE(offer->description()->contents().empty());
254 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
255 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
256 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
257 offer->description()));
258}
Harald Alvestrandca327932022-04-04 15:37:31 +0000259
Harald Alvestrand50b95522021-11-18 10:01:06 +0000260TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
261 RTCConfiguration config;
262 config.enable_dtls_srtp.emplace(false);
263 auto caller = CreatePeerConnectionWithAudioVideo(config);
264 auto callee = CreatePeerConnectionWithAudioVideo(config);
265
266 callee->SetRemoteDescription(caller->CreateOffer());
267 auto answer = callee->CreateAnswer();
268 ASSERT_TRUE(answer);
269
270 ASSERT_FALSE(answer->description()->contents().empty());
271 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
272 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
273 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
274 answer->description()));
275}
276
Steve Anton6b63cd52017-10-06 11:20:31 -0700277// When encryption is disabled, the SDP offer/answer should have neither a DTLS
278// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800279TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700280 PeerConnectionFactoryInterface::Options options;
281 options.disable_encryption = true;
282 pc_factory_->SetOptions(options);
283
284 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000285 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700286 auto caller = CreatePeerConnectionWithAudioVideo(config);
287
288 auto offer = caller->CreateOffer();
289 ASSERT_TRUE(offer);
290
291 ASSERT_FALSE(offer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000292 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700293 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
294 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
295 offer->description()));
296}
Harald Alvestrandca327932022-04-04 15:37:31 +0000297
Steve Anton71182f42018-01-19 14:59:54 -0800298TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700299 PeerConnectionFactoryInterface::Options options;
300 options.disable_encryption = true;
301 pc_factory_->SetOptions(options);
302
303 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000304 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700305 auto caller = CreatePeerConnectionWithAudioVideo(config);
306 auto callee = CreatePeerConnectionWithAudioVideo(config);
307
308 callee->SetRemoteDescription(caller->CreateOffer());
309 auto answer = callee->CreateAnswer();
310 ASSERT_TRUE(answer);
311
312 ASSERT_FALSE(answer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000313 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700314 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
315 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
316 answer->description()));
317}
318
Harald Alvestrand50b95522021-11-18 10:01:06 +0000319// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
320// set in the configuration it should overrite the settings set in the factory.
321TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
322 PeerConnectionFactoryInterface::Options options;
323 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
324 pc_factory_->SetOptions(options);
325
326 RTCConfiguration config;
327 config.enable_dtls_srtp.emplace(false);
328 CryptoOptions crypto_options;
329 crypto_options.srtp.enable_gcm_crypto_suites = false;
330 config.crypto_options = crypto_options;
331 auto caller = CreatePeerConnectionWithAudioVideo(config);
332
333 auto offer = caller->CreateOffer();
334 ASSERT_TRUE(offer);
335
336 ASSERT_FALSE(offer->description()->contents().empty());
337 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
338 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
339}
340
341// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
342// should have the correct ciphers in the SDES crypto options.
343// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
344// in the answer.
345TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
346 PeerConnectionFactoryInterface::Options options;
347 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
348 pc_factory_->SetOptions(options);
349
350 RTCConfiguration config;
351 config.enable_dtls_srtp.emplace(false);
352 auto caller = CreatePeerConnectionWithAudioVideo(config);
353
354 auto offer = caller->CreateOffer();
355 ASSERT_TRUE(offer);
356
357 ASSERT_FALSE(offer->description()->contents().empty());
358 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
359}
360
361TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
362 PeerConnectionFactoryInterface::Options options;
363 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
364 pc_factory_->SetOptions(options);
365
366 RTCConfiguration config;
367 config.enable_dtls_srtp.emplace(false);
368 auto caller = CreatePeerConnectionWithAudioVideo(config);
369 auto callee = CreatePeerConnectionWithAudioVideo(config);
370
371 auto offer = caller->CreateOffer();
372 for (cricket::ContentInfo& content : offer->description()->contents()) {
373 auto cryptos = content.media_description()->cryptos();
374 cryptos.erase(cryptos.begin()); // Assumes that non-GCM is the default.
375 content.media_description()->set_cryptos(cryptos);
376 }
377
378 callee->SetRemoteDescription(std::move(offer));
379 auto answer = callee->CreateAnswer();
380 ASSERT_TRUE(answer);
381
382 ASSERT_FALSE(answer->description()->contents().empty());
383 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
384}
385
Steve Anton71182f42018-01-19 14:59:54 -0800386TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700387 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700388 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700389 pc_factory_->SetOptions(options);
390
391 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000392 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700393 auto caller = CreatePeerConnectionWithAudioVideo(config);
394 auto callee = CreatePeerConnectionWithAudioVideo(config);
395
396 auto offer = caller->CreateOffer();
397 ASSERT_TRUE(offer);
398 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
399
400 auto answer = callee->CreateAnswer();
401 ASSERT_TRUE(answer);
402 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
Harald Alvestrand50b95522021-11-18 10:01:06 +0000403}
404
405// The following group tests that two PeerConnections can successfully exchange
406// an offer/answer when DTLS is off and that they will refuse any offer/answer
407// applied locally/remotely if it does not include SDES cryptos.
408TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
409 RTCConfiguration config;
410 config.enable_dtls_srtp.emplace(false);
411 auto caller = CreatePeerConnectionWithAudioVideo(config);
412 auto callee = CreatePeerConnectionWithAudioVideo(config);
413
414 auto offer = caller->CreateOfferAndSetAsLocal();
415 ASSERT_TRUE(offer);
416 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
417
418 auto answer = callee->CreateAnswerAndSetAsLocal();
419 ASSERT_TRUE(answer);
420 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
421}
422TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
423 RTCConfiguration config;
424 config.enable_dtls_srtp.emplace(false);
425 auto caller = CreatePeerConnectionWithAudioVideo(config);
426
427 auto offer = caller->CreateOffer();
428 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
429
430 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
431}
432TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
433 RTCConfiguration config;
434 config.enable_dtls_srtp.emplace(false);
435 auto caller = CreatePeerConnectionWithAudioVideo(config);
436 auto callee = CreatePeerConnectionWithAudioVideo(config);
437
438 auto offer = caller->CreateOffer();
439 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
440
441 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
442}
443TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
444 RTCConfiguration config;
445 config.enable_dtls_srtp.emplace(false);
446 auto caller = CreatePeerConnectionWithAudioVideo(config);
447 auto callee = CreatePeerConnectionWithAudioVideo(config);
448
449 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
450 auto answer = callee->CreateAnswer();
451 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
452
453 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
454}
455TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
456 RTCConfiguration config;
457 config.enable_dtls_srtp.emplace(false);
458 auto caller = CreatePeerConnectionWithAudioVideo(config);
459 auto callee = CreatePeerConnectionWithAudioVideo(config);
460
461 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
462 auto answer = callee->CreateAnswerAndSetAsLocal();
463 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
464
465 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
Steve Anton6b63cd52017-10-06 11:20:31 -0700466}
Harald Alvestrandca327932022-04-04 15:37:31 +0000467#endif
Steve Anton6b63cd52017-10-06 11:20:31 -0700468
469// The following group tests that two PeerConnections can successfully exchange
470// an offer/answer when DTLS is on and that they will refuse any offer/answer
471// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800472TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700473 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700474 auto caller = CreatePeerConnectionWithAudioVideo(config);
475 auto callee = CreatePeerConnectionWithAudioVideo(config);
476
477 auto offer = caller->CreateOfferAndSetAsLocal();
478 ASSERT_TRUE(offer);
479 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
480
481 auto answer = callee->CreateAnswerAndSetAsLocal();
482 ASSERT_TRUE(answer);
483 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
484}
Steve Anton71182f42018-01-19 14:59:54 -0800485TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700486 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
487 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700488 auto caller = CreatePeerConnectionWithAudioVideo(config);
489
490 auto offer = caller->CreateOffer();
491 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
492
493 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
494}
Steve Anton71182f42018-01-19 14:59:54 -0800495TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700496 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
497 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700498 auto caller = CreatePeerConnectionWithAudioVideo(config);
499 auto callee = CreatePeerConnectionWithAudioVideo(config);
500
501 auto offer = caller->CreateOffer();
502 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
503
504 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
505}
Steve Anton71182f42018-01-19 14:59:54 -0800506TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700507 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
508 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700509 auto caller = CreatePeerConnectionWithAudioVideo(config);
510 auto callee = CreatePeerConnectionWithAudioVideo(config);
511
512 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
513 auto answer = callee->CreateAnswer();
514 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
515}
Steve Anton71182f42018-01-19 14:59:54 -0800516TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700517 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
518 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700519 auto caller = CreatePeerConnectionWithAudioVideo(config);
520 auto callee = CreatePeerConnectionWithAudioVideo(config);
521
522 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
523 auto answer = callee->CreateAnswerAndSetAsLocal();
524 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
525
526 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
527}
528
Harald Alvestrandca327932022-04-04 15:37:31 +0000529#if defined(WEBRTC_FUCHSIA)
Steve Anton6b63cd52017-10-06 11:20:31 -0700530// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800531TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700532 PeerConnectionFactoryInterface::Options options;
533 options.disable_encryption = true;
534 pc_factory_->SetOptions(options);
535
536 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000537 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700538 auto caller = CreatePeerConnectionWithAudioVideo(config);
539 auto callee = CreatePeerConnectionWithAudioVideo(config);
540
541 auto offer = caller->CreateOfferAndSetAsLocal();
542 ASSERT_TRUE(offer);
543 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
544
545 auto answer = callee->CreateAnswerAndSetAsLocal();
546 ASSERT_TRUE(answer);
547 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
548}
Harald Alvestrandca327932022-04-04 15:37:31 +0000549#endif
Steve Anton6b63cd52017-10-06 11:20:31 -0700550
551// Tests that a DTLS call can be established when the certificate is specified
552// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800553TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700554 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
555 RTCConfiguration caller_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700556 caller_config.certificates.push_back(
557 FakeRTCCertificateGenerator::GenerateCertificate());
558 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
559
560 RTCConfiguration callee_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700561 callee_config.certificates.push_back(
562 FakeRTCCertificateGenerator::GenerateCertificate());
563 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
564
565 auto offer = caller->CreateOfferAndSetAsLocal();
566 ASSERT_TRUE(offer);
567 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
568
569 auto answer = callee->CreateAnswerAndSetAsLocal();
570 ASSERT_TRUE(answer);
571 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
572}
573
574// The following parameterized test verifies that CreateOffer/CreateAnswer
575// returns successfully (or with failure if the underlying certificate generator
576// fails) no matter when the DTLS certificate is generated. If multiple
577// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
578// they all finish after the certificate is generated.
579
Steve Anton6b63cd52017-10-06 11:20:31 -0700580// Whether the certificate will be generated before calling CreateOffer or
581// while CreateOffer is executing.
582enum class CertGenTime { kBefore, kDuring };
583std::ostream& operator<<(std::ostream& out, CertGenTime value) {
584 switch (value) {
585 case CertGenTime::kBefore:
586 return out << "before";
587 case CertGenTime::kDuring:
588 return out << "during";
589 default:
590 return out << "unknown";
591 }
592}
593
594// Whether the fake certificate generator will produce a certificate or fail.
595enum class CertGenResult { kSucceed, kFail };
596std::ostream& operator<<(std::ostream& out, CertGenResult value) {
597 switch (value) {
598 case CertGenResult::kSucceed:
599 return out << "succeed";
600 case CertGenResult::kFail:
601 return out << "fail";
602 default:
603 return out << "unknown";
604 }
605}
606
Steve Anton71182f42018-01-19 14:59:54 -0800607class PeerConnectionCryptoDtlsCertGenTest
608 : public PeerConnectionCryptoBaseTest,
609 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
610 SdpType,
611 CertGenTime,
612 CertGenResult,
613 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700614 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800615 PeerConnectionCryptoDtlsCertGenTest()
616 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
617 sdp_type_ = std::get<1>(GetParam());
618 cert_gen_time_ = std::get<2>(GetParam());
619 cert_gen_result_ = std::get<3>(GetParam());
620 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700621 }
622
623 SdpType sdp_type_;
624 CertGenTime cert_gen_time_;
625 CertGenResult cert_gen_result_;
626 size_t concurrent_calls_;
627};
628
Steve Anton71182f42018-01-19 14:59:54 -0800629TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700630 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700631 auto owned_fake_certificate_generator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200632 std::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700633 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
634 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
635 CertGenResult::kFail);
636 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
637 CertGenTime::kDuring);
638 WrapperPtr pc;
639 if (sdp_type_ == SdpType::kOffer) {
640 pc = CreatePeerConnectionWithAudioVideo(
641 config, std::move(owned_fake_certificate_generator));
642 } else {
643 auto caller = CreatePeerConnectionWithAudioVideo(config);
644 pc = CreatePeerConnectionWithAudioVideo(
645 config, std::move(owned_fake_certificate_generator));
646 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
647 }
648 if (cert_gen_time_ == CertGenTime::kBefore) {
649 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
650 fake_certificate_generator->generated_failures() >
651 0,
652 kGenerateCertTimeout);
653 } else {
654 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
655 fake_certificate_generator->set_should_wait(false);
656 }
657 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
658 observers;
659 for (size_t i = 0; i < concurrent_calls_; i++) {
660 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
Tommi87f70902021-04-27 14:43:08 +0200661 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700662 observers.push_back(observer);
663 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerafb246b2022-04-20 14:26:50 +0200664 pc->pc()->CreateOffer(observer.get(),
Niels Möllerf06f9232018-08-07 12:32:18 +0200665 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700666 } else {
Niels Möllerafb246b2022-04-20 14:26:50 +0200667 pc->pc()->CreateAnswer(observer.get(),
Niels Möllerf06f9232018-08-07 12:32:18 +0200668 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700669 }
670 }
671 for (auto& observer : observers) {
672 EXPECT_TRUE_WAIT(observer->called(), 1000);
673 if (cert_gen_result_ == CertGenResult::kSucceed) {
674 EXPECT_TRUE(observer->result());
675 } else {
676 EXPECT_FALSE(observer->result());
677 }
678 }
679}
680
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100681INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800682 PeerConnectionCryptoTest,
683 PeerConnectionCryptoDtlsCertGenTest,
Florent Castelli15a38de2022-04-06 00:38:21 +0200684 Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
Steve Anton71182f42018-01-19 14:59:54 -0800685 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700686 Values(CertGenTime::kBefore, CertGenTime::kDuring),
687 Values(CertGenResult::kSucceed, CertGenResult::kFail),
688 Values(1, 3)));
689
Steve Anton8a63f782017-10-23 13:08:53 -0700690// Test that we can create and set an answer correctly when different
691// SSL roles have been negotiated for different transports.
692// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800693TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700694 auto caller = CreatePeerConnectionWithAudioVideo();
695 auto callee = CreatePeerConnectionWithAudioVideo();
696
697 RTCOfferAnswerOptions options_no_bundle;
698 options_no_bundle.use_rtp_mux = false;
699
700 // First, negotiate different SSL roles for audio and video.
701 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
702 auto answer = callee->CreateAnswer(options_no_bundle);
703
704 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
705 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
706
707 ASSERT_TRUE(
708 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
709 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
710
711 // Now create an offer in the reverse direction, and ensure the initial
712 // offerer responds with an answer with the correct SSL roles.
713 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
714 answer = caller->CreateAnswer(options_no_bundle);
715
716 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
717 AudioConnectionRole(answer->description()));
718 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
719 VideoConnectionRole(answer->description()));
720
721 ASSERT_TRUE(
722 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
723 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
724
725 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
726 // audio is transferred over to video in the answer that completes the BUNDLE
727 // negotiation.
728 RTCOfferAnswerOptions options_bundle;
729 options_bundle.use_rtp_mux = true;
730
731 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
732 answer = caller->CreateAnswer(options_bundle);
733
734 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
735 AudioConnectionRole(answer->description()));
736 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
737 VideoConnectionRole(answer->description()));
738
739 ASSERT_TRUE(
740 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
741 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
742}
743
Steve Anton80dd7b52018-02-16 17:08:42 -0800744// Tests that if the DTLS fingerprint is invalid then all future calls to
745// SetLocalDescription and SetRemoteDescription will fail due to a session
746// error.
747// This is a regression test for crbug.com/800775
748TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
749 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
750 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
751
752 auto caller = CreatePeerConnectionWithAudioVideo();
753 RTCConfiguration callee_config;
Steve Anton80dd7b52018-02-16 17:08:42 -0800754 callee_config.certificates.push_back(callee_certificate);
755 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
756
757 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
758
759 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 10:40:48 -0700760 auto valid_answer = callee->CreateAnswer();
761 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-16 17:08:42 -0800762 auto* audio_content =
763 cricket::GetFirstAudioContent(invalid_answer->description());
764 ASSERT_TRUE(audio_content);
765 auto* audio_transport_info =
766 invalid_answer->description()->GetTransportInfoByName(
767 audio_content->name);
768 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700769 audio_transport_info->description.identity_fingerprint =
770 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800771
772 // Set the invalid answer and expect a fingerprint error.
773 std::string error;
774 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
775 EXPECT_PRED_FORMAT2(AssertStringContains, error,
776 "Local fingerprint does not match identity.");
777
778 // Make sure that setting a valid remote offer or local answer also fails now.
779 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
780 EXPECT_PRED_FORMAT2(AssertStringContains, error,
781 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 10:40:48 -0700782 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-16 17:08:42 -0800783 EXPECT_PRED_FORMAT2(AssertStringContains, error,
784 "Session error code: ERROR_CONTENT.");
785}
786
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100787INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
788 PeerConnectionCryptoTest,
Florent Castelli15a38de2022-04-06 00:38:21 +0200789 Values(SdpSemantics::kPlanB_DEPRECATED,
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100790 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 14:59:54 -0800791
Steve Anton6b63cd52017-10-06 11:20:31 -0700792} // namespace webrtc