blob: 9c9e9b6c559e1c57b2ac0e9266ff8e78bcfa9553 [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_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700104 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton71182f42018-01-19 14:59:54 -0800105 modified_config, std::move(fake_port_allocator), std::move(cert_gen),
Steve Anton6b63cd52017-10-06 11:20:31 -0700106 observer.get());
107 if (!pc) {
108 return nullptr;
109 }
110
Yves Gerey4e933292018-10-31 15:36:05 +0100111 observer->SetPeerConnectionInterface(pc.get());
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200112 return std::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
113 std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -0700114 }
115
116 // Accepts the same arguments as CreatePeerConnection and adds default audio
117 // and video tracks.
118 template <typename... Args>
119 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
120 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
121 if (!wrapper) {
122 return nullptr;
123 }
Steve Anton8d3444d2017-10-20 15:30:51 -0700124 wrapper->AddAudioTrack("a");
125 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -0700126 return wrapper;
127 }
128
Steve Anton8a63f782017-10-23 13:08:53 -0700129 cricket::ConnectionRole& AudioConnectionRole(
130 cricket::SessionDescription* desc) {
131 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
132 }
133
134 cricket::ConnectionRole& VideoConnectionRole(
135 cricket::SessionDescription* desc) {
136 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
137 }
138
139 cricket::ConnectionRole& ConnectionRoleFromContent(
140 cricket::SessionDescription* desc,
141 cricket::ContentInfo* content) {
142 RTC_DCHECK(content);
143 auto* transport_info = desc->GetTransportInfoByName(content->name);
144 RTC_DCHECK(transport_info);
145 return transport_info->description.connection_role;
146 }
147
Steve Anton6b63cd52017-10-06 11:20:31 -0700148 std::unique_ptr<rtc::VirtualSocketServer> vss_;
149 rtc::AutoSocketServerThread main_;
150 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800151 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700152};
153
154SdpContentPredicate HaveDtlsFingerprint() {
155 return [](const cricket::ContentInfo* content,
156 const cricket::TransportInfo* transport) {
157 return transport->description.identity_fingerprint != nullptr;
158 };
159}
160
Harald Alvestrand0d018412021-11-04 13:52:31 +0000161SdpContentPredicate HaveSdesCryptos() {
162 return [](const cricket::ContentInfo* content,
163 const cricket::TransportInfo* transport) {
164 return !content->media_description()->cryptos().empty();
165 };
166}
167
Steve Anton6b63cd52017-10-06 11:20:31 -0700168SdpContentPredicate HaveProtocol(const std::string& protocol) {
169 return [protocol](const cricket::ContentInfo* content,
170 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800171 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700172 };
173}
174
Harald Alvestrand0d018412021-11-04 13:52:31 +0000175SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
176 return [num_crypto_suites](const cricket::ContentInfo* content,
177 const cricket::TransportInfo* transport) {
178 const auto& cryptos = content->media_description()->cryptos();
179 if (cryptos.size() != num_crypto_suites) {
180 return false;
181 }
182 for (size_t i = 0; i < cryptos.size(); ++i) {
183 if (cryptos[i].key_params.size() == 67U &&
184 cryptos[i].cipher_suite == "AEAD_AES_256_GCM")
185 return true;
186 }
187 return false;
188 };
189}
190
Steve Anton71182f42018-01-19 14:59:54 -0800191class PeerConnectionCryptoTest
192 : public PeerConnectionCryptoBaseTest,
193 public ::testing::WithParamInterface<SdpSemantics> {
194 protected:
195 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
196};
197
Harald Alvestrand0d018412021-11-04 13:52:31 +0000198SdpContentMutator RemoveSdesCryptos() {
199 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
200 content->media_description()->set_cryptos({});
201 };
202}
203
Steve Anton6b63cd52017-10-06 11:20:31 -0700204SdpContentMutator RemoveDtlsFingerprint() {
205 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
206 transport->description.identity_fingerprint.reset();
207 };
208}
209
Harald Alvestrand0d018412021-11-04 13:52:31 +0000210// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
211// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800212TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700213 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700214 auto caller = CreatePeerConnectionWithAudioVideo(config);
215
216 auto offer = caller->CreateOffer();
217 ASSERT_TRUE(offer);
218
219 ASSERT_FALSE(offer->description()->contents().empty());
220 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000221 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700222 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
223 offer->description()));
224}
Steve Anton71182f42018-01-19 14:59:54 -0800225TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700226 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700227 auto caller = CreatePeerConnectionWithAudioVideo(config);
228 auto callee = CreatePeerConnectionWithAudioVideo(config);
229
230 callee->SetRemoteDescription(caller->CreateOffer());
231 auto answer = callee->CreateAnswer();
232 ASSERT_TRUE(answer);
233
234 ASSERT_FALSE(answer->description()->contents().empty());
235 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000236 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700237 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
238 answer->description()));
239}
240
Harald Alvestrandca327932022-04-04 15:37:31 +0000241#if defined(WEBRTC_FUCHSIA)
Harald Alvestrand50b95522021-11-18 10:01:06 +0000242// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
243// should not have a DTLS fingerprint.
244TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
245 RTCConfiguration config;
246 config.enable_dtls_srtp.emplace(false);
247 auto caller = CreatePeerConnectionWithAudioVideo(config);
248
249 auto offer = caller->CreateOffer();
250 ASSERT_TRUE(offer);
251
252 ASSERT_FALSE(offer->description()->contents().empty());
253 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
254 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
255 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
256 offer->description()));
257}
Harald Alvestrandca327932022-04-04 15:37:31 +0000258
Harald Alvestrand50b95522021-11-18 10:01:06 +0000259TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
260 RTCConfiguration config;
261 config.enable_dtls_srtp.emplace(false);
262 auto caller = CreatePeerConnectionWithAudioVideo(config);
263 auto callee = CreatePeerConnectionWithAudioVideo(config);
264
265 callee->SetRemoteDescription(caller->CreateOffer());
266 auto answer = callee->CreateAnswer();
267 ASSERT_TRUE(answer);
268
269 ASSERT_FALSE(answer->description()->contents().empty());
270 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
271 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
272 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
273 answer->description()));
274}
275
Steve Anton6b63cd52017-10-06 11:20:31 -0700276// When encryption is disabled, the SDP offer/answer should have neither a DTLS
277// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800278TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700279 PeerConnectionFactoryInterface::Options options;
280 options.disable_encryption = true;
281 pc_factory_->SetOptions(options);
282
283 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000284 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700285 auto caller = CreatePeerConnectionWithAudioVideo(config);
286
287 auto offer = caller->CreateOffer();
288 ASSERT_TRUE(offer);
289
290 ASSERT_FALSE(offer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000291 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700292 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
293 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
294 offer->description()));
295}
Harald Alvestrandca327932022-04-04 15:37:31 +0000296
Steve Anton71182f42018-01-19 14:59:54 -0800297TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700298 PeerConnectionFactoryInterface::Options options;
299 options.disable_encryption = true;
300 pc_factory_->SetOptions(options);
301
302 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000303 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700304 auto caller = CreatePeerConnectionWithAudioVideo(config);
305 auto callee = CreatePeerConnectionWithAudioVideo(config);
306
307 callee->SetRemoteDescription(caller->CreateOffer());
308 auto answer = callee->CreateAnswer();
309 ASSERT_TRUE(answer);
310
311 ASSERT_FALSE(answer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000312 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700313 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
314 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
315 answer->description()));
316}
317
Harald Alvestrand50b95522021-11-18 10:01:06 +0000318// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
319// set in the configuration it should overrite the settings set in the factory.
320TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
321 PeerConnectionFactoryInterface::Options options;
322 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
323 pc_factory_->SetOptions(options);
324
325 RTCConfiguration config;
326 config.enable_dtls_srtp.emplace(false);
327 CryptoOptions crypto_options;
328 crypto_options.srtp.enable_gcm_crypto_suites = false;
329 config.crypto_options = crypto_options;
330 auto caller = CreatePeerConnectionWithAudioVideo(config);
331
332 auto offer = caller->CreateOffer();
333 ASSERT_TRUE(offer);
334
335 ASSERT_FALSE(offer->description()->contents().empty());
336 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
337 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
338}
339
340// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
341// should have the correct ciphers in the SDES crypto options.
342// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
343// in the answer.
344TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
345 PeerConnectionFactoryInterface::Options options;
346 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
347 pc_factory_->SetOptions(options);
348
349 RTCConfiguration config;
350 config.enable_dtls_srtp.emplace(false);
351 auto caller = CreatePeerConnectionWithAudioVideo(config);
352
353 auto offer = caller->CreateOffer();
354 ASSERT_TRUE(offer);
355
356 ASSERT_FALSE(offer->description()->contents().empty());
357 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
358}
359
360TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
361 PeerConnectionFactoryInterface::Options options;
362 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
363 pc_factory_->SetOptions(options);
364
365 RTCConfiguration config;
366 config.enable_dtls_srtp.emplace(false);
367 auto caller = CreatePeerConnectionWithAudioVideo(config);
368 auto callee = CreatePeerConnectionWithAudioVideo(config);
369
370 auto offer = caller->CreateOffer();
371 for (cricket::ContentInfo& content : offer->description()->contents()) {
372 auto cryptos = content.media_description()->cryptos();
373 cryptos.erase(cryptos.begin()); // Assumes that non-GCM is the default.
374 content.media_description()->set_cryptos(cryptos);
375 }
376
377 callee->SetRemoteDescription(std::move(offer));
378 auto answer = callee->CreateAnswer();
379 ASSERT_TRUE(answer);
380
381 ASSERT_FALSE(answer->description()->contents().empty());
382 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
383}
384
Steve Anton71182f42018-01-19 14:59:54 -0800385TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700386 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700387 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700388 pc_factory_->SetOptions(options);
389
390 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000391 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700392 auto caller = CreatePeerConnectionWithAudioVideo(config);
393 auto callee = CreatePeerConnectionWithAudioVideo(config);
394
395 auto offer = caller->CreateOffer();
396 ASSERT_TRUE(offer);
397 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
398
399 auto answer = callee->CreateAnswer();
400 ASSERT_TRUE(answer);
401 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
Harald Alvestrand50b95522021-11-18 10:01:06 +0000402}
403
404// The following group tests that two PeerConnections can successfully exchange
405// an offer/answer when DTLS is off and that they will refuse any offer/answer
406// applied locally/remotely if it does not include SDES cryptos.
407TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
408 RTCConfiguration config;
409 config.enable_dtls_srtp.emplace(false);
410 auto caller = CreatePeerConnectionWithAudioVideo(config);
411 auto callee = CreatePeerConnectionWithAudioVideo(config);
412
413 auto offer = caller->CreateOfferAndSetAsLocal();
414 ASSERT_TRUE(offer);
415 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
416
417 auto answer = callee->CreateAnswerAndSetAsLocal();
418 ASSERT_TRUE(answer);
419 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
420}
421TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
422 RTCConfiguration config;
423 config.enable_dtls_srtp.emplace(false);
424 auto caller = CreatePeerConnectionWithAudioVideo(config);
425
426 auto offer = caller->CreateOffer();
427 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
428
429 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
430}
431TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
432 RTCConfiguration config;
433 config.enable_dtls_srtp.emplace(false);
434 auto caller = CreatePeerConnectionWithAudioVideo(config);
435 auto callee = CreatePeerConnectionWithAudioVideo(config);
436
437 auto offer = caller->CreateOffer();
438 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
439
440 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
441}
442TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
443 RTCConfiguration config;
444 config.enable_dtls_srtp.emplace(false);
445 auto caller = CreatePeerConnectionWithAudioVideo(config);
446 auto callee = CreatePeerConnectionWithAudioVideo(config);
447
448 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
449 auto answer = callee->CreateAnswer();
450 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
451
452 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
453}
454TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
455 RTCConfiguration config;
456 config.enable_dtls_srtp.emplace(false);
457 auto caller = CreatePeerConnectionWithAudioVideo(config);
458 auto callee = CreatePeerConnectionWithAudioVideo(config);
459
460 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
461 auto answer = callee->CreateAnswerAndSetAsLocal();
462 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
463
464 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
Steve Anton6b63cd52017-10-06 11:20:31 -0700465}
Harald Alvestrandca327932022-04-04 15:37:31 +0000466#endif
Steve Anton6b63cd52017-10-06 11:20:31 -0700467
468// The following group tests that two PeerConnections can successfully exchange
469// an offer/answer when DTLS is on and that they will refuse any offer/answer
470// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800471TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700472 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700473 auto caller = CreatePeerConnectionWithAudioVideo(config);
474 auto callee = CreatePeerConnectionWithAudioVideo(config);
475
476 auto offer = caller->CreateOfferAndSetAsLocal();
477 ASSERT_TRUE(offer);
478 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
479
480 auto answer = callee->CreateAnswerAndSetAsLocal();
481 ASSERT_TRUE(answer);
482 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
483}
Steve Anton71182f42018-01-19 14:59:54 -0800484TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700485 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
486 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700487 auto caller = CreatePeerConnectionWithAudioVideo(config);
488
489 auto offer = caller->CreateOffer();
490 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
491
492 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
493}
Steve Anton71182f42018-01-19 14:59:54 -0800494TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700495 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
496 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700497 auto caller = CreatePeerConnectionWithAudioVideo(config);
498 auto callee = CreatePeerConnectionWithAudioVideo(config);
499
500 auto offer = caller->CreateOffer();
501 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
502
503 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
504}
Steve Anton71182f42018-01-19 14:59:54 -0800505TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700506 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
507 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700508 auto caller = CreatePeerConnectionWithAudioVideo(config);
509 auto callee = CreatePeerConnectionWithAudioVideo(config);
510
511 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
512 auto answer = callee->CreateAnswer();
513 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
514}
Steve Anton71182f42018-01-19 14:59:54 -0800515TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700516 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
517 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700518 auto caller = CreatePeerConnectionWithAudioVideo(config);
519 auto callee = CreatePeerConnectionWithAudioVideo(config);
520
521 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
522 auto answer = callee->CreateAnswerAndSetAsLocal();
523 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
524
525 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
526}
527
Harald Alvestrandca327932022-04-04 15:37:31 +0000528#if defined(WEBRTC_FUCHSIA)
Steve Anton6b63cd52017-10-06 11:20:31 -0700529// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800530TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700531 PeerConnectionFactoryInterface::Options options;
532 options.disable_encryption = true;
533 pc_factory_->SetOptions(options);
534
535 RTCConfiguration config;
Harald Alvestrand50b95522021-11-18 10:01:06 +0000536 config.enable_dtls_srtp.emplace(false);
Steve Anton6b63cd52017-10-06 11:20:31 -0700537 auto caller = CreatePeerConnectionWithAudioVideo(config);
538 auto callee = CreatePeerConnectionWithAudioVideo(config);
539
540 auto offer = caller->CreateOfferAndSetAsLocal();
541 ASSERT_TRUE(offer);
542 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
543
544 auto answer = callee->CreateAnswerAndSetAsLocal();
545 ASSERT_TRUE(answer);
546 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
547}
Harald Alvestrandca327932022-04-04 15:37:31 +0000548#endif
Steve Anton6b63cd52017-10-06 11:20:31 -0700549
550// Tests that a DTLS call can be established when the certificate is specified
551// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800552TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700553 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
554 RTCConfiguration caller_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700555 caller_config.certificates.push_back(
556 FakeRTCCertificateGenerator::GenerateCertificate());
557 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
558
559 RTCConfiguration callee_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700560 callee_config.certificates.push_back(
561 FakeRTCCertificateGenerator::GenerateCertificate());
562 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
563
564 auto offer = caller->CreateOfferAndSetAsLocal();
565 ASSERT_TRUE(offer);
566 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
567
568 auto answer = callee->CreateAnswerAndSetAsLocal();
569 ASSERT_TRUE(answer);
570 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
571}
572
573// The following parameterized test verifies that CreateOffer/CreateAnswer
574// returns successfully (or with failure if the underlying certificate generator
575// fails) no matter when the DTLS certificate is generated. If multiple
576// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
577// they all finish after the certificate is generated.
578
Steve Anton6b63cd52017-10-06 11:20:31 -0700579// Whether the certificate will be generated before calling CreateOffer or
580// while CreateOffer is executing.
581enum class CertGenTime { kBefore, kDuring };
582std::ostream& operator<<(std::ostream& out, CertGenTime value) {
583 switch (value) {
584 case CertGenTime::kBefore:
585 return out << "before";
586 case CertGenTime::kDuring:
587 return out << "during";
588 default:
589 return out << "unknown";
590 }
591}
592
593// Whether the fake certificate generator will produce a certificate or fail.
594enum class CertGenResult { kSucceed, kFail };
595std::ostream& operator<<(std::ostream& out, CertGenResult value) {
596 switch (value) {
597 case CertGenResult::kSucceed:
598 return out << "succeed";
599 case CertGenResult::kFail:
600 return out << "fail";
601 default:
602 return out << "unknown";
603 }
604}
605
Steve Anton71182f42018-01-19 14:59:54 -0800606class PeerConnectionCryptoDtlsCertGenTest
607 : public PeerConnectionCryptoBaseTest,
608 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
609 SdpType,
610 CertGenTime,
611 CertGenResult,
612 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700613 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800614 PeerConnectionCryptoDtlsCertGenTest()
615 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
616 sdp_type_ = std::get<1>(GetParam());
617 cert_gen_time_ = std::get<2>(GetParam());
618 cert_gen_result_ = std::get<3>(GetParam());
619 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700620 }
621
622 SdpType sdp_type_;
623 CertGenTime cert_gen_time_;
624 CertGenResult cert_gen_result_;
625 size_t concurrent_calls_;
626};
627
Steve Anton71182f42018-01-19 14:59:54 -0800628TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700629 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700630 auto owned_fake_certificate_generator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200631 std::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700632 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
633 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
634 CertGenResult::kFail);
635 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
636 CertGenTime::kDuring);
637 WrapperPtr pc;
638 if (sdp_type_ == SdpType::kOffer) {
639 pc = CreatePeerConnectionWithAudioVideo(
640 config, std::move(owned_fake_certificate_generator));
641 } else {
642 auto caller = CreatePeerConnectionWithAudioVideo(config);
643 pc = CreatePeerConnectionWithAudioVideo(
644 config, std::move(owned_fake_certificate_generator));
645 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
646 }
647 if (cert_gen_time_ == CertGenTime::kBefore) {
648 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
649 fake_certificate_generator->generated_failures() >
650 0,
651 kGenerateCertTimeout);
652 } else {
653 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
654 fake_certificate_generator->set_should_wait(false);
655 }
656 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
657 observers;
658 for (size_t i = 0; i < concurrent_calls_; i++) {
659 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
Tommi87f70902021-04-27 14:43:08 +0200660 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700661 observers.push_back(observer);
662 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 12:32:18 +0200663 pc->pc()->CreateOffer(observer,
664 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700665 } else {
Niels Möllerf06f9232018-08-07 12:32:18 +0200666 pc->pc()->CreateAnswer(observer,
667 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700668 }
669 }
670 for (auto& observer : observers) {
671 EXPECT_TRUE_WAIT(observer->called(), 1000);
672 if (cert_gen_result_ == CertGenResult::kSucceed) {
673 EXPECT_TRUE(observer->result());
674 } else {
675 EXPECT_FALSE(observer->result());
676 }
677 }
678}
679
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100680INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800681 PeerConnectionCryptoTest,
682 PeerConnectionCryptoDtlsCertGenTest,
683 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
684 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700685 Values(CertGenTime::kBefore, CertGenTime::kDuring),
686 Values(CertGenResult::kSucceed, CertGenResult::kFail),
687 Values(1, 3)));
688
Steve Anton8a63f782017-10-23 13:08:53 -0700689// Test that we can create and set an answer correctly when different
690// SSL roles have been negotiated for different transports.
691// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800692TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700693 auto caller = CreatePeerConnectionWithAudioVideo();
694 auto callee = CreatePeerConnectionWithAudioVideo();
695
696 RTCOfferAnswerOptions options_no_bundle;
697 options_no_bundle.use_rtp_mux = false;
698
699 // First, negotiate different SSL roles for audio and video.
700 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
701 auto answer = callee->CreateAnswer(options_no_bundle);
702
703 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
704 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
705
706 ASSERT_TRUE(
707 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
708 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
709
710 // Now create an offer in the reverse direction, and ensure the initial
711 // offerer responds with an answer with the correct SSL roles.
712 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
713 answer = caller->CreateAnswer(options_no_bundle);
714
715 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
716 AudioConnectionRole(answer->description()));
717 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
718 VideoConnectionRole(answer->description()));
719
720 ASSERT_TRUE(
721 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
722 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
723
724 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
725 // audio is transferred over to video in the answer that completes the BUNDLE
726 // negotiation.
727 RTCOfferAnswerOptions options_bundle;
728 options_bundle.use_rtp_mux = true;
729
730 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
731 answer = caller->CreateAnswer(options_bundle);
732
733 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
734 AudioConnectionRole(answer->description()));
735 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
736 VideoConnectionRole(answer->description()));
737
738 ASSERT_TRUE(
739 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
740 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
741}
742
Steve Anton80dd7b52018-02-16 17:08:42 -0800743// Tests that if the DTLS fingerprint is invalid then all future calls to
744// SetLocalDescription and SetRemoteDescription will fail due to a session
745// error.
746// This is a regression test for crbug.com/800775
747TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
748 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
749 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
750
751 auto caller = CreatePeerConnectionWithAudioVideo();
752 RTCConfiguration callee_config;
Steve Anton80dd7b52018-02-16 17:08:42 -0800753 callee_config.certificates.push_back(callee_certificate);
754 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
755
756 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
757
758 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 10:40:48 -0700759 auto valid_answer = callee->CreateAnswer();
760 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-16 17:08:42 -0800761 auto* audio_content =
762 cricket::GetFirstAudioContent(invalid_answer->description());
763 ASSERT_TRUE(audio_content);
764 auto* audio_transport_info =
765 invalid_answer->description()->GetTransportInfoByName(
766 audio_content->name);
767 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700768 audio_transport_info->description.identity_fingerprint =
769 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800770
771 // Set the invalid answer and expect a fingerprint error.
772 std::string error;
773 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
774 EXPECT_PRED_FORMAT2(AssertStringContains, error,
775 "Local fingerprint does not match identity.");
776
777 // Make sure that setting a valid remote offer or local answer also fails now.
778 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
779 EXPECT_PRED_FORMAT2(AssertStringContains, error,
780 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 10:40:48 -0700781 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-16 17:08:42 -0800782 EXPECT_PRED_FORMAT2(AssertStringContains, error,
783 "Session error code: ERROR_CONTENT.");
784}
785
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100786INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
787 PeerConnectionCryptoTest,
788 Values(SdpSemantics::kPlanB,
789 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 14:59:54 -0800790
Steve Anton6b63cd52017-10-06 11:20:31 -0700791} // namespace webrtc