blob: 99eb5cd7ac8ed08016cd3caea3ba09cd8bbe6feb [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
131SdpContentPredicate HaveSdesCryptos() {
132 return [](const cricket::ContentInfo* content,
133 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800134 return !content->media_description()->cryptos().empty();
Steve Anton6b63cd52017-10-06 11:20:31 -0700135 };
136}
137
138SdpContentPredicate 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
145SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
146 return [num_crypto_suites](const cricket::ContentInfo* content,
147 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800148 const auto& cryptos = content->media_description()->cryptos();
149 if (cryptos.size() != num_crypto_suites) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700150 return false;
151 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800152 const cricket::CryptoParams first_params = cryptos[0];
Steve Anton6b63cd52017-10-06 11:20:31 -0700153 return first_params.key_params.size() == 67U &&
154 first_params.cipher_suite == "AEAD_AES_256_GCM";
155 };
156}
157
Steve Anton71182f42018-01-19 14:59:54 -0800158class PeerConnectionCryptoTest
159 : public PeerConnectionCryptoBaseTest,
160 public ::testing::WithParamInterface<SdpSemantics> {
161 protected:
162 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
163};
164
Steve Anton6b63cd52017-10-06 11:20:31 -0700165SdpContentMutator RemoveSdesCryptos() {
166 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800167 content->media_description()->set_cryptos({});
Steve Anton6b63cd52017-10-06 11:20:31 -0700168 };
169}
170
171SdpContentMutator RemoveDtlsFingerprint() {
172 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
173 transport->description.identity_fingerprint.reset();
174 };
175}
176
177// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
178// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800179TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700180 RTCConfiguration config;
181 config.enable_dtls_srtp.emplace(true);
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(HaveDtlsFingerprint(), offer->description()));
189 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
190 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
191 offer->description()));
192}
Steve Anton71182f42018-01-19 14:59:54 -0800193TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700194 RTCConfiguration config;
195 config.enable_dtls_srtp.emplace(true);
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(HaveDtlsFingerprint(), answer->description()));
205 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
206 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
207 answer->description()));
208}
209
210// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
211// should not have a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800212TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700213 RTCConfiguration config;
214 config.enable_dtls_srtp.emplace(false);
215 auto caller = CreatePeerConnectionWithAudioVideo(config);
216
217 auto offer = caller->CreateOffer();
218 ASSERT_TRUE(offer);
219
220 ASSERT_FALSE(offer->description()->contents().empty());
221 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
222 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
223 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
224 offer->description()));
225}
Steve Anton71182f42018-01-19 14:59:54 -0800226TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700227 RTCConfiguration config;
228 config.enable_dtls_srtp.emplace(false);
229 auto caller = CreatePeerConnectionWithAudioVideo(config);
230 auto callee = CreatePeerConnectionWithAudioVideo(config);
231
232 callee->SetRemoteDescription(caller->CreateOffer());
233 auto answer = callee->CreateAnswer();
234 ASSERT_TRUE(answer);
235
236 ASSERT_FALSE(answer->description()->contents().empty());
237 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
238 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
239 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
240 answer->description()));
241}
242
243// When encryption is disabled, the SDP offer/answer should have neither a DTLS
244// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800245TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700246 PeerConnectionFactoryInterface::Options options;
247 options.disable_encryption = true;
248 pc_factory_->SetOptions(options);
249
250 RTCConfiguration config;
251 config.enable_dtls_srtp.emplace(false);
252 auto caller = CreatePeerConnectionWithAudioVideo(config);
253
254 auto offer = caller->CreateOffer();
255 ASSERT_TRUE(offer);
256
257 ASSERT_FALSE(offer->description()->contents().empty());
258 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
259 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
260 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
261 offer->description()));
262}
Steve Anton71182f42018-01-19 14:59:54 -0800263TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700264 PeerConnectionFactoryInterface::Options options;
265 options.disable_encryption = true;
266 pc_factory_->SetOptions(options);
267
268 RTCConfiguration config;
269 config.enable_dtls_srtp.emplace(false);
270 auto caller = CreatePeerConnectionWithAudioVideo(config);
271 auto callee = CreatePeerConnectionWithAudioVideo(config);
272
273 callee->SetRemoteDescription(caller->CreateOffer());
274 auto answer = callee->CreateAnswer();
275 ASSERT_TRUE(answer);
276
277 ASSERT_FALSE(answer->description()->contents().empty());
278 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
279 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
280 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
281 answer->description()));
282}
283
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700284// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
285// set in the configuration it should overrite the settings set in the factory.
286TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
287 PeerConnectionFactoryInterface::Options options;
288 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
289 pc_factory_->SetOptions(options);
290
291 RTCConfiguration config;
292 config.enable_dtls_srtp.emplace(false);
293 CryptoOptions crypto_options;
294 crypto_options.srtp.enable_gcm_crypto_suites = false;
295 config.crypto_options = crypto_options;
296 auto caller = CreatePeerConnectionWithAudioVideo(config);
297
298 auto offer = caller->CreateOffer();
299 ASSERT_TRUE(offer);
300
301 ASSERT_FALSE(offer->description()->contents().empty());
302 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
303 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
304}
305
Steve Anton6b63cd52017-10-06 11:20:31 -0700306// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
307// should have the correct ciphers in the SDES crypto options.
308// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
309// in the answer.
Steve Anton71182f42018-01-19 14:59:54 -0800310TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700311 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700312 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700313 pc_factory_->SetOptions(options);
314
315 RTCConfiguration config;
316 config.enable_dtls_srtp.emplace(false);
317 auto caller = CreatePeerConnectionWithAudioVideo(config);
318
319 auto offer = caller->CreateOffer();
320 ASSERT_TRUE(offer);
321
322 ASSERT_FALSE(offer->description()->contents().empty());
323 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
324}
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700325
Steve Anton71182f42018-01-19 14:59:54 -0800326TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700327 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700328 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700329 pc_factory_->SetOptions(options);
330
331 RTCConfiguration config;
332 config.enable_dtls_srtp.emplace(false);
333 auto caller = CreatePeerConnectionWithAudioVideo(config);
334 auto callee = CreatePeerConnectionWithAudioVideo(config);
335
336 callee->SetRemoteDescription(caller->CreateOffer());
337 auto answer = callee->CreateAnswer();
338 ASSERT_TRUE(answer);
339
340 ASSERT_FALSE(answer->description()->contents().empty());
341 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
342}
343
Steve Anton71182f42018-01-19 14:59:54 -0800344TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700345 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700346 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700347 pc_factory_->SetOptions(options);
348
349 RTCConfiguration config;
350 config.enable_dtls_srtp.emplace(false);
351 auto caller = CreatePeerConnectionWithAudioVideo(config);
352 auto callee = CreatePeerConnectionWithAudioVideo(config);
353
354 auto offer = caller->CreateOffer();
355 ASSERT_TRUE(offer);
356 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
357
358 auto answer = callee->CreateAnswer();
359 ASSERT_TRUE(answer);
360 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
361}
362
363// The following group tests that two PeerConnections can successfully exchange
364// an offer/answer when DTLS is off and that they will refuse any offer/answer
365// applied locally/remotely if it does not include SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800366TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700367 RTCConfiguration config;
368 config.enable_dtls_srtp.emplace(false);
369 auto caller = CreatePeerConnectionWithAudioVideo(config);
370 auto callee = CreatePeerConnectionWithAudioVideo(config);
371
372 auto offer = caller->CreateOfferAndSetAsLocal();
373 ASSERT_TRUE(offer);
374 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
375
376 auto answer = callee->CreateAnswerAndSetAsLocal();
377 ASSERT_TRUE(answer);
378 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
379}
Steve Anton71182f42018-01-19 14:59:54 -0800380TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700381 RTCConfiguration config;
382 config.enable_dtls_srtp.emplace(false);
383 auto caller = CreatePeerConnectionWithAudioVideo(config);
384
385 auto offer = caller->CreateOffer();
386 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
387
388 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
389}
Steve Anton71182f42018-01-19 14:59:54 -0800390TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700391 RTCConfiguration config;
392 config.enable_dtls_srtp.emplace(false);
393 auto caller = CreatePeerConnectionWithAudioVideo(config);
394 auto callee = CreatePeerConnectionWithAudioVideo(config);
395
396 auto offer = caller->CreateOffer();
397 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
398
399 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
400}
Steve Anton71182f42018-01-19 14:59:54 -0800401TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700402 RTCConfiguration config;
403 config.enable_dtls_srtp.emplace(false);
404 auto caller = CreatePeerConnectionWithAudioVideo(config);
405 auto callee = CreatePeerConnectionWithAudioVideo(config);
406
407 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
408 auto answer = callee->CreateAnswer();
409 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
410
411 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
412}
Steve Anton71182f42018-01-19 14:59:54 -0800413TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700414 RTCConfiguration config;
415 config.enable_dtls_srtp.emplace(false);
416 auto caller = CreatePeerConnectionWithAudioVideo(config);
417 auto callee = CreatePeerConnectionWithAudioVideo(config);
418
419 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
420 auto answer = callee->CreateAnswerAndSetAsLocal();
421 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
422
423 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
424}
425
426// The following group tests that two PeerConnections can successfully exchange
427// an offer/answer when DTLS is on and that they will refuse any offer/answer
428// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800429TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700430 RTCConfiguration config;
431 config.enable_dtls_srtp.emplace(true);
432 auto caller = CreatePeerConnectionWithAudioVideo(config);
433 auto callee = CreatePeerConnectionWithAudioVideo(config);
434
435 auto offer = caller->CreateOfferAndSetAsLocal();
436 ASSERT_TRUE(offer);
437 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
438
439 auto answer = callee->CreateAnswerAndSetAsLocal();
440 ASSERT_TRUE(answer);
441 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
442}
Steve Anton71182f42018-01-19 14:59:54 -0800443TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700444 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
445 RTCConfiguration config;
446 config.enable_dtls_srtp.emplace(true);
447 auto caller = CreatePeerConnectionWithAudioVideo(config);
448
449 auto offer = caller->CreateOffer();
450 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
451
452 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
453}
Steve Anton71182f42018-01-19 14:59:54 -0800454TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700455 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
456 RTCConfiguration config;
457 config.enable_dtls_srtp.emplace(true);
458 auto caller = CreatePeerConnectionWithAudioVideo(config);
459 auto callee = CreatePeerConnectionWithAudioVideo(config);
460
461 auto offer = caller->CreateOffer();
462 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
463
464 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
465}
Steve Anton71182f42018-01-19 14:59:54 -0800466TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700467 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
468 RTCConfiguration config;
469 config.enable_dtls_srtp.emplace(true);
470 auto caller = CreatePeerConnectionWithAudioVideo(config);
471 auto callee = CreatePeerConnectionWithAudioVideo(config);
472
473 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
474 auto answer = callee->CreateAnswer();
475 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
476}
Steve Anton71182f42018-01-19 14:59:54 -0800477TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700478 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
479 RTCConfiguration config;
480 config.enable_dtls_srtp.emplace(true);
481 auto caller = CreatePeerConnectionWithAudioVideo(config);
482 auto callee = CreatePeerConnectionWithAudioVideo(config);
483
484 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
485 auto answer = callee->CreateAnswerAndSetAsLocal();
486 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
487
488 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
489}
490
491// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800492TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700493 PeerConnectionFactoryInterface::Options options;
494 options.disable_encryption = true;
495 pc_factory_->SetOptions(options);
496
497 RTCConfiguration config;
498 config.enable_dtls_srtp.emplace(false);
499 auto caller = CreatePeerConnectionWithAudioVideo(config);
500 auto callee = CreatePeerConnectionWithAudioVideo(config);
501
502 auto offer = caller->CreateOfferAndSetAsLocal();
503 ASSERT_TRUE(offer);
504 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
505
506 auto answer = callee->CreateAnswerAndSetAsLocal();
507 ASSERT_TRUE(answer);
508 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
509}
510
511// Tests that a DTLS call can be established when the certificate is specified
512// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800513TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700514 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
515 RTCConfiguration caller_config;
516 caller_config.enable_dtls_srtp.emplace(true);
517 caller_config.certificates.push_back(
518 FakeRTCCertificateGenerator::GenerateCertificate());
519 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
520
521 RTCConfiguration callee_config;
522 callee_config.enable_dtls_srtp.emplace(true);
523 callee_config.certificates.push_back(
524 FakeRTCCertificateGenerator::GenerateCertificate());
525 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
526
527 auto offer = caller->CreateOfferAndSetAsLocal();
528 ASSERT_TRUE(offer);
529 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
530
531 auto answer = callee->CreateAnswerAndSetAsLocal();
532 ASSERT_TRUE(answer);
533 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
534}
535
536// The following parameterized test verifies that CreateOffer/CreateAnswer
537// returns successfully (or with failure if the underlying certificate generator
538// fails) no matter when the DTLS certificate is generated. If multiple
539// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
540// they all finish after the certificate is generated.
541
Steve Anton6b63cd52017-10-06 11:20:31 -0700542// Whether the certificate will be generated before calling CreateOffer or
543// while CreateOffer is executing.
544enum class CertGenTime { kBefore, kDuring };
545std::ostream& operator<<(std::ostream& out, CertGenTime value) {
546 switch (value) {
547 case CertGenTime::kBefore:
548 return out << "before";
549 case CertGenTime::kDuring:
550 return out << "during";
551 default:
552 return out << "unknown";
553 }
554}
555
556// Whether the fake certificate generator will produce a certificate or fail.
557enum class CertGenResult { kSucceed, kFail };
558std::ostream& operator<<(std::ostream& out, CertGenResult value) {
559 switch (value) {
560 case CertGenResult::kSucceed:
561 return out << "succeed";
562 case CertGenResult::kFail:
563 return out << "fail";
564 default:
565 return out << "unknown";
566 }
567}
568
Steve Anton71182f42018-01-19 14:59:54 -0800569class PeerConnectionCryptoDtlsCertGenTest
570 : public PeerConnectionCryptoBaseTest,
571 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
572 SdpType,
573 CertGenTime,
574 CertGenResult,
575 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700576 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800577 PeerConnectionCryptoDtlsCertGenTest()
578 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
579 sdp_type_ = std::get<1>(GetParam());
580 cert_gen_time_ = std::get<2>(GetParam());
581 cert_gen_result_ = std::get<3>(GetParam());
582 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700583 }
584
585 SdpType sdp_type_;
586 CertGenTime cert_gen_time_;
587 CertGenResult cert_gen_result_;
588 size_t concurrent_calls_;
589};
590
Steve Anton71182f42018-01-19 14:59:54 -0800591TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700592 RTCConfiguration config;
593 config.enable_dtls_srtp.emplace(true);
594 auto owned_fake_certificate_generator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200595 std::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700596 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
597 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
598 CertGenResult::kFail);
599 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
600 CertGenTime::kDuring);
601 WrapperPtr pc;
602 if (sdp_type_ == SdpType::kOffer) {
603 pc = CreatePeerConnectionWithAudioVideo(
604 config, std::move(owned_fake_certificate_generator));
605 } else {
606 auto caller = CreatePeerConnectionWithAudioVideo(config);
607 pc = CreatePeerConnectionWithAudioVideo(
608 config, std::move(owned_fake_certificate_generator));
609 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
610 }
611 if (cert_gen_time_ == CertGenTime::kBefore) {
612 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
613 fake_certificate_generator->generated_failures() >
614 0,
615 kGenerateCertTimeout);
616 } else {
617 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
618 fake_certificate_generator->set_should_wait(false);
619 }
620 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
621 observers;
622 for (size_t i = 0; i < concurrent_calls_; i++) {
623 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
624 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
625 observers.push_back(observer);
626 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 12:32:18 +0200627 pc->pc()->CreateOffer(observer,
628 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700629 } else {
Niels Möllerf06f9232018-08-07 12:32:18 +0200630 pc->pc()->CreateAnswer(observer,
631 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700632 }
633 }
634 for (auto& observer : observers) {
635 EXPECT_TRUE_WAIT(observer->called(), 1000);
636 if (cert_gen_result_ == CertGenResult::kSucceed) {
637 EXPECT_TRUE(observer->result());
638 } else {
639 EXPECT_FALSE(observer->result());
640 }
641 }
642}
643
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100644INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800645 PeerConnectionCryptoTest,
646 PeerConnectionCryptoDtlsCertGenTest,
647 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
648 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700649 Values(CertGenTime::kBefore, CertGenTime::kDuring),
650 Values(CertGenResult::kSucceed, CertGenResult::kFail),
651 Values(1, 3)));
652
Steve Anton8a63f782017-10-23 13:08:53 -0700653// Test that we can create and set an answer correctly when different
654// SSL roles have been negotiated for different transports.
655// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800656TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700657 auto caller = CreatePeerConnectionWithAudioVideo();
658 auto callee = CreatePeerConnectionWithAudioVideo();
659
660 RTCOfferAnswerOptions options_no_bundle;
661 options_no_bundle.use_rtp_mux = false;
662
663 // First, negotiate different SSL roles for audio and video.
664 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
665 auto answer = callee->CreateAnswer(options_no_bundle);
666
667 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
668 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
669
670 ASSERT_TRUE(
671 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
672 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
673
674 // Now create an offer in the reverse direction, and ensure the initial
675 // offerer responds with an answer with the correct SSL roles.
676 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
677 answer = caller->CreateAnswer(options_no_bundle);
678
679 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
680 AudioConnectionRole(answer->description()));
681 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
682 VideoConnectionRole(answer->description()));
683
684 ASSERT_TRUE(
685 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
686 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
687
688 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
689 // audio is transferred over to video in the answer that completes the BUNDLE
690 // negotiation.
691 RTCOfferAnswerOptions options_bundle;
692 options_bundle.use_rtp_mux = true;
693
694 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
695 answer = caller->CreateAnswer(options_bundle);
696
697 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
698 AudioConnectionRole(answer->description()));
699 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
700 VideoConnectionRole(answer->description()));
701
702 ASSERT_TRUE(
703 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
704 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
705}
706
Steve Anton80dd7b52018-02-16 17:08:42 -0800707// Tests that if the DTLS fingerprint is invalid then all future calls to
708// SetLocalDescription and SetRemoteDescription will fail due to a session
709// error.
710// This is a regression test for crbug.com/800775
711TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
712 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
713 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
714
715 auto caller = CreatePeerConnectionWithAudioVideo();
716 RTCConfiguration callee_config;
717 callee_config.enable_dtls_srtp.emplace(true);
718 callee_config.certificates.push_back(callee_certificate);
719 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
720
721 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
722
723 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 10:40:48 -0700724 auto valid_answer = callee->CreateAnswer();
725 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-16 17:08:42 -0800726 auto* audio_content =
727 cricket::GetFirstAudioContent(invalid_answer->description());
728 ASSERT_TRUE(audio_content);
729 auto* audio_transport_info =
730 invalid_answer->description()->GetTransportInfoByName(
731 audio_content->name);
732 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700733 audio_transport_info->description.identity_fingerprint =
734 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800735
736 // Set the invalid answer and expect a fingerprint error.
737 std::string error;
738 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
739 EXPECT_PRED_FORMAT2(AssertStringContains, error,
740 "Local fingerprint does not match identity.");
741
742 // Make sure that setting a valid remote offer or local answer also fails now.
743 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
744 EXPECT_PRED_FORMAT2(AssertStringContains, error,
745 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 10:40:48 -0700746 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-16 17:08:42 -0800747 EXPECT_PRED_FORMAT2(AssertStringContains, error,
748 "Session error code: ERROR_CONTENT.");
749}
750
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100751INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
752 PeerConnectionCryptoTest,
753 Values(SdpSemantics::kPlanB,
754 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 14:59:54 -0800755
Steve Anton6b63cd52017-10-06 11:20:31 -0700756} // namespace webrtc