1 /*
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
11 #include <stddef.h>
12
13 #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"
23 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
24 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
25 #include "api/create_peerconnection_factory.h"
26 #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"
31 #include "api/video_codecs/builtin_video_decoder_factory.h"
32 #include "api/video_codecs/builtin_video_encoder_factory.h"
33 #include "modules/audio_device/include/audio_device.h"
34 #include "modules/audio_processing/include/audio_processing.h"
35 #include "p2p/base/fake_port_allocator.h"
36 #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"
40 #include "pc/media_session.h"
41 #include "pc/peer_connection_wrapper.h"
42 #include "pc/sdp_utils.h"
43 #include "pc/session_description.h"
44 #include "pc/test/mock_peer_connection_observers.h"
45 #include "rtc_base/checks.h"
46 #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"
51 #ifdef WEBRTC_ANDROID
52 #include "pc/test/android_test_initializer.h"
53 #endif
54 #include "pc/test/fake_audio_capture_module.h"
55 #include "pc/test/fake_rtc_certificate_generator.h"
56 #include "rtc_base/gunit.h"
57 #include "rtc_base/virtual_socket_server.h"
58
59 namespace webrtc {
60
61 using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
62 using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
63 using ::testing::Combine;
64 using ::testing::Values;
65
66 constexpr int kGenerateCertTimeout = 1000;
67
68 class PeerConnectionCryptoBaseTest : public ::testing::Test {
69 protected:
70 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
71
PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)72 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
73 : vss_(new rtc::VirtualSocketServer()),
74 main_(vss_.get()),
75 sdp_semantics_(sdp_semantics) {
76 #ifdef WEBRTC_ANDROID
77 InitializeAndroidObjects();
78 #endif
79 pc_factory_ = CreatePeerConnectionFactory(
80 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
81 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
82 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
83 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
84 nullptr /* audio_processing */);
85 }
86
CreatePeerConnection()87 WrapperPtr CreatePeerConnection() {
88 return CreatePeerConnection(RTCConfiguration());
89 }
90
CreatePeerConnection(const RTCConfiguration & config)91 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
92 return CreatePeerConnection(config, nullptr);
93 }
94
CreatePeerConnection(const RTCConfiguration & config,std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen)95 WrapperPtr CreatePeerConnection(
96 const RTCConfiguration& config,
97 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
98 auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
99 rtc::Thread::Current(),
100 std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get()));
101 auto observer = std::make_unique<MockPeerConnectionObserver>();
102 RTCConfiguration modified_config = config;
103 modified_config.sdp_semantics = sdp_semantics_;
104 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()) {
110 return nullptr;
111 }
112
113 observer->SetPeerConnectionInterface(result.value().get());
114 return std::make_unique<PeerConnectionWrapper>(
115 pc_factory_, result.MoveValue(), std::move(observer));
116 }
117
118 // Accepts the same arguments as CreatePeerConnection and adds default audio
119 // and video tracks.
120 template <typename... Args>
CreatePeerConnectionWithAudioVideo(Args &&...args)121 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
122 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
123 if (!wrapper) {
124 return nullptr;
125 }
126 wrapper->AddAudioTrack("a");
127 wrapper->AddVideoTrack("v");
128 return wrapper;
129 }
130
AudioConnectionRole(cricket::SessionDescription * desc)131 cricket::ConnectionRole& AudioConnectionRole(
132 cricket::SessionDescription* desc) {
133 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
134 }
135
VideoConnectionRole(cricket::SessionDescription * desc)136 cricket::ConnectionRole& VideoConnectionRole(
137 cricket::SessionDescription* desc) {
138 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
139 }
140
ConnectionRoleFromContent(cricket::SessionDescription * desc,cricket::ContentInfo * content)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
150 std::unique_ptr<rtc::VirtualSocketServer> vss_;
151 rtc::AutoSocketServerThread main_;
152 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
153 const SdpSemantics sdp_semantics_;
154 };
155
HaveDtlsFingerprint()156 SdpContentPredicate HaveDtlsFingerprint() {
157 return [](const cricket::ContentInfo* content,
158 const cricket::TransportInfo* transport) {
159 return transport->description.identity_fingerprint != nullptr;
160 };
161 }
162
HaveSdesCryptos()163 SdpContentPredicate HaveSdesCryptos() {
164 return [](const cricket::ContentInfo* content,
165 const cricket::TransportInfo* transport) {
166 return !content->media_description()->cryptos().empty();
167 };
168 }
169
HaveProtocol(const std::string & protocol)170 SdpContentPredicate HaveProtocol(const std::string& protocol) {
171 return [protocol](const cricket::ContentInfo* content,
172 const cricket::TransportInfo* transport) {
173 return content->media_description()->protocol() == protocol;
174 };
175 }
176
HaveSdesGcmCryptos(size_t num_crypto_suites)177 SdpContentPredicate 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
193 class PeerConnectionCryptoTest
194 : public PeerConnectionCryptoBaseTest,
195 public ::testing::WithParamInterface<SdpSemantics> {
196 protected:
PeerConnectionCryptoTest()197 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
198 };
199
RemoveSdesCryptos()200 SdpContentMutator RemoveSdesCryptos() {
201 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
202 content->media_description()->set_cryptos({});
203 };
204 }
205
RemoveDtlsFingerprint()206 SdpContentMutator RemoveDtlsFingerprint() {
207 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
208 transport->description.identity_fingerprint.reset();
209 };
210 }
211
212 // When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
213 // no SDES cryptos.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenDtlsEnabled)214 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
215 RTCConfiguration config;
216 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()));
223 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
224 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
225 offer->description()));
226 }
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenDtlsEnabled)227 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
228 RTCConfiguration config;
229 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()));
238 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
239 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
240 answer->description()));
241 }
242
243 #if defined(WEBRTC_FUCHSIA)
244 // When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
245 // should not have a DTLS fingerprint.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenDtlsDisabled)246 TEST_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 }
260
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenDtlsDisabled)261 TEST_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
278 // When encryption is disabled, the SDP offer/answer should have neither a DTLS
279 // fingerprint nor any SDES crypto options.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenEncryptionDisabled)280 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
281 PeerConnectionFactoryInterface::Options options;
282 options.disable_encryption = true;
283 pc_factory_->SetOptions(options);
284
285 RTCConfiguration config;
286 config.enable_dtls_srtp.emplace(false);
287 auto caller = CreatePeerConnectionWithAudioVideo(config);
288
289 auto offer = caller->CreateOffer();
290 ASSERT_TRUE(offer);
291
292 ASSERT_FALSE(offer->description()->contents().empty());
293 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
294 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
295 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
296 offer->description()));
297 }
298
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenEncryptionDisabled)299 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
300 PeerConnectionFactoryInterface::Options options;
301 options.disable_encryption = true;
302 pc_factory_->SetOptions(options);
303
304 RTCConfiguration config;
305 config.enable_dtls_srtp.emplace(false);
306 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());
314 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
315 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
316 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
317 answer->description()));
318 }
319
320 // 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.
TEST_P(PeerConnectionCryptoTest,RTCConfigurationCryptoOptionOverridesFactory)322 TEST_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.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWithSdesAndGcm)346 TEST_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
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWithSdesAndGcm)362 TEST_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
TEST_P(PeerConnectionCryptoTest,CanSetSdesGcmRemoteOfferAndLocalAnswer)387 TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
388 PeerConnectionFactoryInterface::Options options;
389 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
390 pc_factory_->SetOptions(options);
391
392 RTCConfiguration config;
393 config.enable_dtls_srtp.emplace(false);
394 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)));
404 }
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.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenSdesOn)409 TEST_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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalOfferWithNoCryptosWhenSdesOn)423 TEST_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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteOfferWithNoCryptosWhenSdesOn)433 TEST_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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalAnswerWithNoCryptosWhenSdesOn)444 TEST_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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteAnswerWithNoCryptosWhenSdesOn)456 TEST_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)));
467 }
468 #endif
469
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.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenDtlsOn)473 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
474 RTCConfiguration config;
475 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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalOfferWithNoFingerprintWhenDtlsOn)486 TEST_P(PeerConnectionCryptoTest,
487 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
488 RTCConfiguration config;
489 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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn)496 TEST_P(PeerConnectionCryptoTest,
497 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
498 RTCConfiguration config;
499 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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn)507 TEST_P(PeerConnectionCryptoTest,
508 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
509 RTCConfiguration config;
510 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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn)517 TEST_P(PeerConnectionCryptoTest,
518 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
519 RTCConfiguration config;
520 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
530 #if defined(WEBRTC_FUCHSIA)
531 // Test that an offer/answer can be exchanged when encryption is disabled.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenNoEncryption)532 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
533 PeerConnectionFactoryInterface::Options options;
534 options.disable_encryption = true;
535 pc_factory_->SetOptions(options);
536
537 RTCConfiguration config;
538 config.enable_dtls_srtp.emplace(false);
539 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 }
550 #endif
551
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.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenDtlsCertificateInConfig)554 TEST_P(PeerConnectionCryptoTest,
555 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
556 RTCConfiguration caller_config;
557 caller_config.certificates.push_back(
558 FakeRTCCertificateGenerator::GenerateCertificate());
559 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
560
561 RTCConfiguration callee_config;
562 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
581 // Whether the certificate will be generated before calling CreateOffer or
582 // while CreateOffer is executing.
583 enum class CertGenTime { kBefore, kDuring };
operator <<(std::ostream & out,CertGenTime value)584 std::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.
596 enum class CertGenResult { kSucceed, kFail };
operator <<(std::ostream & out,CertGenResult value)597 std::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
608 class PeerConnectionCryptoDtlsCertGenTest
609 : public PeerConnectionCryptoBaseTest,
610 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
611 SdpType,
612 CertGenTime,
613 CertGenResult,
614 size_t>> {
615 protected:
PeerConnectionCryptoDtlsCertGenTest()616 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());
622 }
623
624 SdpType sdp_type_;
625 CertGenTime cert_gen_time_;
626 CertGenResult cert_gen_result_;
627 size_t concurrent_calls_;
628 };
629
TEST_P(PeerConnectionCryptoDtlsCertGenTest,TestCertificateGeneration)630 TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
631 RTCConfiguration config;
632 auto owned_fake_certificate_generator =
633 std::make_unique<FakeRTCCertificateGenerator>();
634 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 =
662 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
663 observers.push_back(observer);
664 if (sdp_type_ == SdpType::kOffer) {
665 pc->pc()->CreateOffer(observer.get(),
666 PeerConnectionInterface::RTCOfferAnswerOptions());
667 } else {
668 pc->pc()->CreateAnswer(observer.get(),
669 PeerConnectionInterface::RTCOfferAnswerOptions());
670 }
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
682 INSTANTIATE_TEST_SUITE_P(
683 PeerConnectionCryptoTest,
684 PeerConnectionCryptoDtlsCertGenTest,
685 Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
686 Values(SdpType::kOffer, SdpType::kAnswer),
687 Values(CertGenTime::kBefore, CertGenTime::kDuring),
688 Values(CertGenResult::kSucceed, CertGenResult::kFail),
689 Values(1, 3)));
690
691 // 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
TEST_P(PeerConnectionCryptoTest,CreateAnswerWithDifferentSslRoles)694 TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
695 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
745 // 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
TEST_P(PeerConnectionCryptoTest,SessionErrorIfFingerprintInvalid)749 TEST_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;
755 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.
761 auto valid_answer = callee->CreateAnswer();
762 auto invalid_answer = CloneSessionDescription(valid_answer.get());
763 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);
770 audio_transport_info->description.identity_fingerprint =
771 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
772
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.");
783 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
784 EXPECT_PRED_FORMAT2(AssertStringContains, error,
785 "Session error code: ERROR_CONTENT.");
786 }
787
788 INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
789 PeerConnectionCryptoTest,
790 Values(SdpSemantics::kPlanB_DEPRECATED,
791 SdpSemantics::kUnifiedPlan));
792
793 } // namespace webrtc
794