blob: 369a909f7a375e8078db91dbf0ffb52e632dc5bf [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"
Anders Carlsson67537952018-05-03 11:28:29 +020013#include "api/video_codecs/builtin_video_decoder_factory.h"
14#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070015#include "p2p/base/fakeportallocator.h"
16#include "pc/mediasession.h"
17#include "pc/peerconnectionwrapper.h"
18#include "pc/sdputils.h"
19#ifdef WEBRTC_ANDROID
20#include "pc/test/androidtestinitializer.h"
21#endif
Karl Wiberg918f50c2018-07-05 11:40:33 +020022#include "absl/memory/memory.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070023#include "pc/test/fakeaudiocapturemodule.h"
24#include "pc/test/fakertccertificategenerator.h"
25#include "rtc_base/gunit.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070026#include "rtc_base/virtualsocketserver.h"
27
28namespace webrtc {
29
30using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 13:08:53 -070031using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 11:20:31 -070032using ::testing::Values;
33using ::testing::Combine;
34
35constexpr int kGenerateCertTimeout = 1000;
36
Steve Anton71182f42018-01-19 14:59:54 -080037class PeerConnectionCryptoBaseTest : public ::testing::Test {
Steve Anton6b63cd52017-10-06 11:20:31 -070038 protected:
39 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
40
Steve Anton71182f42018-01-19 14:59:54 -080041 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
42 : vss_(new rtc::VirtualSocketServer()),
43 main_(vss_.get()),
44 sdp_semantics_(sdp_semantics) {
Steve Anton6b63cd52017-10-06 11:20:31 -070045#ifdef WEBRTC_ANDROID
46 InitializeAndroidObjects();
47#endif
48 pc_factory_ = CreatePeerConnectionFactory(
49 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020050 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
Anders Carlsson67537952018-05-03 11:28:29 +020051 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
52 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
53 nullptr /* audio_processing */);
Steve Anton6b63cd52017-10-06 11:20:31 -070054 }
55
Steve Anton8a63f782017-10-23 13:08:53 -070056 WrapperPtr CreatePeerConnection() {
57 return CreatePeerConnection(RTCConfiguration());
58 }
59
Steve Anton6b63cd52017-10-06 11:20:31 -070060 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
61 return CreatePeerConnection(config, nullptr);
62 }
63
64 WrapperPtr CreatePeerConnection(
65 const RTCConfiguration& config,
66 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
Karl Wiberg918f50c2018-07-05 11:40:33 +020067 auto fake_port_allocator = absl::make_unique<cricket::FakePortAllocator>(
Steve Anton6b63cd52017-10-06 11:20:31 -070068 rtc::Thread::Current(), nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +020069 auto observer = absl::make_unique<MockPeerConnectionObserver>();
Steve Anton71182f42018-01-19 14:59:54 -080070 RTCConfiguration modified_config = config;
71 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -070072 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton71182f42018-01-19 14:59:54 -080073 modified_config, std::move(fake_port_allocator), std::move(cert_gen),
Steve Anton6b63cd52017-10-06 11:20:31 -070074 observer.get());
75 if (!pc) {
76 return nullptr;
77 }
78
Yves Gerey4e933292018-10-31 15:36:05 +010079 observer->SetPeerConnectionInterface(pc.get());
Karl Wiberg918f50c2018-07-05 11:40:33 +020080 return absl::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
81 std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -070082 }
83
84 // Accepts the same arguments as CreatePeerConnection and adds default audio
85 // and video tracks.
86 template <typename... Args>
87 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
88 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
89 if (!wrapper) {
90 return nullptr;
91 }
Steve Anton8d3444d2017-10-20 15:30:51 -070092 wrapper->AddAudioTrack("a");
93 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -070094 return wrapper;
95 }
96
Steve Anton8a63f782017-10-23 13:08:53 -070097 cricket::ConnectionRole& AudioConnectionRole(
98 cricket::SessionDescription* desc) {
99 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
100 }
101
102 cricket::ConnectionRole& VideoConnectionRole(
103 cricket::SessionDescription* desc) {
104 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
105 }
106
107 cricket::ConnectionRole& ConnectionRoleFromContent(
108 cricket::SessionDescription* desc,
109 cricket::ContentInfo* content) {
110 RTC_DCHECK(content);
111 auto* transport_info = desc->GetTransportInfoByName(content->name);
112 RTC_DCHECK(transport_info);
113 return transport_info->description.connection_role;
114 }
115
Steve Anton6b63cd52017-10-06 11:20:31 -0700116 std::unique_ptr<rtc::VirtualSocketServer> vss_;
117 rtc::AutoSocketServerThread main_;
118 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800119 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700120};
121
122SdpContentPredicate HaveDtlsFingerprint() {
123 return [](const cricket::ContentInfo* content,
124 const cricket::TransportInfo* transport) {
125 return transport->description.identity_fingerprint != nullptr;
126 };
127}
128
129SdpContentPredicate HaveSdesCryptos() {
130 return [](const cricket::ContentInfo* content,
131 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800132 return !content->media_description()->cryptos().empty();
Steve Anton6b63cd52017-10-06 11:20:31 -0700133 };
134}
135
136SdpContentPredicate HaveProtocol(const std::string& protocol) {
137 return [protocol](const cricket::ContentInfo* content,
138 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800139 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700140 };
141}
142
143SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
144 return [num_crypto_suites](const cricket::ContentInfo* content,
145 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800146 const auto& cryptos = content->media_description()->cryptos();
147 if (cryptos.size() != num_crypto_suites) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700148 return false;
149 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800150 const cricket::CryptoParams first_params = cryptos[0];
Steve Anton6b63cd52017-10-06 11:20:31 -0700151 return first_params.key_params.size() == 67U &&
152 first_params.cipher_suite == "AEAD_AES_256_GCM";
153 };
154}
155
Steve Anton71182f42018-01-19 14:59:54 -0800156class PeerConnectionCryptoTest
157 : public PeerConnectionCryptoBaseTest,
158 public ::testing::WithParamInterface<SdpSemantics> {
159 protected:
160 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
161};
162
Steve Anton6b63cd52017-10-06 11:20:31 -0700163SdpContentMutator RemoveSdesCryptos() {
164 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800165 content->media_description()->set_cryptos({});
Steve Anton6b63cd52017-10-06 11:20:31 -0700166 };
167}
168
169SdpContentMutator RemoveDtlsFingerprint() {
170 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
171 transport->description.identity_fingerprint.reset();
172 };
173}
174
175// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
176// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800177TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700178 RTCConfiguration config;
179 config.enable_dtls_srtp.emplace(true);
180 auto caller = CreatePeerConnectionWithAudioVideo(config);
181
182 auto offer = caller->CreateOffer();
183 ASSERT_TRUE(offer);
184
185 ASSERT_FALSE(offer->description()->contents().empty());
186 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
187 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
188 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
189 offer->description()));
190}
Steve Anton71182f42018-01-19 14:59:54 -0800191TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700192 RTCConfiguration config;
193 config.enable_dtls_srtp.emplace(true);
194 auto caller = CreatePeerConnectionWithAudioVideo(config);
195 auto callee = CreatePeerConnectionWithAudioVideo(config);
196
197 callee->SetRemoteDescription(caller->CreateOffer());
198 auto answer = callee->CreateAnswer();
199 ASSERT_TRUE(answer);
200
201 ASSERT_FALSE(answer->description()->contents().empty());
202 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
203 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
204 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
205 answer->description()));
206}
207
208// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
209// should not have a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800210TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700211 RTCConfiguration config;
212 config.enable_dtls_srtp.emplace(false);
213 auto caller = CreatePeerConnectionWithAudioVideo(config);
214
215 auto offer = caller->CreateOffer();
216 ASSERT_TRUE(offer);
217
218 ASSERT_FALSE(offer->description()->contents().empty());
219 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
220 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
221 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
222 offer->description()));
223}
Steve Anton71182f42018-01-19 14:59:54 -0800224TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700225 RTCConfiguration config;
226 config.enable_dtls_srtp.emplace(false);
227 auto caller = CreatePeerConnectionWithAudioVideo(config);
228 auto callee = CreatePeerConnectionWithAudioVideo(config);
229
230 callee->SetRemoteDescription(caller->CreateOffer());
231 auto answer = callee->CreateAnswer();
232 ASSERT_TRUE(answer);
233
234 ASSERT_FALSE(answer->description()->contents().empty());
235 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
236 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
237 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
238 answer->description()));
239}
240
241// When encryption is disabled, the SDP offer/answer should have neither a DTLS
242// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800243TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700244 PeerConnectionFactoryInterface::Options options;
245 options.disable_encryption = true;
246 pc_factory_->SetOptions(options);
247
248 RTCConfiguration config;
249 config.enable_dtls_srtp.emplace(false);
250 auto caller = CreatePeerConnectionWithAudioVideo(config);
251
252 auto offer = caller->CreateOffer();
253 ASSERT_TRUE(offer);
254
255 ASSERT_FALSE(offer->description()->contents().empty());
256 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
257 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
258 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
259 offer->description()));
260}
Steve Anton71182f42018-01-19 14:59:54 -0800261TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700262 PeerConnectionFactoryInterface::Options options;
263 options.disable_encryption = true;
264 pc_factory_->SetOptions(options);
265
266 RTCConfiguration config;
267 config.enable_dtls_srtp.emplace(false);
268 auto caller = CreatePeerConnectionWithAudioVideo(config);
269 auto callee = CreatePeerConnectionWithAudioVideo(config);
270
271 callee->SetRemoteDescription(caller->CreateOffer());
272 auto answer = callee->CreateAnswer();
273 ASSERT_TRUE(answer);
274
275 ASSERT_FALSE(answer->description()->contents().empty());
276 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
277 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
278 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
279 answer->description()));
280}
281
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700282// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
283// set in the configuration it should overrite the settings set in the factory.
284TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
285 PeerConnectionFactoryInterface::Options options;
286 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
287 pc_factory_->SetOptions(options);
288
289 RTCConfiguration config;
290 config.enable_dtls_srtp.emplace(false);
291 CryptoOptions crypto_options;
292 crypto_options.srtp.enable_gcm_crypto_suites = false;
293 config.crypto_options = crypto_options;
294 auto caller = CreatePeerConnectionWithAudioVideo(config);
295
296 auto offer = caller->CreateOffer();
297 ASSERT_TRUE(offer);
298
299 ASSERT_FALSE(offer->description()->contents().empty());
300 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
301 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
302}
303
Steve Anton6b63cd52017-10-06 11:20:31 -0700304// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
305// should have the correct ciphers in the SDES crypto options.
306// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
307// in the answer.
Steve Anton71182f42018-01-19 14:59:54 -0800308TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700309 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700310 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700311 pc_factory_->SetOptions(options);
312
313 RTCConfiguration config;
314 config.enable_dtls_srtp.emplace(false);
315 auto caller = CreatePeerConnectionWithAudioVideo(config);
316
317 auto offer = caller->CreateOffer();
318 ASSERT_TRUE(offer);
319
320 ASSERT_FALSE(offer->description()->contents().empty());
321 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
322}
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700323
Steve Anton71182f42018-01-19 14:59:54 -0800324TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700325 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700326 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700327 pc_factory_->SetOptions(options);
328
329 RTCConfiguration config;
330 config.enable_dtls_srtp.emplace(false);
331 auto caller = CreatePeerConnectionWithAudioVideo(config);
332 auto callee = CreatePeerConnectionWithAudioVideo(config);
333
334 callee->SetRemoteDescription(caller->CreateOffer());
335 auto answer = callee->CreateAnswer();
336 ASSERT_TRUE(answer);
337
338 ASSERT_FALSE(answer->description()->contents().empty());
339 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
340}
341
Steve Anton71182f42018-01-19 14:59:54 -0800342TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700343 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700344 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700345 pc_factory_->SetOptions(options);
346
347 RTCConfiguration config;
348 config.enable_dtls_srtp.emplace(false);
349 auto caller = CreatePeerConnectionWithAudioVideo(config);
350 auto callee = CreatePeerConnectionWithAudioVideo(config);
351
352 auto offer = caller->CreateOffer();
353 ASSERT_TRUE(offer);
354 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
355
356 auto answer = callee->CreateAnswer();
357 ASSERT_TRUE(answer);
358 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
359}
360
361// The following group tests that two PeerConnections can successfully exchange
362// an offer/answer when DTLS is off and that they will refuse any offer/answer
363// applied locally/remotely if it does not include SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800364TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700365 RTCConfiguration config;
366 config.enable_dtls_srtp.emplace(false);
367 auto caller = CreatePeerConnectionWithAudioVideo(config);
368 auto callee = CreatePeerConnectionWithAudioVideo(config);
369
370 auto offer = caller->CreateOfferAndSetAsLocal();
371 ASSERT_TRUE(offer);
372 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
373
374 auto answer = callee->CreateAnswerAndSetAsLocal();
375 ASSERT_TRUE(answer);
376 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
377}
Steve Anton71182f42018-01-19 14:59:54 -0800378TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700379 RTCConfiguration config;
380 config.enable_dtls_srtp.emplace(false);
381 auto caller = CreatePeerConnectionWithAudioVideo(config);
382
383 auto offer = caller->CreateOffer();
384 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
385
386 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
387}
Steve Anton71182f42018-01-19 14:59:54 -0800388TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700389 RTCConfiguration config;
390 config.enable_dtls_srtp.emplace(false);
391 auto caller = CreatePeerConnectionWithAudioVideo(config);
392 auto callee = CreatePeerConnectionWithAudioVideo(config);
393
394 auto offer = caller->CreateOffer();
395 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
396
397 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
398}
Steve Anton71182f42018-01-19 14:59:54 -0800399TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700400 RTCConfiguration config;
401 config.enable_dtls_srtp.emplace(false);
402 auto caller = CreatePeerConnectionWithAudioVideo(config);
403 auto callee = CreatePeerConnectionWithAudioVideo(config);
404
405 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
406 auto answer = callee->CreateAnswer();
407 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
408
409 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
410}
Steve Anton71182f42018-01-19 14:59:54 -0800411TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700412 RTCConfiguration config;
413 config.enable_dtls_srtp.emplace(false);
414 auto caller = CreatePeerConnectionWithAudioVideo(config);
415 auto callee = CreatePeerConnectionWithAudioVideo(config);
416
417 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
418 auto answer = callee->CreateAnswerAndSetAsLocal();
419 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
420
421 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
422}
423
424// The following group tests that two PeerConnections can successfully exchange
425// an offer/answer when DTLS is on and that they will refuse any offer/answer
426// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800427TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700428 RTCConfiguration config;
429 config.enable_dtls_srtp.emplace(true);
430 auto caller = CreatePeerConnectionWithAudioVideo(config);
431 auto callee = CreatePeerConnectionWithAudioVideo(config);
432
433 auto offer = caller->CreateOfferAndSetAsLocal();
434 ASSERT_TRUE(offer);
435 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
436
437 auto answer = callee->CreateAnswerAndSetAsLocal();
438 ASSERT_TRUE(answer);
439 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
440}
Steve Anton71182f42018-01-19 14:59:54 -0800441TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700442 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
443 RTCConfiguration config;
444 config.enable_dtls_srtp.emplace(true);
445 auto caller = CreatePeerConnectionWithAudioVideo(config);
446
447 auto offer = caller->CreateOffer();
448 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
449
450 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
451}
Steve Anton71182f42018-01-19 14:59:54 -0800452TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700453 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
454 RTCConfiguration config;
455 config.enable_dtls_srtp.emplace(true);
456 auto caller = CreatePeerConnectionWithAudioVideo(config);
457 auto callee = CreatePeerConnectionWithAudioVideo(config);
458
459 auto offer = caller->CreateOffer();
460 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
461
462 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
463}
Steve Anton71182f42018-01-19 14:59:54 -0800464TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700465 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
466 RTCConfiguration config;
467 config.enable_dtls_srtp.emplace(true);
468 auto caller = CreatePeerConnectionWithAudioVideo(config);
469 auto callee = CreatePeerConnectionWithAudioVideo(config);
470
471 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
472 auto answer = callee->CreateAnswer();
473 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
474}
Steve Anton71182f42018-01-19 14:59:54 -0800475TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700476 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
477 RTCConfiguration config;
478 config.enable_dtls_srtp.emplace(true);
479 auto caller = CreatePeerConnectionWithAudioVideo(config);
480 auto callee = CreatePeerConnectionWithAudioVideo(config);
481
482 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
483 auto answer = callee->CreateAnswerAndSetAsLocal();
484 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
485
486 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
487}
488
489// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800490TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700491 PeerConnectionFactoryInterface::Options options;
492 options.disable_encryption = true;
493 pc_factory_->SetOptions(options);
494
495 RTCConfiguration config;
496 config.enable_dtls_srtp.emplace(false);
497 auto caller = CreatePeerConnectionWithAudioVideo(config);
498 auto callee = CreatePeerConnectionWithAudioVideo(config);
499
500 auto offer = caller->CreateOfferAndSetAsLocal();
501 ASSERT_TRUE(offer);
502 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
503
504 auto answer = callee->CreateAnswerAndSetAsLocal();
505 ASSERT_TRUE(answer);
506 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
507}
508
509// Tests that a DTLS call can be established when the certificate is specified
510// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800511TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700512 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
513 RTCConfiguration caller_config;
514 caller_config.enable_dtls_srtp.emplace(true);
515 caller_config.certificates.push_back(
516 FakeRTCCertificateGenerator::GenerateCertificate());
517 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
518
519 RTCConfiguration callee_config;
520 callee_config.enable_dtls_srtp.emplace(true);
521 callee_config.certificates.push_back(
522 FakeRTCCertificateGenerator::GenerateCertificate());
523 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
524
525 auto offer = caller->CreateOfferAndSetAsLocal();
526 ASSERT_TRUE(offer);
527 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
528
529 auto answer = callee->CreateAnswerAndSetAsLocal();
530 ASSERT_TRUE(answer);
531 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
532}
533
534// The following parameterized test verifies that CreateOffer/CreateAnswer
535// returns successfully (or with failure if the underlying certificate generator
536// fails) no matter when the DTLS certificate is generated. If multiple
537// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
538// they all finish after the certificate is generated.
539
Steve Anton6b63cd52017-10-06 11:20:31 -0700540// Whether the certificate will be generated before calling CreateOffer or
541// while CreateOffer is executing.
542enum class CertGenTime { kBefore, kDuring };
543std::ostream& operator<<(std::ostream& out, CertGenTime value) {
544 switch (value) {
545 case CertGenTime::kBefore:
546 return out << "before";
547 case CertGenTime::kDuring:
548 return out << "during";
549 default:
550 return out << "unknown";
551 }
552}
553
554// Whether the fake certificate generator will produce a certificate or fail.
555enum class CertGenResult { kSucceed, kFail };
556std::ostream& operator<<(std::ostream& out, CertGenResult value) {
557 switch (value) {
558 case CertGenResult::kSucceed:
559 return out << "succeed";
560 case CertGenResult::kFail:
561 return out << "fail";
562 default:
563 return out << "unknown";
564 }
565}
566
Steve Anton71182f42018-01-19 14:59:54 -0800567class PeerConnectionCryptoDtlsCertGenTest
568 : public PeerConnectionCryptoBaseTest,
569 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
570 SdpType,
571 CertGenTime,
572 CertGenResult,
573 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700574 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800575 PeerConnectionCryptoDtlsCertGenTest()
576 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
577 sdp_type_ = std::get<1>(GetParam());
578 cert_gen_time_ = std::get<2>(GetParam());
579 cert_gen_result_ = std::get<3>(GetParam());
580 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700581 }
582
583 SdpType sdp_type_;
584 CertGenTime cert_gen_time_;
585 CertGenResult cert_gen_result_;
586 size_t concurrent_calls_;
587};
588
Steve Anton71182f42018-01-19 14:59:54 -0800589TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700590 RTCConfiguration config;
591 config.enable_dtls_srtp.emplace(true);
592 auto owned_fake_certificate_generator =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200593 absl::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700594 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
595 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
596 CertGenResult::kFail);
597 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
598 CertGenTime::kDuring);
599 WrapperPtr pc;
600 if (sdp_type_ == SdpType::kOffer) {
601 pc = CreatePeerConnectionWithAudioVideo(
602 config, std::move(owned_fake_certificate_generator));
603 } else {
604 auto caller = CreatePeerConnectionWithAudioVideo(config);
605 pc = CreatePeerConnectionWithAudioVideo(
606 config, std::move(owned_fake_certificate_generator));
607 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
608 }
609 if (cert_gen_time_ == CertGenTime::kBefore) {
610 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
611 fake_certificate_generator->generated_failures() >
612 0,
613 kGenerateCertTimeout);
614 } else {
615 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
616 fake_certificate_generator->set_should_wait(false);
617 }
618 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
619 observers;
620 for (size_t i = 0; i < concurrent_calls_; i++) {
621 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
622 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
623 observers.push_back(observer);
624 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 12:32:18 +0200625 pc->pc()->CreateOffer(observer,
626 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700627 } else {
Niels Möllerf06f9232018-08-07 12:32:18 +0200628 pc->pc()->CreateAnswer(observer,
629 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700630 }
631 }
632 for (auto& observer : observers) {
633 EXPECT_TRUE_WAIT(observer->called(), 1000);
634 if (cert_gen_result_ == CertGenResult::kSucceed) {
635 EXPECT_TRUE(observer->result());
636 } else {
637 EXPECT_FALSE(observer->result());
638 }
639 }
640}
641
642INSTANTIATE_TEST_CASE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800643 PeerConnectionCryptoTest,
644 PeerConnectionCryptoDtlsCertGenTest,
645 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
646 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700647 Values(CertGenTime::kBefore, CertGenTime::kDuring),
648 Values(CertGenResult::kSucceed, CertGenResult::kFail),
649 Values(1, 3)));
650
Steve Anton8a63f782017-10-23 13:08:53 -0700651// Test that we can create and set an answer correctly when different
652// SSL roles have been negotiated for different transports.
653// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800654TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700655 auto caller = CreatePeerConnectionWithAudioVideo();
656 auto callee = CreatePeerConnectionWithAudioVideo();
657
658 RTCOfferAnswerOptions options_no_bundle;
659 options_no_bundle.use_rtp_mux = false;
660
661 // First, negotiate different SSL roles for audio and video.
662 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
663 auto answer = callee->CreateAnswer(options_no_bundle);
664
665 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
666 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
667
668 ASSERT_TRUE(
669 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
670 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
671
672 // Now create an offer in the reverse direction, and ensure the initial
673 // offerer responds with an answer with the correct SSL roles.
674 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
675 answer = caller->CreateAnswer(options_no_bundle);
676
677 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
678 AudioConnectionRole(answer->description()));
679 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
680 VideoConnectionRole(answer->description()));
681
682 ASSERT_TRUE(
683 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
684 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
685
686 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
687 // audio is transferred over to video in the answer that completes the BUNDLE
688 // negotiation.
689 RTCOfferAnswerOptions options_bundle;
690 options_bundle.use_rtp_mux = true;
691
692 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
693 answer = caller->CreateAnswer(options_bundle);
694
695 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
696 AudioConnectionRole(answer->description()));
697 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
698 VideoConnectionRole(answer->description()));
699
700 ASSERT_TRUE(
701 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
702 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
703}
704
Steve Anton80dd7b52018-02-16 17:08:42 -0800705// Tests that if the DTLS fingerprint is invalid then all future calls to
706// SetLocalDescription and SetRemoteDescription will fail due to a session
707// error.
708// This is a regression test for crbug.com/800775
709TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
710 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
711 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
712
713 auto caller = CreatePeerConnectionWithAudioVideo();
714 RTCConfiguration callee_config;
715 callee_config.enable_dtls_srtp.emplace(true);
716 callee_config.certificates.push_back(callee_certificate);
717 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
718
719 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
720
721 // Create an invalid answer with the other certificate's fingerprint.
722 auto invalid_answer = callee->CreateAnswer();
723 auto* audio_content =
724 cricket::GetFirstAudioContent(invalid_answer->description());
725 ASSERT_TRUE(audio_content);
726 auto* audio_transport_info =
727 invalid_answer->description()->GetTransportInfoByName(
728 audio_content->name);
729 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700730 audio_transport_info->description.identity_fingerprint =
731 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800732
733 // Set the invalid answer and expect a fingerprint error.
734 std::string error;
735 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
736 EXPECT_PRED_FORMAT2(AssertStringContains, error,
737 "Local fingerprint does not match identity.");
738
739 // Make sure that setting a valid remote offer or local answer also fails now.
740 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
741 EXPECT_PRED_FORMAT2(AssertStringContains, error,
742 "Session error code: ERROR_CONTENT.");
743 ASSERT_FALSE(callee->SetLocalDescription(callee->CreateAnswer(), &error));
744 EXPECT_PRED_FORMAT2(AssertStringContains, error,
745 "Session error code: ERROR_CONTENT.");
746}
747
Steve Anton71182f42018-01-19 14:59:54 -0800748INSTANTIATE_TEST_CASE_P(PeerConnectionCryptoTest,
749 PeerConnectionCryptoTest,
750 Values(SdpSemantics::kPlanB,
751 SdpSemantics::kUnifiedPlan));
752
Steve Anton6b63cd52017-10-06 11:20:31 -0700753} // namespace webrtc