blob: c0c328161a91f82923e6094b518344ee70e0e055 [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
Mirko Bonadei317a1f02019-09-17 17:06:18 +020011#include <memory>
12
Karl Wiberg1b0eae32017-10-17 14:48:54 +020013#include "api/audio_codecs/builtin_audio_decoder_factory.h"
14#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010015#include "api/create_peerconnection_factory.h"
Anders Carlsson67537952018-05-03 11:28:29 +020016#include "api/video_codecs/builtin_video_decoder_factory.h"
17#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton10542f22019-01-11 09:11:00 -080018#include "p2p/base/fake_port_allocator.h"
19#include "pc/media_session.h"
20#include "pc/peer_connection_wrapper.h"
21#include "pc/sdp_utils.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070022#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080023#include "pc/test/android_test_initializer.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070024#endif
Steve Anton10542f22019-01-11 09:11:00 -080025#include "pc/test/fake_audio_capture_module.h"
26#include "pc/test/fake_rtc_certificate_generator.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070027#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080028#include "rtc_base/virtual_socket_server.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070029
30namespace webrtc {
31
32using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 13:08:53 -070033using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 11:20:31 -070034using ::testing::Combine;
Jonas Olssona4d87372019-07-05 19:08:33 +020035using ::testing::Values;
Steve Anton6b63cd52017-10-06 11:20:31 -070036
37constexpr int kGenerateCertTimeout = 1000;
38
Steve Anton71182f42018-01-19 14:59:54 -080039class PeerConnectionCryptoBaseTest : public ::testing::Test {
Steve Anton6b63cd52017-10-06 11:20:31 -070040 protected:
41 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
42
Steve Anton71182f42018-01-19 14:59:54 -080043 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
44 : vss_(new rtc::VirtualSocketServer()),
45 main_(vss_.get()),
46 sdp_semantics_(sdp_semantics) {
Steve Anton6b63cd52017-10-06 11:20:31 -070047#ifdef WEBRTC_ANDROID
48 InitializeAndroidObjects();
49#endif
50 pc_factory_ = CreatePeerConnectionFactory(
51 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020052 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
Anders Carlsson67537952018-05-03 11:28:29 +020053 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
54 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
55 nullptr /* audio_processing */);
Steve Anton6b63cd52017-10-06 11:20:31 -070056 }
57
Steve Anton8a63f782017-10-23 13:08:53 -070058 WrapperPtr CreatePeerConnection() {
59 return CreatePeerConnection(RTCConfiguration());
60 }
61
Steve Anton6b63cd52017-10-06 11:20:31 -070062 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
63 return CreatePeerConnection(config, nullptr);
64 }
65
66 WrapperPtr CreatePeerConnection(
67 const RTCConfiguration& config,
68 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020069 auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
Steve Anton6b63cd52017-10-06 11:20:31 -070070 rtc::Thread::Current(), nullptr);
Mirko Bonadei317a1f02019-09-17 17:06:18 +020071 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton71182f42018-01-19 14:59:54 -080072 RTCConfiguration modified_config = config;
73 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -070074 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton71182f42018-01-19 14:59:54 -080075 modified_config, std::move(fake_port_allocator), std::move(cert_gen),
Steve Anton6b63cd52017-10-06 11:20:31 -070076 observer.get());
77 if (!pc) {
78 return nullptr;
79 }
80
Yves Gerey4e933292018-10-31 15:36:05 +010081 observer->SetPeerConnectionInterface(pc.get());
Mirko Bonadei317a1f02019-09-17 17:06:18 +020082 return std::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
83 std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -070084 }
85
86 // Accepts the same arguments as CreatePeerConnection and adds default audio
87 // and video tracks.
88 template <typename... Args>
89 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
90 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
91 if (!wrapper) {
92 return nullptr;
93 }
Steve Anton8d3444d2017-10-20 15:30:51 -070094 wrapper->AddAudioTrack("a");
95 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -070096 return wrapper;
97 }
98
Steve Anton8a63f782017-10-23 13:08:53 -070099 cricket::ConnectionRole& AudioConnectionRole(
100 cricket::SessionDescription* desc) {
101 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
102 }
103
104 cricket::ConnectionRole& VideoConnectionRole(
105 cricket::SessionDescription* desc) {
106 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
107 }
108
109 cricket::ConnectionRole& ConnectionRoleFromContent(
110 cricket::SessionDescription* desc,
111 cricket::ContentInfo* content) {
112 RTC_DCHECK(content);
113 auto* transport_info = desc->GetTransportInfoByName(content->name);
114 RTC_DCHECK(transport_info);
115 return transport_info->description.connection_role;
116 }
117
Steve Anton6b63cd52017-10-06 11:20:31 -0700118 std::unique_ptr<rtc::VirtualSocketServer> vss_;
119 rtc::AutoSocketServerThread main_;
120 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800121 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700122};
123
124SdpContentPredicate HaveDtlsFingerprint() {
125 return [](const cricket::ContentInfo* content,
126 const cricket::TransportInfo* transport) {
127 return transport->description.identity_fingerprint != nullptr;
128 };
129}
130
Harald Alvestrand0d018412021-11-04 13:52:31 +0000131SdpContentPredicate HaveSdesCryptos() {
132 return [](const cricket::ContentInfo* content,
133 const cricket::TransportInfo* transport) {
134 return !content->media_description()->cryptos().empty();
135 };
136}
137
Steve Anton6b63cd52017-10-06 11:20:31 -0700138SdpContentPredicate HaveProtocol(const std::string& protocol) {
139 return [protocol](const cricket::ContentInfo* content,
140 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800141 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700142 };
143}
144
Harald Alvestrand0d018412021-11-04 13:52:31 +0000145SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
146 return [num_crypto_suites](const cricket::ContentInfo* content,
147 const cricket::TransportInfo* transport) {
148 const auto& cryptos = content->media_description()->cryptos();
149 if (cryptos.size() != num_crypto_suites) {
150 return false;
151 }
152 for (size_t i = 0; i < cryptos.size(); ++i) {
153 if (cryptos[i].key_params.size() == 67U &&
154 cryptos[i].cipher_suite == "AEAD_AES_256_GCM")
155 return true;
156 }
157 return false;
158 };
159}
160
Steve Anton71182f42018-01-19 14:59:54 -0800161class PeerConnectionCryptoTest
162 : public PeerConnectionCryptoBaseTest,
163 public ::testing::WithParamInterface<SdpSemantics> {
164 protected:
165 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
166};
167
Harald Alvestrand0d018412021-11-04 13:52:31 +0000168SdpContentMutator RemoveSdesCryptos() {
169 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
170 content->media_description()->set_cryptos({});
171 };
172}
173
Steve Anton6b63cd52017-10-06 11:20:31 -0700174SdpContentMutator RemoveDtlsFingerprint() {
175 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
176 transport->description.identity_fingerprint.reset();
177 };
178}
179
Harald Alvestrand0d018412021-11-04 13:52:31 +0000180// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
181// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800182TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700183 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700184 auto caller = CreatePeerConnectionWithAudioVideo(config);
185
186 auto offer = caller->CreateOffer();
187 ASSERT_TRUE(offer);
188
189 ASSERT_FALSE(offer->description()->contents().empty());
190 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000191 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700192 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
193 offer->description()));
194}
Steve Anton71182f42018-01-19 14:59:54 -0800195TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700196 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700197 auto caller = CreatePeerConnectionWithAudioVideo(config);
198 auto callee = CreatePeerConnectionWithAudioVideo(config);
199
200 callee->SetRemoteDescription(caller->CreateOffer());
201 auto answer = callee->CreateAnswer();
202 ASSERT_TRUE(answer);
203
204 ASSERT_FALSE(answer->description()->contents().empty());
205 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
Harald Alvestrand0d018412021-11-04 13:52:31 +0000206 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700207 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
208 answer->description()));
209}
210
Steve Anton6b63cd52017-10-06 11:20:31 -0700211// When encryption is disabled, the SDP offer/answer should have neither a DTLS
212// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800213TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700214 PeerConnectionFactoryInterface::Options options;
215 options.disable_encryption = true;
216 pc_factory_->SetOptions(options);
217
218 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700219 auto caller = CreatePeerConnectionWithAudioVideo(config);
220
221 auto offer = caller->CreateOffer();
222 ASSERT_TRUE(offer);
223
224 ASSERT_FALSE(offer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000225 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700226 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
227 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
228 offer->description()));
229}
Steve Anton71182f42018-01-19 14:59:54 -0800230TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700231 PeerConnectionFactoryInterface::Options options;
232 options.disable_encryption = true;
233 pc_factory_->SetOptions(options);
234
235 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700236 auto caller = CreatePeerConnectionWithAudioVideo(config);
237 auto callee = CreatePeerConnectionWithAudioVideo(config);
238
239 callee->SetRemoteDescription(caller->CreateOffer());
240 auto answer = callee->CreateAnswer();
241 ASSERT_TRUE(answer);
242
243 ASSERT_FALSE(answer->description()->contents().empty());
Harald Alvestrand0d018412021-11-04 13:52:31 +0000244 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
Steve Anton6b63cd52017-10-06 11:20:31 -0700245 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
246 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
247 answer->description()));
248}
249
Steve Anton71182f42018-01-19 14:59:54 -0800250TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700251 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700252 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700253 pc_factory_->SetOptions(options);
254
255 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700256 auto caller = CreatePeerConnectionWithAudioVideo(config);
257 auto callee = CreatePeerConnectionWithAudioVideo(config);
258
259 auto offer = caller->CreateOffer();
260 ASSERT_TRUE(offer);
261 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
262
263 auto answer = callee->CreateAnswer();
264 ASSERT_TRUE(answer);
265 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
Harald Alvestrandf9e502d2021-10-13 15:26:26 +0000266 // Note - this test doesn't verify that Gcm is present, just that it
267 // does not caue a failure.
Steve Anton6b63cd52017-10-06 11:20:31 -0700268}
269
270// The following group tests that two PeerConnections can successfully exchange
271// an offer/answer when DTLS is on and that they will refuse any offer/answer
272// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800273TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700274 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700275 auto caller = CreatePeerConnectionWithAudioVideo(config);
276 auto callee = CreatePeerConnectionWithAudioVideo(config);
277
278 auto offer = caller->CreateOfferAndSetAsLocal();
279 ASSERT_TRUE(offer);
280 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
281
282 auto answer = callee->CreateAnswerAndSetAsLocal();
283 ASSERT_TRUE(answer);
284 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
285}
Steve Anton71182f42018-01-19 14:59:54 -0800286TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700287 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
288 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700289 auto caller = CreatePeerConnectionWithAudioVideo(config);
290
291 auto offer = caller->CreateOffer();
292 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
293
294 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
295}
Steve Anton71182f42018-01-19 14:59:54 -0800296TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700297 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
298 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700299 auto caller = CreatePeerConnectionWithAudioVideo(config);
300 auto callee = CreatePeerConnectionWithAudioVideo(config);
301
302 auto offer = caller->CreateOffer();
303 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
304
305 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
306}
Steve Anton71182f42018-01-19 14:59:54 -0800307TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700308 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
309 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700310 auto caller = CreatePeerConnectionWithAudioVideo(config);
311 auto callee = CreatePeerConnectionWithAudioVideo(config);
312
313 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
314 auto answer = callee->CreateAnswer();
315 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
316}
Steve Anton71182f42018-01-19 14:59:54 -0800317TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700318 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
319 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700320 auto caller = CreatePeerConnectionWithAudioVideo(config);
321 auto callee = CreatePeerConnectionWithAudioVideo(config);
322
323 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
324 auto answer = callee->CreateAnswerAndSetAsLocal();
325 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
326
327 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
328}
329
330// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800331TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700332 PeerConnectionFactoryInterface::Options options;
333 options.disable_encryption = true;
334 pc_factory_->SetOptions(options);
335
336 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700337 auto caller = CreatePeerConnectionWithAudioVideo(config);
338 auto callee = CreatePeerConnectionWithAudioVideo(config);
339
340 auto offer = caller->CreateOfferAndSetAsLocal();
341 ASSERT_TRUE(offer);
342 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
343
344 auto answer = callee->CreateAnswerAndSetAsLocal();
345 ASSERT_TRUE(answer);
346 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
347}
348
349// Tests that a DTLS call can be established when the certificate is specified
350// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800351TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700352 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
353 RTCConfiguration caller_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700354 caller_config.certificates.push_back(
355 FakeRTCCertificateGenerator::GenerateCertificate());
356 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
357
358 RTCConfiguration callee_config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700359 callee_config.certificates.push_back(
360 FakeRTCCertificateGenerator::GenerateCertificate());
361 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
362
363 auto offer = caller->CreateOfferAndSetAsLocal();
364 ASSERT_TRUE(offer);
365 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
366
367 auto answer = callee->CreateAnswerAndSetAsLocal();
368 ASSERT_TRUE(answer);
369 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
370}
371
372// The following parameterized test verifies that CreateOffer/CreateAnswer
373// returns successfully (or with failure if the underlying certificate generator
374// fails) no matter when the DTLS certificate is generated. If multiple
375// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
376// they all finish after the certificate is generated.
377
Steve Anton6b63cd52017-10-06 11:20:31 -0700378// Whether the certificate will be generated before calling CreateOffer or
379// while CreateOffer is executing.
380enum class CertGenTime { kBefore, kDuring };
381std::ostream& operator<<(std::ostream& out, CertGenTime value) {
382 switch (value) {
383 case CertGenTime::kBefore:
384 return out << "before";
385 case CertGenTime::kDuring:
386 return out << "during";
387 default:
388 return out << "unknown";
389 }
390}
391
392// Whether the fake certificate generator will produce a certificate or fail.
393enum class CertGenResult { kSucceed, kFail };
394std::ostream& operator<<(std::ostream& out, CertGenResult value) {
395 switch (value) {
396 case CertGenResult::kSucceed:
397 return out << "succeed";
398 case CertGenResult::kFail:
399 return out << "fail";
400 default:
401 return out << "unknown";
402 }
403}
404
Steve Anton71182f42018-01-19 14:59:54 -0800405class PeerConnectionCryptoDtlsCertGenTest
406 : public PeerConnectionCryptoBaseTest,
407 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
408 SdpType,
409 CertGenTime,
410 CertGenResult,
411 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700412 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800413 PeerConnectionCryptoDtlsCertGenTest()
414 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
415 sdp_type_ = std::get<1>(GetParam());
416 cert_gen_time_ = std::get<2>(GetParam());
417 cert_gen_result_ = std::get<3>(GetParam());
418 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700419 }
420
421 SdpType sdp_type_;
422 CertGenTime cert_gen_time_;
423 CertGenResult cert_gen_result_;
424 size_t concurrent_calls_;
425};
426
Steve Anton71182f42018-01-19 14:59:54 -0800427TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700428 RTCConfiguration config;
Steve Anton6b63cd52017-10-06 11:20:31 -0700429 auto owned_fake_certificate_generator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200430 std::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700431 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
432 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
433 CertGenResult::kFail);
434 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
435 CertGenTime::kDuring);
436 WrapperPtr pc;
437 if (sdp_type_ == SdpType::kOffer) {
438 pc = CreatePeerConnectionWithAudioVideo(
439 config, std::move(owned_fake_certificate_generator));
440 } else {
441 auto caller = CreatePeerConnectionWithAudioVideo(config);
442 pc = CreatePeerConnectionWithAudioVideo(
443 config, std::move(owned_fake_certificate_generator));
444 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
445 }
446 if (cert_gen_time_ == CertGenTime::kBefore) {
447 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
448 fake_certificate_generator->generated_failures() >
449 0,
450 kGenerateCertTimeout);
451 } else {
452 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
453 fake_certificate_generator->set_should_wait(false);
454 }
455 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
456 observers;
457 for (size_t i = 0; i < concurrent_calls_; i++) {
458 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
Tommi87f70902021-04-27 14:43:08 +0200459 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700460 observers.push_back(observer);
461 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 12:32:18 +0200462 pc->pc()->CreateOffer(observer,
463 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700464 } else {
Niels Möllerf06f9232018-08-07 12:32:18 +0200465 pc->pc()->CreateAnswer(observer,
466 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700467 }
468 }
469 for (auto& observer : observers) {
470 EXPECT_TRUE_WAIT(observer->called(), 1000);
471 if (cert_gen_result_ == CertGenResult::kSucceed) {
472 EXPECT_TRUE(observer->result());
473 } else {
474 EXPECT_FALSE(observer->result());
475 }
476 }
477}
478
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100479INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800480 PeerConnectionCryptoTest,
481 PeerConnectionCryptoDtlsCertGenTest,
482 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
483 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700484 Values(CertGenTime::kBefore, CertGenTime::kDuring),
485 Values(CertGenResult::kSucceed, CertGenResult::kFail),
486 Values(1, 3)));
487
Steve Anton8a63f782017-10-23 13:08:53 -0700488// Test that we can create and set an answer correctly when different
489// SSL roles have been negotiated for different transports.
490// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800491TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700492 auto caller = CreatePeerConnectionWithAudioVideo();
493 auto callee = CreatePeerConnectionWithAudioVideo();
494
495 RTCOfferAnswerOptions options_no_bundle;
496 options_no_bundle.use_rtp_mux = false;
497
498 // First, negotiate different SSL roles for audio and video.
499 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
500 auto answer = callee->CreateAnswer(options_no_bundle);
501
502 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
503 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
504
505 ASSERT_TRUE(
506 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
507 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
508
509 // Now create an offer in the reverse direction, and ensure the initial
510 // offerer responds with an answer with the correct SSL roles.
511 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
512 answer = caller->CreateAnswer(options_no_bundle);
513
514 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
515 AudioConnectionRole(answer->description()));
516 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
517 VideoConnectionRole(answer->description()));
518
519 ASSERT_TRUE(
520 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
521 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
522
523 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
524 // audio is transferred over to video in the answer that completes the BUNDLE
525 // negotiation.
526 RTCOfferAnswerOptions options_bundle;
527 options_bundle.use_rtp_mux = true;
528
529 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
530 answer = caller->CreateAnswer(options_bundle);
531
532 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
533 AudioConnectionRole(answer->description()));
534 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
535 VideoConnectionRole(answer->description()));
536
537 ASSERT_TRUE(
538 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
539 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
540}
541
Steve Anton80dd7b52018-02-16 17:08:42 -0800542// Tests that if the DTLS fingerprint is invalid then all future calls to
543// SetLocalDescription and SetRemoteDescription will fail due to a session
544// error.
545// This is a regression test for crbug.com/800775
546TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
547 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
548 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
549
550 auto caller = CreatePeerConnectionWithAudioVideo();
551 RTCConfiguration callee_config;
Steve Anton80dd7b52018-02-16 17:08:42 -0800552 callee_config.certificates.push_back(callee_certificate);
553 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
554
555 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
556
557 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 10:40:48 -0700558 auto valid_answer = callee->CreateAnswer();
559 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-16 17:08:42 -0800560 auto* audio_content =
561 cricket::GetFirstAudioContent(invalid_answer->description());
562 ASSERT_TRUE(audio_content);
563 auto* audio_transport_info =
564 invalid_answer->description()->GetTransportInfoByName(
565 audio_content->name);
566 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700567 audio_transport_info->description.identity_fingerprint =
568 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800569
570 // Set the invalid answer and expect a fingerprint error.
571 std::string error;
572 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
573 EXPECT_PRED_FORMAT2(AssertStringContains, error,
574 "Local fingerprint does not match identity.");
575
576 // Make sure that setting a valid remote offer or local answer also fails now.
577 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
578 EXPECT_PRED_FORMAT2(AssertStringContains, error,
579 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 10:40:48 -0700580 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-16 17:08:42 -0800581 EXPECT_PRED_FORMAT2(AssertStringContains, error,
582 "Session error code: ERROR_CONTENT.");
583}
584
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100585INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
586 PeerConnectionCryptoTest,
587 Values(SdpSemantics::kPlanB,
588 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 14:59:54 -0800589
Steve Anton6b63cd52017-10-06 11:20:31 -0700590} // namespace webrtc