blob: 081e11ac6e64132dadd52212e3d25cfaea3ac70f [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;
29using ::testing::Values;
30using ::testing::Combine;
31
32constexpr int kGenerateCertTimeout = 1000;
33
34class PeerConnectionCryptoUnitTest : public ::testing::Test {
35 protected:
36 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
37
38 PeerConnectionCryptoUnitTest()
39 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
40#ifdef WEBRTC_ANDROID
41 InitializeAndroidObjects();
42#endif
43 pc_factory_ = CreatePeerConnectionFactory(
44 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020045 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
46 CreateBuiltinAudioDecoderFactory(), nullptr, nullptr);
Steve Anton6b63cd52017-10-06 11:20:31 -070047 }
48
49 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
50 return CreatePeerConnection(config, nullptr);
51 }
52
53 WrapperPtr CreatePeerConnection(
54 const RTCConfiguration& config,
55 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
56 auto fake_port_allocator = rtc::MakeUnique<cricket::FakePortAllocator>(
57 rtc::Thread::Current(), nullptr);
58 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
59 auto pc = pc_factory_->CreatePeerConnection(
60 config, std::move(fake_port_allocator), std::move(cert_gen),
61 observer.get());
62 if (!pc) {
63 return nullptr;
64 }
65
66 return rtc::MakeUnique<PeerConnectionWrapper>(pc_factory_, pc,
67 std::move(observer));
68 }
69
70 // Accepts the same arguments as CreatePeerConnection and adds default audio
71 // and video tracks.
72 template <typename... Args>
73 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
74 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
75 if (!wrapper) {
76 return nullptr;
77 }
78 wrapper->AddAudioVideoStream("s", "a", "v");
79 return wrapper;
80 }
81
82 std::unique_ptr<rtc::VirtualSocketServer> vss_;
83 rtc::AutoSocketServerThread main_;
84 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
85};
86
87SdpContentPredicate HaveDtlsFingerprint() {
88 return [](const cricket::ContentInfo* content,
89 const cricket::TransportInfo* transport) {
90 return transport->description.identity_fingerprint != nullptr;
91 };
92}
93
94SdpContentPredicate HaveSdesCryptos() {
95 return [](const cricket::ContentInfo* content,
96 const cricket::TransportInfo* transport) {
97 const auto* media_desc =
98 static_cast<const cricket::MediaContentDescription*>(
99 content->description);
100 return !media_desc->cryptos().empty();
101 };
102}
103
104SdpContentPredicate HaveProtocol(const std::string& protocol) {
105 return [protocol](const cricket::ContentInfo* content,
106 const cricket::TransportInfo* transport) {
107 const auto* media_desc =
108 static_cast<const cricket::MediaContentDescription*>(
109 content->description);
110 return media_desc->protocol() == protocol;
111 };
112}
113
114SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
115 return [num_crypto_suites](const cricket::ContentInfo* content,
116 const cricket::TransportInfo* transport) {
117 const auto* media_desc =
118 static_cast<const cricket::MediaContentDescription*>(
119 content->description);
120 if (media_desc->cryptos().size() != num_crypto_suites) {
121 return false;
122 }
123 const cricket::CryptoParams first_params = media_desc->cryptos()[0];
124 return first_params.key_params.size() == 67U &&
125 first_params.cipher_suite == "AEAD_AES_256_GCM";
126 };
127}
128
129SdpContentMutator RemoveSdesCryptos() {
130 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
131 auto* media_desc =
132 static_cast<cricket::MediaContentDescription*>(content->description);
133 media_desc->set_cryptos({});
134 };
135}
136
137SdpContentMutator RemoveDtlsFingerprint() {
138 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
139 transport->description.identity_fingerprint.reset();
140 };
141}
142
143// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
144// no SDES cryptos.
145TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsEnabled) {
146 RTCConfiguration config;
147 config.enable_dtls_srtp.emplace(true);
148 auto caller = CreatePeerConnectionWithAudioVideo(config);
149
150 auto offer = caller->CreateOffer();
151 ASSERT_TRUE(offer);
152
153 ASSERT_FALSE(offer->description()->contents().empty());
154 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
155 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
156 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
157 offer->description()));
158}
159TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
160 RTCConfiguration config;
161 config.enable_dtls_srtp.emplace(true);
162 auto caller = CreatePeerConnectionWithAudioVideo(config);
163 auto callee = CreatePeerConnectionWithAudioVideo(config);
164
165 callee->SetRemoteDescription(caller->CreateOffer());
166 auto answer = callee->CreateAnswer();
167 ASSERT_TRUE(answer);
168
169 ASSERT_FALSE(answer->description()->contents().empty());
170 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
171 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
172 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
173 answer->description()));
174}
175
176// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
177// should not have a DTLS fingerprint.
178TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsDisabled) {
179 RTCConfiguration config;
180 config.enable_dtls_srtp.emplace(false);
181 auto caller = CreatePeerConnectionWithAudioVideo(config);
182
183 auto offer = caller->CreateOffer();
184 ASSERT_TRUE(offer);
185
186 ASSERT_FALSE(offer->description()->contents().empty());
187 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
188 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
189 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
190 offer->description()));
191}
192TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
193 RTCConfiguration config;
194 config.enable_dtls_srtp.emplace(false);
195 auto caller = CreatePeerConnectionWithAudioVideo(config);
196 auto callee = CreatePeerConnectionWithAudioVideo(config);
197
198 callee->SetRemoteDescription(caller->CreateOffer());
199 auto answer = callee->CreateAnswer();
200 ASSERT_TRUE(answer);
201
202 ASSERT_FALSE(answer->description()->contents().empty());
203 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
204 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
205 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
206 answer->description()));
207}
208
209// When encryption is disabled, the SDP offer/answer should have neither a DTLS
210// fingerprint nor any SDES crypto options.
211TEST_F(PeerConnectionCryptoUnitTest,
212 CorrectCryptoInOfferWhenEncryptionDisabled) {
213 PeerConnectionFactoryInterface::Options options;
214 options.disable_encryption = true;
215 pc_factory_->SetOptions(options);
216
217 RTCConfiguration config;
218 config.enable_dtls_srtp.emplace(false);
219 auto caller = CreatePeerConnectionWithAudioVideo(config);
220
221 auto offer = caller->CreateOffer();
222 ASSERT_TRUE(offer);
223
224 ASSERT_FALSE(offer->description()->contents().empty());
225 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
226 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
227 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
228 offer->description()));
229}
230TEST_F(PeerConnectionCryptoUnitTest,
231 CorrectCryptoInAnswerWhenEncryptionDisabled) {
232 PeerConnectionFactoryInterface::Options options;
233 options.disable_encryption = true;
234 pc_factory_->SetOptions(options);
235
236 RTCConfiguration config;
237 config.enable_dtls_srtp.emplace(false);
238 auto caller = CreatePeerConnectionWithAudioVideo(config);
239 auto callee = CreatePeerConnectionWithAudioVideo(config);
240
241 callee->SetRemoteDescription(caller->CreateOffer());
242 auto answer = callee->CreateAnswer();
243 ASSERT_TRUE(answer);
244
245 ASSERT_FALSE(answer->description()->contents().empty());
246 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
247 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
248 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
249 answer->description()));
250}
251
252// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
253// should have the correct ciphers in the SDES crypto options.
254// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
255// in the answer.
256TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWithSdesAndGcm) {
257 PeerConnectionFactoryInterface::Options options;
258 options.crypto_options.enable_gcm_crypto_suites = true;
259 pc_factory_->SetOptions(options);
260
261 RTCConfiguration config;
262 config.enable_dtls_srtp.emplace(false);
263 auto caller = CreatePeerConnectionWithAudioVideo(config);
264
265 auto offer = caller->CreateOffer();
266 ASSERT_TRUE(offer);
267
268 ASSERT_FALSE(offer->description()->contents().empty());
269 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
270}
271TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWithSdesAndGcm) {
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 auto callee = CreatePeerConnectionWithAudioVideo(config);
280
281 callee->SetRemoteDescription(caller->CreateOffer());
282 auto answer = callee->CreateAnswer();
283 ASSERT_TRUE(answer);
284
285 ASSERT_FALSE(answer->description()->contents().empty());
286 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
287}
288
289TEST_F(PeerConnectionCryptoUnitTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
290 PeerConnectionFactoryInterface::Options options;
291 options.crypto_options.enable_gcm_crypto_suites = true;
292 pc_factory_->SetOptions(options);
293
294 RTCConfiguration config;
295 config.enable_dtls_srtp.emplace(false);
296 auto caller = CreatePeerConnectionWithAudioVideo(config);
297 auto callee = CreatePeerConnectionWithAudioVideo(config);
298
299 auto offer = caller->CreateOffer();
300 ASSERT_TRUE(offer);
301 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
302
303 auto answer = callee->CreateAnswer();
304 ASSERT_TRUE(answer);
305 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
306}
307
308// The following group tests that two PeerConnections can successfully exchange
309// an offer/answer when DTLS is off and that they will refuse any offer/answer
310// applied locally/remotely if it does not include SDES cryptos.
311TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenSdesOn) {
312 RTCConfiguration config;
313 config.enable_dtls_srtp.emplace(false);
314 auto caller = CreatePeerConnectionWithAudioVideo(config);
315 auto callee = CreatePeerConnectionWithAudioVideo(config);
316
317 auto offer = caller->CreateOfferAndSetAsLocal();
318 ASSERT_TRUE(offer);
319 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
320
321 auto answer = callee->CreateAnswerAndSetAsLocal();
322 ASSERT_TRUE(answer);
323 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
324}
325TEST_F(PeerConnectionCryptoUnitTest,
326 FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
327 RTCConfiguration config;
328 config.enable_dtls_srtp.emplace(false);
329 auto caller = CreatePeerConnectionWithAudioVideo(config);
330
331 auto offer = caller->CreateOffer();
332 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
333
334 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
335}
336TEST_F(PeerConnectionCryptoUnitTest,
337 FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
338 RTCConfiguration config;
339 config.enable_dtls_srtp.emplace(false);
340 auto caller = CreatePeerConnectionWithAudioVideo(config);
341 auto callee = CreatePeerConnectionWithAudioVideo(config);
342
343 auto offer = caller->CreateOffer();
344 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
345
346 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
347}
348TEST_F(PeerConnectionCryptoUnitTest,
349 FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
350 RTCConfiguration config;
351 config.enable_dtls_srtp.emplace(false);
352 auto caller = CreatePeerConnectionWithAudioVideo(config);
353 auto callee = CreatePeerConnectionWithAudioVideo(config);
354
355 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
356 auto answer = callee->CreateAnswer();
357 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
358
359 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
360}
361TEST_F(PeerConnectionCryptoUnitTest,
362 FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
363 RTCConfiguration config;
364 config.enable_dtls_srtp.emplace(false);
365 auto caller = CreatePeerConnectionWithAudioVideo(config);
366 auto callee = CreatePeerConnectionWithAudioVideo(config);
367
368 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
369 auto answer = callee->CreateAnswerAndSetAsLocal();
370 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
371
372 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
373}
374
375// The following group tests that two PeerConnections can successfully exchange
376// an offer/answer when DTLS is on and that they will refuse any offer/answer
377// applied locally/remotely if it does not include a DTLS fingerprint.
378TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenDtlsOn) {
379 RTCConfiguration config;
380 config.enable_dtls_srtp.emplace(true);
381 auto caller = CreatePeerConnectionWithAudioVideo(config);
382 auto callee = CreatePeerConnectionWithAudioVideo(config);
383
384 auto offer = caller->CreateOfferAndSetAsLocal();
385 ASSERT_TRUE(offer);
386 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
387
388 auto answer = callee->CreateAnswerAndSetAsLocal();
389 ASSERT_TRUE(answer);
390 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
391}
392TEST_F(PeerConnectionCryptoUnitTest,
393 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
394 RTCConfiguration config;
395 config.enable_dtls_srtp.emplace(true);
396 auto caller = CreatePeerConnectionWithAudioVideo(config);
397
398 auto offer = caller->CreateOffer();
399 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
400
401 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
402}
403TEST_F(PeerConnectionCryptoUnitTest,
404 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
405 RTCConfiguration config;
406 config.enable_dtls_srtp.emplace(true);
407 auto caller = CreatePeerConnectionWithAudioVideo(config);
408 auto callee = CreatePeerConnectionWithAudioVideo(config);
409
410 auto offer = caller->CreateOffer();
411 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
412
413 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
414}
415TEST_F(PeerConnectionCryptoUnitTest,
416 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
417 RTCConfiguration config;
418 config.enable_dtls_srtp.emplace(true);
419 auto caller = CreatePeerConnectionWithAudioVideo(config);
420 auto callee = CreatePeerConnectionWithAudioVideo(config);
421
422 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
423 auto answer = callee->CreateAnswer();
424 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
425}
426TEST_F(PeerConnectionCryptoUnitTest,
427 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
428 RTCConfiguration config;
429 config.enable_dtls_srtp.emplace(true);
430 auto caller = CreatePeerConnectionWithAudioVideo(config);
431 auto callee = CreatePeerConnectionWithAudioVideo(config);
432
433 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
434 auto answer = callee->CreateAnswerAndSetAsLocal();
435 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
436
437 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
438}
439
440// Test that an offer/answer can be exchanged when encryption is disabled.
441TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenNoEncryption) {
442 PeerConnectionFactoryInterface::Options options;
443 options.disable_encryption = true;
444 pc_factory_->SetOptions(options);
445
446 RTCConfiguration config;
447 config.enable_dtls_srtp.emplace(false);
448 auto caller = CreatePeerConnectionWithAudioVideo(config);
449 auto callee = CreatePeerConnectionWithAudioVideo(config);
450
451 auto offer = caller->CreateOfferAndSetAsLocal();
452 ASSERT_TRUE(offer);
453 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
454
455 auto answer = callee->CreateAnswerAndSetAsLocal();
456 ASSERT_TRUE(answer);
457 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
458}
459
460// Tests that a DTLS call can be established when the certificate is specified
461// in the PeerConnection config and no certificate generator is specified.
462TEST_F(PeerConnectionCryptoUnitTest,
463 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
464 RTCConfiguration caller_config;
465 caller_config.enable_dtls_srtp.emplace(true);
466 caller_config.certificates.push_back(
467 FakeRTCCertificateGenerator::GenerateCertificate());
468 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
469
470 RTCConfiguration callee_config;
471 callee_config.enable_dtls_srtp.emplace(true);
472 callee_config.certificates.push_back(
473 FakeRTCCertificateGenerator::GenerateCertificate());
474 auto callee = CreatePeerConnectionWithAudioVideo(callee_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// The following parameterized test verifies that CreateOffer/CreateAnswer
486// returns successfully (or with failure if the underlying certificate generator
487// fails) no matter when the DTLS certificate is generated. If multiple
488// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
489// they all finish after the certificate is generated.
490
491// Whether the test will call CreateOffer or CreateAnswer.
492enum class SdpType { kOffer, kAnswer };
493std::ostream& operator<<(std::ostream& out, SdpType value) {
494 switch (value) {
495 case SdpType::kOffer:
496 return out << "offer";
497 case SdpType::kAnswer:
498 return out << "answer";
499 default:
500 return out << "unknown";
501 }
502}
503
504// Whether the certificate will be generated before calling CreateOffer or
505// while CreateOffer is executing.
506enum class CertGenTime { kBefore, kDuring };
507std::ostream& operator<<(std::ostream& out, CertGenTime value) {
508 switch (value) {
509 case CertGenTime::kBefore:
510 return out << "before";
511 case CertGenTime::kDuring:
512 return out << "during";
513 default:
514 return out << "unknown";
515 }
516}
517
518// Whether the fake certificate generator will produce a certificate or fail.
519enum class CertGenResult { kSucceed, kFail };
520std::ostream& operator<<(std::ostream& out, CertGenResult value) {
521 switch (value) {
522 case CertGenResult::kSucceed:
523 return out << "succeed";
524 case CertGenResult::kFail:
525 return out << "fail";
526 default:
527 return out << "unknown";
528 }
529}
530
531class PeerConnectionCryptoDtlsCertGenUnitTest
532 : public PeerConnectionCryptoUnitTest,
533 public ::testing::WithParamInterface<
534 ::testing::tuple<SdpType, CertGenTime, CertGenResult, size_t>> {
535 protected:
536 PeerConnectionCryptoDtlsCertGenUnitTest() {
537 sdp_type_ = ::testing::get<0>(GetParam());
538 cert_gen_time_ = ::testing::get<1>(GetParam());
539 cert_gen_result_ = ::testing::get<2>(GetParam());
540 concurrent_calls_ = ::testing::get<3>(GetParam());
541 }
542
543 SdpType sdp_type_;
544 CertGenTime cert_gen_time_;
545 CertGenResult cert_gen_result_;
546 size_t concurrent_calls_;
547};
548
549TEST_P(PeerConnectionCryptoDtlsCertGenUnitTest, TestCertificateGeneration) {
550 RTCConfiguration config;
551 config.enable_dtls_srtp.emplace(true);
552 auto owned_fake_certificate_generator =
553 rtc::MakeUnique<FakeRTCCertificateGenerator>();
554 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
555 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
556 CertGenResult::kFail);
557 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
558 CertGenTime::kDuring);
559 WrapperPtr pc;
560 if (sdp_type_ == SdpType::kOffer) {
561 pc = CreatePeerConnectionWithAudioVideo(
562 config, std::move(owned_fake_certificate_generator));
563 } else {
564 auto caller = CreatePeerConnectionWithAudioVideo(config);
565 pc = CreatePeerConnectionWithAudioVideo(
566 config, std::move(owned_fake_certificate_generator));
567 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
568 }
569 if (cert_gen_time_ == CertGenTime::kBefore) {
570 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
571 fake_certificate_generator->generated_failures() >
572 0,
573 kGenerateCertTimeout);
574 } else {
575 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
576 fake_certificate_generator->set_should_wait(false);
577 }
578 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
579 observers;
580 for (size_t i = 0; i < concurrent_calls_; i++) {
581 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
582 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
583 observers.push_back(observer);
584 if (sdp_type_ == SdpType::kOffer) {
585 pc->pc()->CreateOffer(observer, nullptr);
586 } else {
587 pc->pc()->CreateAnswer(observer, nullptr);
588 }
589 }
590 for (auto& observer : observers) {
591 EXPECT_TRUE_WAIT(observer->called(), 1000);
592 if (cert_gen_result_ == CertGenResult::kSucceed) {
593 EXPECT_TRUE(observer->result());
594 } else {
595 EXPECT_FALSE(observer->result());
596 }
597 }
598}
599
600INSTANTIATE_TEST_CASE_P(
601 PeerConnectionCryptoUnitTest,
602 PeerConnectionCryptoDtlsCertGenUnitTest,
603 Combine(Values(SdpType::kOffer, SdpType::kAnswer),
604 Values(CertGenTime::kBefore, CertGenTime::kDuring),
605 Values(CertGenResult::kSucceed, CertGenResult::kFail),
606 Values(1, 3)));
607
608} // namespace webrtc