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