blob: eb447d68b16016912f95e0fdb7f428500f28ca47 [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
Karl Wiberg1b0eae32017-10-17 14:48:54 +020011#include "api/audio_codecs/builtin_audio_decoder_factory.h"
12#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070013#include "p2p/base/fakeportallocator.h"
14#include "pc/mediasession.h"
15#include "pc/peerconnectionwrapper.h"
16#include "pc/sdputils.h"
17#ifdef WEBRTC_ANDROID
18#include "pc/test/androidtestinitializer.h"
19#endif
20#include "pc/test/fakeaudiocapturemodule.h"
21#include "pc/test/fakertccertificategenerator.h"
22#include "rtc_base/gunit.h"
23#include "rtc_base/ptr_util.h"
24#include "rtc_base/virtualsocketserver.h"
25
26namespace webrtc {
27
28using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 13:08:53 -070029using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 11:20:31 -070030using ::testing::Values;
31using ::testing::Combine;
32
33constexpr int kGenerateCertTimeout = 1000;
34
35class PeerConnectionCryptoUnitTest : public ::testing::Test {
36 protected:
37 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
38
39 PeerConnectionCryptoUnitTest()
40 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
41#ifdef WEBRTC_ANDROID
42 InitializeAndroidObjects();
43#endif
44 pc_factory_ = CreatePeerConnectionFactory(
45 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020046 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
47 CreateBuiltinAudioDecoderFactory(), nullptr, nullptr);
Steve Anton6b63cd52017-10-06 11:20:31 -070048 }
49
Steve Anton8a63f782017-10-23 13:08:53 -070050 WrapperPtr CreatePeerConnection() {
51 return CreatePeerConnection(RTCConfiguration());
52 }
53
Steve Anton6b63cd52017-10-06 11:20:31 -070054 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
55 return CreatePeerConnection(config, nullptr);
56 }
57
58 WrapperPtr CreatePeerConnection(
59 const RTCConfiguration& config,
60 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
61 auto fake_port_allocator = rtc::MakeUnique<cricket::FakePortAllocator>(
62 rtc::Thread::Current(), nullptr);
63 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
64 auto pc = pc_factory_->CreatePeerConnection(
65 config, std::move(fake_port_allocator), std::move(cert_gen),
66 observer.get());
67 if (!pc) {
68 return nullptr;
69 }
70
71 return rtc::MakeUnique<PeerConnectionWrapper>(pc_factory_, pc,
72 std::move(observer));
73 }
74
75 // Accepts the same arguments as CreatePeerConnection and adds default audio
76 // and video tracks.
77 template <typename... Args>
78 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
79 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
80 if (!wrapper) {
81 return nullptr;
82 }
Steve Anton8d3444d2017-10-20 15:30:51 -070083 wrapper->AddAudioTrack("a");
84 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -070085 return wrapper;
86 }
87
Steve Anton8a63f782017-10-23 13:08:53 -070088 cricket::ConnectionRole& AudioConnectionRole(
89 cricket::SessionDescription* desc) {
90 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
91 }
92
93 cricket::ConnectionRole& VideoConnectionRole(
94 cricket::SessionDescription* desc) {
95 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
96 }
97
98 cricket::ConnectionRole& ConnectionRoleFromContent(
99 cricket::SessionDescription* desc,
100 cricket::ContentInfo* content) {
101 RTC_DCHECK(content);
102 auto* transport_info = desc->GetTransportInfoByName(content->name);
103 RTC_DCHECK(transport_info);
104 return transport_info->description.connection_role;
105 }
106
Steve Anton6b63cd52017-10-06 11:20:31 -0700107 std::unique_ptr<rtc::VirtualSocketServer> vss_;
108 rtc::AutoSocketServerThread main_;
109 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
110};
111
112SdpContentPredicate HaveDtlsFingerprint() {
113 return [](const cricket::ContentInfo* content,
114 const cricket::TransportInfo* transport) {
115 return transport->description.identity_fingerprint != nullptr;
116 };
117}
118
119SdpContentPredicate HaveSdesCryptos() {
120 return [](const cricket::ContentInfo* content,
121 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800122 return !content->media_description()->cryptos().empty();
Steve Anton6b63cd52017-10-06 11:20:31 -0700123 };
124}
125
126SdpContentPredicate HaveProtocol(const std::string& protocol) {
127 return [protocol](const cricket::ContentInfo* content,
128 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800129 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700130 };
131}
132
133SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
134 return [num_crypto_suites](const cricket::ContentInfo* content,
135 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800136 const auto& cryptos = content->media_description()->cryptos();
137 if (cryptos.size() != num_crypto_suites) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700138 return false;
139 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800140 const cricket::CryptoParams first_params = cryptos[0];
Steve Anton6b63cd52017-10-06 11:20:31 -0700141 return first_params.key_params.size() == 67U &&
142 first_params.cipher_suite == "AEAD_AES_256_GCM";
143 };
144}
145
146SdpContentMutator RemoveSdesCryptos() {
147 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800148 content->media_description()->set_cryptos({});
Steve Anton6b63cd52017-10-06 11:20:31 -0700149 };
150}
151
152SdpContentMutator RemoveDtlsFingerprint() {
153 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
154 transport->description.identity_fingerprint.reset();
155 };
156}
157
158// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
159// no SDES cryptos.
160TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsEnabled) {
161 RTCConfiguration config;
162 config.enable_dtls_srtp.emplace(true);
163 auto caller = CreatePeerConnectionWithAudioVideo(config);
164
165 auto offer = caller->CreateOffer();
166 ASSERT_TRUE(offer);
167
168 ASSERT_FALSE(offer->description()->contents().empty());
169 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
170 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
171 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
172 offer->description()));
173}
174TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
175 RTCConfiguration config;
176 config.enable_dtls_srtp.emplace(true);
177 auto caller = CreatePeerConnectionWithAudioVideo(config);
178 auto callee = CreatePeerConnectionWithAudioVideo(config);
179
180 callee->SetRemoteDescription(caller->CreateOffer());
181 auto answer = callee->CreateAnswer();
182 ASSERT_TRUE(answer);
183
184 ASSERT_FALSE(answer->description()->contents().empty());
185 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
186 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
187 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
188 answer->description()));
189}
190
191// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
192// should not have a DTLS fingerprint.
193TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsDisabled) {
194 RTCConfiguration config;
195 config.enable_dtls_srtp.emplace(false);
196 auto caller = CreatePeerConnectionWithAudioVideo(config);
197
198 auto offer = caller->CreateOffer();
199 ASSERT_TRUE(offer);
200
201 ASSERT_FALSE(offer->description()->contents().empty());
202 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
203 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
204 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
205 offer->description()));
206}
207TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
208 RTCConfiguration config;
209 config.enable_dtls_srtp.emplace(false);
210 auto caller = CreatePeerConnectionWithAudioVideo(config);
211 auto callee = CreatePeerConnectionWithAudioVideo(config);
212
213 callee->SetRemoteDescription(caller->CreateOffer());
214 auto answer = callee->CreateAnswer();
215 ASSERT_TRUE(answer);
216
217 ASSERT_FALSE(answer->description()->contents().empty());
218 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
219 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
220 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
221 answer->description()));
222}
223
224// When encryption is disabled, the SDP offer/answer should have neither a DTLS
225// fingerprint nor any SDES crypto options.
226TEST_F(PeerConnectionCryptoUnitTest,
227 CorrectCryptoInOfferWhenEncryptionDisabled) {
228 PeerConnectionFactoryInterface::Options options;
229 options.disable_encryption = true;
230 pc_factory_->SetOptions(options);
231
232 RTCConfiguration config;
233 config.enable_dtls_srtp.emplace(false);
234 auto caller = CreatePeerConnectionWithAudioVideo(config);
235
236 auto offer = caller->CreateOffer();
237 ASSERT_TRUE(offer);
238
239 ASSERT_FALSE(offer->description()->contents().empty());
240 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
241 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
242 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
243 offer->description()));
244}
245TEST_F(PeerConnectionCryptoUnitTest,
246 CorrectCryptoInAnswerWhenEncryptionDisabled) {
247 PeerConnectionFactoryInterface::Options options;
248 options.disable_encryption = true;
249 pc_factory_->SetOptions(options);
250
251 RTCConfiguration config;
252 config.enable_dtls_srtp.emplace(false);
253 auto caller = CreatePeerConnectionWithAudioVideo(config);
254 auto callee = CreatePeerConnectionWithAudioVideo(config);
255
256 callee->SetRemoteDescription(caller->CreateOffer());
257 auto answer = callee->CreateAnswer();
258 ASSERT_TRUE(answer);
259
260 ASSERT_FALSE(answer->description()->contents().empty());
261 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
262 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
263 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
264 answer->description()));
265}
266
267// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
268// should have the correct ciphers in the SDES crypto options.
269// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
270// in the answer.
271TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWithSdesAndGcm) {
272 PeerConnectionFactoryInterface::Options options;
273 options.crypto_options.enable_gcm_crypto_suites = true;
274 pc_factory_->SetOptions(options);
275
276 RTCConfiguration config;
277 config.enable_dtls_srtp.emplace(false);
278 auto caller = CreatePeerConnectionWithAudioVideo(config);
279
280 auto offer = caller->CreateOffer();
281 ASSERT_TRUE(offer);
282
283 ASSERT_FALSE(offer->description()->contents().empty());
284 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
285}
286TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWithSdesAndGcm) {
287 PeerConnectionFactoryInterface::Options options;
288 options.crypto_options.enable_gcm_crypto_suites = true;
289 pc_factory_->SetOptions(options);
290
291 RTCConfiguration config;
292 config.enable_dtls_srtp.emplace(false);
293 auto caller = CreatePeerConnectionWithAudioVideo(config);
294 auto callee = CreatePeerConnectionWithAudioVideo(config);
295
296 callee->SetRemoteDescription(caller->CreateOffer());
297 auto answer = callee->CreateAnswer();
298 ASSERT_TRUE(answer);
299
300 ASSERT_FALSE(answer->description()->contents().empty());
301 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
302}
303
304TEST_F(PeerConnectionCryptoUnitTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
305 PeerConnectionFactoryInterface::Options options;
306 options.crypto_options.enable_gcm_crypto_suites = true;
307 pc_factory_->SetOptions(options);
308
309 RTCConfiguration config;
310 config.enable_dtls_srtp.emplace(false);
311 auto caller = CreatePeerConnectionWithAudioVideo(config);
312 auto callee = CreatePeerConnectionWithAudioVideo(config);
313
314 auto offer = caller->CreateOffer();
315 ASSERT_TRUE(offer);
316 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
317
318 auto answer = callee->CreateAnswer();
319 ASSERT_TRUE(answer);
320 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
321}
322
323// The following group tests that two PeerConnections can successfully exchange
324// an offer/answer when DTLS is off and that they will refuse any offer/answer
325// applied locally/remotely if it does not include SDES cryptos.
326TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenSdesOn) {
327 RTCConfiguration config;
328 config.enable_dtls_srtp.emplace(false);
329 auto caller = CreatePeerConnectionWithAudioVideo(config);
330 auto callee = CreatePeerConnectionWithAudioVideo(config);
331
332 auto offer = caller->CreateOfferAndSetAsLocal();
333 ASSERT_TRUE(offer);
334 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
335
336 auto answer = callee->CreateAnswerAndSetAsLocal();
337 ASSERT_TRUE(answer);
338 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
339}
340TEST_F(PeerConnectionCryptoUnitTest,
341 FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
342 RTCConfiguration config;
343 config.enable_dtls_srtp.emplace(false);
344 auto caller = CreatePeerConnectionWithAudioVideo(config);
345
346 auto offer = caller->CreateOffer();
347 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
348
349 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
350}
351TEST_F(PeerConnectionCryptoUnitTest,
352 FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
353 RTCConfiguration config;
354 config.enable_dtls_srtp.emplace(false);
355 auto caller = CreatePeerConnectionWithAudioVideo(config);
356 auto callee = CreatePeerConnectionWithAudioVideo(config);
357
358 auto offer = caller->CreateOffer();
359 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
360
361 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
362}
363TEST_F(PeerConnectionCryptoUnitTest,
364 FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
365 RTCConfiguration config;
366 config.enable_dtls_srtp.emplace(false);
367 auto caller = CreatePeerConnectionWithAudioVideo(config);
368 auto callee = CreatePeerConnectionWithAudioVideo(config);
369
370 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
371 auto answer = callee->CreateAnswer();
372 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
373
374 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
375}
376TEST_F(PeerConnectionCryptoUnitTest,
377 FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
378 RTCConfiguration config;
379 config.enable_dtls_srtp.emplace(false);
380 auto caller = CreatePeerConnectionWithAudioVideo(config);
381 auto callee = CreatePeerConnectionWithAudioVideo(config);
382
383 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
384 auto answer = callee->CreateAnswerAndSetAsLocal();
385 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
386
387 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
388}
389
390// The following group tests that two PeerConnections can successfully exchange
391// an offer/answer when DTLS is on and that they will refuse any offer/answer
392// applied locally/remotely if it does not include a DTLS fingerprint.
393TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenDtlsOn) {
394 RTCConfiguration config;
395 config.enable_dtls_srtp.emplace(true);
396 auto caller = CreatePeerConnectionWithAudioVideo(config);
397 auto callee = CreatePeerConnectionWithAudioVideo(config);
398
399 auto offer = caller->CreateOfferAndSetAsLocal();
400 ASSERT_TRUE(offer);
401 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
402
403 auto answer = callee->CreateAnswerAndSetAsLocal();
404 ASSERT_TRUE(answer);
405 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
406}
407TEST_F(PeerConnectionCryptoUnitTest,
408 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
409 RTCConfiguration config;
410 config.enable_dtls_srtp.emplace(true);
411 auto caller = CreatePeerConnectionWithAudioVideo(config);
412
413 auto offer = caller->CreateOffer();
414 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
415
416 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
417}
418TEST_F(PeerConnectionCryptoUnitTest,
419 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
420 RTCConfiguration config;
421 config.enable_dtls_srtp.emplace(true);
422 auto caller = CreatePeerConnectionWithAudioVideo(config);
423 auto callee = CreatePeerConnectionWithAudioVideo(config);
424
425 auto offer = caller->CreateOffer();
426 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
427
428 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
429}
430TEST_F(PeerConnectionCryptoUnitTest,
431 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
432 RTCConfiguration config;
433 config.enable_dtls_srtp.emplace(true);
434 auto caller = CreatePeerConnectionWithAudioVideo(config);
435 auto callee = CreatePeerConnectionWithAudioVideo(config);
436
437 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
438 auto answer = callee->CreateAnswer();
439 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
440}
441TEST_F(PeerConnectionCryptoUnitTest,
442 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
443 RTCConfiguration config;
444 config.enable_dtls_srtp.emplace(true);
445 auto caller = CreatePeerConnectionWithAudioVideo(config);
446 auto callee = CreatePeerConnectionWithAudioVideo(config);
447
448 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
449 auto answer = callee->CreateAnswerAndSetAsLocal();
450 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
451
452 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
453}
454
455// Test that an offer/answer can be exchanged when encryption is disabled.
456TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenNoEncryption) {
457 PeerConnectionFactoryInterface::Options options;
458 options.disable_encryption = true;
459 pc_factory_->SetOptions(options);
460
461 RTCConfiguration config;
462 config.enable_dtls_srtp.emplace(false);
463 auto caller = CreatePeerConnectionWithAudioVideo(config);
464 auto callee = CreatePeerConnectionWithAudioVideo(config);
465
466 auto offer = caller->CreateOfferAndSetAsLocal();
467 ASSERT_TRUE(offer);
468 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
469
470 auto answer = callee->CreateAnswerAndSetAsLocal();
471 ASSERT_TRUE(answer);
472 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
473}
474
475// Tests that a DTLS call can be established when the certificate is specified
476// in the PeerConnection config and no certificate generator is specified.
477TEST_F(PeerConnectionCryptoUnitTest,
478 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
479 RTCConfiguration caller_config;
480 caller_config.enable_dtls_srtp.emplace(true);
481 caller_config.certificates.push_back(
482 FakeRTCCertificateGenerator::GenerateCertificate());
483 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
484
485 RTCConfiguration callee_config;
486 callee_config.enable_dtls_srtp.emplace(true);
487 callee_config.certificates.push_back(
488 FakeRTCCertificateGenerator::GenerateCertificate());
489 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
490
491 auto offer = caller->CreateOfferAndSetAsLocal();
492 ASSERT_TRUE(offer);
493 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
494
495 auto answer = callee->CreateAnswerAndSetAsLocal();
496 ASSERT_TRUE(answer);
497 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
498}
499
500// The following parameterized test verifies that CreateOffer/CreateAnswer
501// returns successfully (or with failure if the underlying certificate generator
502// fails) no matter when the DTLS certificate is generated. If multiple
503// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
504// they all finish after the certificate is generated.
505
Steve Anton6b63cd52017-10-06 11:20:31 -0700506// Whether the certificate will be generated before calling CreateOffer or
507// while CreateOffer is executing.
508enum class CertGenTime { kBefore, kDuring };
509std::ostream& operator<<(std::ostream& out, CertGenTime value) {
510 switch (value) {
511 case CertGenTime::kBefore:
512 return out << "before";
513 case CertGenTime::kDuring:
514 return out << "during";
515 default:
516 return out << "unknown";
517 }
518}
519
520// Whether the fake certificate generator will produce a certificate or fail.
521enum class CertGenResult { kSucceed, kFail };
522std::ostream& operator<<(std::ostream& out, CertGenResult value) {
523 switch (value) {
524 case CertGenResult::kSucceed:
525 return out << "succeed";
526 case CertGenResult::kFail:
527 return out << "fail";
528 default:
529 return out << "unknown";
530 }
531}
532
533class PeerConnectionCryptoDtlsCertGenUnitTest
534 : public PeerConnectionCryptoUnitTest,
535 public ::testing::WithParamInterface<
536 ::testing::tuple<SdpType, CertGenTime, CertGenResult, size_t>> {
537 protected:
538 PeerConnectionCryptoDtlsCertGenUnitTest() {
539 sdp_type_ = ::testing::get<0>(GetParam());
540 cert_gen_time_ = ::testing::get<1>(GetParam());
541 cert_gen_result_ = ::testing::get<2>(GetParam());
542 concurrent_calls_ = ::testing::get<3>(GetParam());
543 }
544
545 SdpType sdp_type_;
546 CertGenTime cert_gen_time_;
547 CertGenResult cert_gen_result_;
548 size_t concurrent_calls_;
549};
550
551TEST_P(PeerConnectionCryptoDtlsCertGenUnitTest, TestCertificateGeneration) {
552 RTCConfiguration config;
553 config.enable_dtls_srtp.emplace(true);
554 auto owned_fake_certificate_generator =
555 rtc::MakeUnique<FakeRTCCertificateGenerator>();
556 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
557 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
558 CertGenResult::kFail);
559 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
560 CertGenTime::kDuring);
561 WrapperPtr pc;
562 if (sdp_type_ == SdpType::kOffer) {
563 pc = CreatePeerConnectionWithAudioVideo(
564 config, std::move(owned_fake_certificate_generator));
565 } else {
566 auto caller = CreatePeerConnectionWithAudioVideo(config);
567 pc = CreatePeerConnectionWithAudioVideo(
568 config, std::move(owned_fake_certificate_generator));
569 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
570 }
571 if (cert_gen_time_ == CertGenTime::kBefore) {
572 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
573 fake_certificate_generator->generated_failures() >
574 0,
575 kGenerateCertTimeout);
576 } else {
577 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
578 fake_certificate_generator->set_should_wait(false);
579 }
580 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
581 observers;
582 for (size_t i = 0; i < concurrent_calls_; i++) {
583 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
584 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
585 observers.push_back(observer);
586 if (sdp_type_ == SdpType::kOffer) {
587 pc->pc()->CreateOffer(observer, nullptr);
588 } else {
589 pc->pc()->CreateAnswer(observer, nullptr);
590 }
591 }
592 for (auto& observer : observers) {
593 EXPECT_TRUE_WAIT(observer->called(), 1000);
594 if (cert_gen_result_ == CertGenResult::kSucceed) {
595 EXPECT_TRUE(observer->result());
596 } else {
597 EXPECT_FALSE(observer->result());
598 }
599 }
600}
601
602INSTANTIATE_TEST_CASE_P(
603 PeerConnectionCryptoUnitTest,
604 PeerConnectionCryptoDtlsCertGenUnitTest,
605 Combine(Values(SdpType::kOffer, SdpType::kAnswer),
606 Values(CertGenTime::kBefore, CertGenTime::kDuring),
607 Values(CertGenResult::kSucceed, CertGenResult::kFail),
608 Values(1, 3)));
609
Steve Anton8a63f782017-10-23 13:08:53 -0700610// Test that we can create and set an answer correctly when different
611// SSL roles have been negotiated for different transports.
612// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
613TEST_F(PeerConnectionCryptoUnitTest, CreateAnswerWithDifferentSslRoles) {
614 auto caller = CreatePeerConnectionWithAudioVideo();
615 auto callee = CreatePeerConnectionWithAudioVideo();
616
617 RTCOfferAnswerOptions options_no_bundle;
618 options_no_bundle.use_rtp_mux = false;
619
620 // First, negotiate different SSL roles for audio and video.
621 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
622 auto answer = callee->CreateAnswer(options_no_bundle);
623
624 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
625 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
626
627 ASSERT_TRUE(
628 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
629 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
630
631 // Now create an offer in the reverse direction, and ensure the initial
632 // offerer responds with an answer with the correct SSL roles.
633 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
634 answer = caller->CreateAnswer(options_no_bundle);
635
636 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
637 AudioConnectionRole(answer->description()));
638 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
639 VideoConnectionRole(answer->description()));
640
641 ASSERT_TRUE(
642 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
643 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
644
645 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
646 // audio is transferred over to video in the answer that completes the BUNDLE
647 // negotiation.
648 RTCOfferAnswerOptions options_bundle;
649 options_bundle.use_rtp_mux = true;
650
651 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
652 answer = caller->CreateAnswer(options_bundle);
653
654 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
655 AudioConnectionRole(answer->description()));
656 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
657 VideoConnectionRole(answer->description()));
658
659 ASSERT_TRUE(
660 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
661 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
662}
663
Steve Anton6b63cd52017-10-06 11:20:31 -0700664} // namespace webrtc