blob: 5d5296bcb4b51158a85654ce8dfe90370eeb4629 [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) {
122 const auto* media_desc =
123 static_cast<const cricket::MediaContentDescription*>(
124 content->description);
125 return !media_desc->cryptos().empty();
126 };
127}
128
129SdpContentPredicate HaveProtocol(const std::string& protocol) {
130 return [protocol](const cricket::ContentInfo* content,
131 const cricket::TransportInfo* transport) {
132 const auto* media_desc =
133 static_cast<const cricket::MediaContentDescription*>(
134 content->description);
135 return media_desc->protocol() == protocol;
136 };
137}
138
139SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
140 return [num_crypto_suites](const cricket::ContentInfo* content,
141 const cricket::TransportInfo* transport) {
142 const auto* media_desc =
143 static_cast<const cricket::MediaContentDescription*>(
144 content->description);
145 if (media_desc->cryptos().size() != num_crypto_suites) {
146 return false;
147 }
148 const cricket::CryptoParams first_params = media_desc->cryptos()[0];
149 return first_params.key_params.size() == 67U &&
150 first_params.cipher_suite == "AEAD_AES_256_GCM";
151 };
152}
153
154SdpContentMutator RemoveSdesCryptos() {
155 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
156 auto* media_desc =
157 static_cast<cricket::MediaContentDescription*>(content->description);
158 media_desc->set_cryptos({});
159 };
160}
161
162SdpContentMutator RemoveDtlsFingerprint() {
163 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
164 transport->description.identity_fingerprint.reset();
165 };
166}
167
168// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
169// no SDES cryptos.
170TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsEnabled) {
171 RTCConfiguration config;
172 config.enable_dtls_srtp.emplace(true);
173 auto caller = CreatePeerConnectionWithAudioVideo(config);
174
175 auto offer = caller->CreateOffer();
176 ASSERT_TRUE(offer);
177
178 ASSERT_FALSE(offer->description()->contents().empty());
179 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
180 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
181 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
182 offer->description()));
183}
184TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
185 RTCConfiguration config;
186 config.enable_dtls_srtp.emplace(true);
187 auto caller = CreatePeerConnectionWithAudioVideo(config);
188 auto callee = CreatePeerConnectionWithAudioVideo(config);
189
190 callee->SetRemoteDescription(caller->CreateOffer());
191 auto answer = callee->CreateAnswer();
192 ASSERT_TRUE(answer);
193
194 ASSERT_FALSE(answer->description()->contents().empty());
195 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
196 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
197 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
198 answer->description()));
199}
200
201// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
202// should not have a DTLS fingerprint.
203TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsDisabled) {
204 RTCConfiguration config;
205 config.enable_dtls_srtp.emplace(false);
206 auto caller = CreatePeerConnectionWithAudioVideo(config);
207
208 auto offer = caller->CreateOffer();
209 ASSERT_TRUE(offer);
210
211 ASSERT_FALSE(offer->description()->contents().empty());
212 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
213 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
214 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
215 offer->description()));
216}
217TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
218 RTCConfiguration config;
219 config.enable_dtls_srtp.emplace(false);
220 auto caller = CreatePeerConnectionWithAudioVideo(config);
221 auto callee = CreatePeerConnectionWithAudioVideo(config);
222
223 callee->SetRemoteDescription(caller->CreateOffer());
224 auto answer = callee->CreateAnswer();
225 ASSERT_TRUE(answer);
226
227 ASSERT_FALSE(answer->description()->contents().empty());
228 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
229 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
230 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
231 answer->description()));
232}
233
234// When encryption is disabled, the SDP offer/answer should have neither a DTLS
235// fingerprint nor any SDES crypto options.
236TEST_F(PeerConnectionCryptoUnitTest,
237 CorrectCryptoInOfferWhenEncryptionDisabled) {
238 PeerConnectionFactoryInterface::Options options;
239 options.disable_encryption = true;
240 pc_factory_->SetOptions(options);
241
242 RTCConfiguration config;
243 config.enable_dtls_srtp.emplace(false);
244 auto caller = CreatePeerConnectionWithAudioVideo(config);
245
246 auto offer = caller->CreateOffer();
247 ASSERT_TRUE(offer);
248
249 ASSERT_FALSE(offer->description()->contents().empty());
250 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
251 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
252 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
253 offer->description()));
254}
255TEST_F(PeerConnectionCryptoUnitTest,
256 CorrectCryptoInAnswerWhenEncryptionDisabled) {
257 PeerConnectionFactoryInterface::Options options;
258 options.disable_encryption = true;
259 pc_factory_->SetOptions(options);
260
261 RTCConfiguration config;
262 config.enable_dtls_srtp.emplace(false);
263 auto caller = CreatePeerConnectionWithAudioVideo(config);
264 auto callee = CreatePeerConnectionWithAudioVideo(config);
265
266 callee->SetRemoteDescription(caller->CreateOffer());
267 auto answer = callee->CreateAnswer();
268 ASSERT_TRUE(answer);
269
270 ASSERT_FALSE(answer->description()->contents().empty());
271 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
272 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
273 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
274 answer->description()));
275}
276
277// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
278// should have the correct ciphers in the SDES crypto options.
279// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
280// in the answer.
281TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWithSdesAndGcm) {
282 PeerConnectionFactoryInterface::Options options;
283 options.crypto_options.enable_gcm_crypto_suites = true;
284 pc_factory_->SetOptions(options);
285
286 RTCConfiguration config;
287 config.enable_dtls_srtp.emplace(false);
288 auto caller = CreatePeerConnectionWithAudioVideo(config);
289
290 auto offer = caller->CreateOffer();
291 ASSERT_TRUE(offer);
292
293 ASSERT_FALSE(offer->description()->contents().empty());
294 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
295}
296TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWithSdesAndGcm) {
297 PeerConnectionFactoryInterface::Options options;
298 options.crypto_options.enable_gcm_crypto_suites = true;
299 pc_factory_->SetOptions(options);
300
301 RTCConfiguration config;
302 config.enable_dtls_srtp.emplace(false);
303 auto caller = CreatePeerConnectionWithAudioVideo(config);
304 auto callee = CreatePeerConnectionWithAudioVideo(config);
305
306 callee->SetRemoteDescription(caller->CreateOffer());
307 auto answer = callee->CreateAnswer();
308 ASSERT_TRUE(answer);
309
310 ASSERT_FALSE(answer->description()->contents().empty());
311 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
312}
313
314TEST_F(PeerConnectionCryptoUnitTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
315 PeerConnectionFactoryInterface::Options options;
316 options.crypto_options.enable_gcm_crypto_suites = true;
317 pc_factory_->SetOptions(options);
318
319 RTCConfiguration config;
320 config.enable_dtls_srtp.emplace(false);
321 auto caller = CreatePeerConnectionWithAudioVideo(config);
322 auto callee = CreatePeerConnectionWithAudioVideo(config);
323
324 auto offer = caller->CreateOffer();
325 ASSERT_TRUE(offer);
326 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
327
328 auto answer = callee->CreateAnswer();
329 ASSERT_TRUE(answer);
330 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
331}
332
333// The following group tests that two PeerConnections can successfully exchange
334// an offer/answer when DTLS is off and that they will refuse any offer/answer
335// applied locally/remotely if it does not include SDES cryptos.
336TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenSdesOn) {
337 RTCConfiguration config;
338 config.enable_dtls_srtp.emplace(false);
339 auto caller = CreatePeerConnectionWithAudioVideo(config);
340 auto callee = CreatePeerConnectionWithAudioVideo(config);
341
342 auto offer = caller->CreateOfferAndSetAsLocal();
343 ASSERT_TRUE(offer);
344 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
345
346 auto answer = callee->CreateAnswerAndSetAsLocal();
347 ASSERT_TRUE(answer);
348 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
349}
350TEST_F(PeerConnectionCryptoUnitTest,
351 FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
352 RTCConfiguration config;
353 config.enable_dtls_srtp.emplace(false);
354 auto caller = CreatePeerConnectionWithAudioVideo(config);
355
356 auto offer = caller->CreateOffer();
357 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
358
359 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
360}
361TEST_F(PeerConnectionCryptoUnitTest,
362 FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
363 RTCConfiguration config;
364 config.enable_dtls_srtp.emplace(false);
365 auto caller = CreatePeerConnectionWithAudioVideo(config);
366 auto callee = CreatePeerConnectionWithAudioVideo(config);
367
368 auto offer = caller->CreateOffer();
369 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
370
371 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
372}
373TEST_F(PeerConnectionCryptoUnitTest,
374 FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
375 RTCConfiguration config;
376 config.enable_dtls_srtp.emplace(false);
377 auto caller = CreatePeerConnectionWithAudioVideo(config);
378 auto callee = CreatePeerConnectionWithAudioVideo(config);
379
380 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
381 auto answer = callee->CreateAnswer();
382 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
383
384 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
385}
386TEST_F(PeerConnectionCryptoUnitTest,
387 FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
388 RTCConfiguration config;
389 config.enable_dtls_srtp.emplace(false);
390 auto caller = CreatePeerConnectionWithAudioVideo(config);
391 auto callee = CreatePeerConnectionWithAudioVideo(config);
392
393 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
394 auto answer = callee->CreateAnswerAndSetAsLocal();
395 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
396
397 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
398}
399
400// The following group tests that two PeerConnections can successfully exchange
401// an offer/answer when DTLS is on and that they will refuse any offer/answer
402// applied locally/remotely if it does not include a DTLS fingerprint.
403TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenDtlsOn) {
404 RTCConfiguration config;
405 config.enable_dtls_srtp.emplace(true);
406 auto caller = CreatePeerConnectionWithAudioVideo(config);
407 auto callee = CreatePeerConnectionWithAudioVideo(config);
408
409 auto offer = caller->CreateOfferAndSetAsLocal();
410 ASSERT_TRUE(offer);
411 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
412
413 auto answer = callee->CreateAnswerAndSetAsLocal();
414 ASSERT_TRUE(answer);
415 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
416}
417TEST_F(PeerConnectionCryptoUnitTest,
418 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
419 RTCConfiguration config;
420 config.enable_dtls_srtp.emplace(true);
421 auto caller = CreatePeerConnectionWithAudioVideo(config);
422
423 auto offer = caller->CreateOffer();
424 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
425
426 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
427}
428TEST_F(PeerConnectionCryptoUnitTest,
429 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
430 RTCConfiguration config;
431 config.enable_dtls_srtp.emplace(true);
432 auto caller = CreatePeerConnectionWithAudioVideo(config);
433 auto callee = CreatePeerConnectionWithAudioVideo(config);
434
435 auto offer = caller->CreateOffer();
436 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
437
438 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
439}
440TEST_F(PeerConnectionCryptoUnitTest,
441 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
442 RTCConfiguration config;
443 config.enable_dtls_srtp.emplace(true);
444 auto caller = CreatePeerConnectionWithAudioVideo(config);
445 auto callee = CreatePeerConnectionWithAudioVideo(config);
446
447 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
448 auto answer = callee->CreateAnswer();
449 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
450}
451TEST_F(PeerConnectionCryptoUnitTest,
452 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
453 RTCConfiguration config;
454 config.enable_dtls_srtp.emplace(true);
455 auto caller = CreatePeerConnectionWithAudioVideo(config);
456 auto callee = CreatePeerConnectionWithAudioVideo(config);
457
458 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
459 auto answer = callee->CreateAnswerAndSetAsLocal();
460 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
461
462 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
463}
464
465// Test that an offer/answer can be exchanged when encryption is disabled.
466TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenNoEncryption) {
467 PeerConnectionFactoryInterface::Options options;
468 options.disable_encryption = true;
469 pc_factory_->SetOptions(options);
470
471 RTCConfiguration config;
472 config.enable_dtls_srtp.emplace(false);
473 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}
484
485// Tests that a DTLS call can be established when the certificate is specified
486// in the PeerConnection config and no certificate generator is specified.
487TEST_F(PeerConnectionCryptoUnitTest,
488 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
489 RTCConfiguration caller_config;
490 caller_config.enable_dtls_srtp.emplace(true);
491 caller_config.certificates.push_back(
492 FakeRTCCertificateGenerator::GenerateCertificate());
493 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
494
495 RTCConfiguration callee_config;
496 callee_config.enable_dtls_srtp.emplace(true);
497 callee_config.certificates.push_back(
498 FakeRTCCertificateGenerator::GenerateCertificate());
499 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
500
501 auto offer = caller->CreateOfferAndSetAsLocal();
502 ASSERT_TRUE(offer);
503 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
504
505 auto answer = callee->CreateAnswerAndSetAsLocal();
506 ASSERT_TRUE(answer);
507 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
508}
509
510// The following parameterized test verifies that CreateOffer/CreateAnswer
511// returns successfully (or with failure if the underlying certificate generator
512// fails) no matter when the DTLS certificate is generated. If multiple
513// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
514// they all finish after the certificate is generated.
515
516// Whether the test will call CreateOffer or CreateAnswer.
517enum class SdpType { kOffer, kAnswer };
518std::ostream& operator<<(std::ostream& out, SdpType value) {
519 switch (value) {
520 case SdpType::kOffer:
521 return out << "offer";
522 case SdpType::kAnswer:
523 return out << "answer";
524 default:
525 return out << "unknown";
526 }
527}
528
529// Whether the certificate will be generated before calling CreateOffer or
530// while CreateOffer is executing.
531enum class CertGenTime { kBefore, kDuring };
532std::ostream& operator<<(std::ostream& out, CertGenTime value) {
533 switch (value) {
534 case CertGenTime::kBefore:
535 return out << "before";
536 case CertGenTime::kDuring:
537 return out << "during";
538 default:
539 return out << "unknown";
540 }
541}
542
543// Whether the fake certificate generator will produce a certificate or fail.
544enum class CertGenResult { kSucceed, kFail };
545std::ostream& operator<<(std::ostream& out, CertGenResult value) {
546 switch (value) {
547 case CertGenResult::kSucceed:
548 return out << "succeed";
549 case CertGenResult::kFail:
550 return out << "fail";
551 default:
552 return out << "unknown";
553 }
554}
555
556class PeerConnectionCryptoDtlsCertGenUnitTest
557 : public PeerConnectionCryptoUnitTest,
558 public ::testing::WithParamInterface<
559 ::testing::tuple<SdpType, CertGenTime, CertGenResult, size_t>> {
560 protected:
561 PeerConnectionCryptoDtlsCertGenUnitTest() {
562 sdp_type_ = ::testing::get<0>(GetParam());
563 cert_gen_time_ = ::testing::get<1>(GetParam());
564 cert_gen_result_ = ::testing::get<2>(GetParam());
565 concurrent_calls_ = ::testing::get<3>(GetParam());
566 }
567
568 SdpType sdp_type_;
569 CertGenTime cert_gen_time_;
570 CertGenResult cert_gen_result_;
571 size_t concurrent_calls_;
572};
573
574TEST_P(PeerConnectionCryptoDtlsCertGenUnitTest, TestCertificateGeneration) {
575 RTCConfiguration config;
576 config.enable_dtls_srtp.emplace(true);
577 auto owned_fake_certificate_generator =
578 rtc::MakeUnique<FakeRTCCertificateGenerator>();
579 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
580 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
581 CertGenResult::kFail);
582 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
583 CertGenTime::kDuring);
584 WrapperPtr pc;
585 if (sdp_type_ == SdpType::kOffer) {
586 pc = CreatePeerConnectionWithAudioVideo(
587 config, std::move(owned_fake_certificate_generator));
588 } else {
589 auto caller = CreatePeerConnectionWithAudioVideo(config);
590 pc = CreatePeerConnectionWithAudioVideo(
591 config, std::move(owned_fake_certificate_generator));
592 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
593 }
594 if (cert_gen_time_ == CertGenTime::kBefore) {
595 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
596 fake_certificate_generator->generated_failures() >
597 0,
598 kGenerateCertTimeout);
599 } else {
600 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
601 fake_certificate_generator->set_should_wait(false);
602 }
603 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
604 observers;
605 for (size_t i = 0; i < concurrent_calls_; i++) {
606 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
607 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
608 observers.push_back(observer);
609 if (sdp_type_ == SdpType::kOffer) {
610 pc->pc()->CreateOffer(observer, nullptr);
611 } else {
612 pc->pc()->CreateAnswer(observer, nullptr);
613 }
614 }
615 for (auto& observer : observers) {
616 EXPECT_TRUE_WAIT(observer->called(), 1000);
617 if (cert_gen_result_ == CertGenResult::kSucceed) {
618 EXPECT_TRUE(observer->result());
619 } else {
620 EXPECT_FALSE(observer->result());
621 }
622 }
623}
624
625INSTANTIATE_TEST_CASE_P(
626 PeerConnectionCryptoUnitTest,
627 PeerConnectionCryptoDtlsCertGenUnitTest,
628 Combine(Values(SdpType::kOffer, SdpType::kAnswer),
629 Values(CertGenTime::kBefore, CertGenTime::kDuring),
630 Values(CertGenResult::kSucceed, CertGenResult::kFail),
631 Values(1, 3)));
632
Steve Anton8a63f782017-10-23 13:08:53 -0700633// Test that we can create and set an answer correctly when different
634// SSL roles have been negotiated for different transports.
635// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
636TEST_F(PeerConnectionCryptoUnitTest, CreateAnswerWithDifferentSslRoles) {
637 auto caller = CreatePeerConnectionWithAudioVideo();
638 auto callee = CreatePeerConnectionWithAudioVideo();
639
640 RTCOfferAnswerOptions options_no_bundle;
641 options_no_bundle.use_rtp_mux = false;
642
643 // First, negotiate different SSL roles for audio and video.
644 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
645 auto answer = callee->CreateAnswer(options_no_bundle);
646
647 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
648 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
649
650 ASSERT_TRUE(
651 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
652 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
653
654 // Now create an offer in the reverse direction, and ensure the initial
655 // offerer responds with an answer with the correct SSL roles.
656 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
657 answer = caller->CreateAnswer(options_no_bundle);
658
659 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
660 AudioConnectionRole(answer->description()));
661 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
662 VideoConnectionRole(answer->description()));
663
664 ASSERT_TRUE(
665 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
666 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
667
668 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
669 // audio is transferred over to video in the answer that completes the BUNDLE
670 // negotiation.
671 RTCOfferAnswerOptions options_bundle;
672 options_bundle.use_rtp_mux = true;
673
674 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
675 answer = caller->CreateAnswer(options_bundle);
676
677 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
678 AudioConnectionRole(answer->description()));
679 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
680 VideoConnectionRole(answer->description()));
681
682 ASSERT_TRUE(
683 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
684 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
685}
686
Steve Anton6b63cd52017-10-06 11:20:31 -0700687} // namespace webrtc