blob: d61c41c00990a9678bd71777bdb789a5ecccb627 [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
Karl Wiberg918f50c2018-07-05 11:40:33 +020079 return absl::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
80 std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -070081 }
82
83 // Accepts the same arguments as CreatePeerConnection and adds default audio
84 // and video tracks.
85 template <typename... Args>
86 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
87 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
88 if (!wrapper) {
89 return nullptr;
90 }
Steve Anton8d3444d2017-10-20 15:30:51 -070091 wrapper->AddAudioTrack("a");
92 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -070093 return wrapper;
94 }
95
Steve Anton8a63f782017-10-23 13:08:53 -070096 cricket::ConnectionRole& AudioConnectionRole(
97 cricket::SessionDescription* desc) {
98 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
99 }
100
101 cricket::ConnectionRole& VideoConnectionRole(
102 cricket::SessionDescription* desc) {
103 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
104 }
105
106 cricket::ConnectionRole& ConnectionRoleFromContent(
107 cricket::SessionDescription* desc,
108 cricket::ContentInfo* content) {
109 RTC_DCHECK(content);
110 auto* transport_info = desc->GetTransportInfoByName(content->name);
111 RTC_DCHECK(transport_info);
112 return transport_info->description.connection_role;
113 }
114
Steve Anton6b63cd52017-10-06 11:20:31 -0700115 std::unique_ptr<rtc::VirtualSocketServer> vss_;
116 rtc::AutoSocketServerThread main_;
117 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800118 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700119};
120
121SdpContentPredicate HaveDtlsFingerprint() {
122 return [](const cricket::ContentInfo* content,
123 const cricket::TransportInfo* transport) {
124 return transport->description.identity_fingerprint != nullptr;
125 };
126}
127
128SdpContentPredicate HaveSdesCryptos() {
129 return [](const cricket::ContentInfo* content,
130 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800131 return !content->media_description()->cryptos().empty();
Steve Anton6b63cd52017-10-06 11:20:31 -0700132 };
133}
134
135SdpContentPredicate HaveProtocol(const std::string& protocol) {
136 return [protocol](const cricket::ContentInfo* content,
137 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800138 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700139 };
140}
141
142SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
143 return [num_crypto_suites](const cricket::ContentInfo* content,
144 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800145 const auto& cryptos = content->media_description()->cryptos();
146 if (cryptos.size() != num_crypto_suites) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700147 return false;
148 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800149 const cricket::CryptoParams first_params = cryptos[0];
Steve Anton6b63cd52017-10-06 11:20:31 -0700150 return first_params.key_params.size() == 67U &&
151 first_params.cipher_suite == "AEAD_AES_256_GCM";
152 };
153}
154
Steve Anton71182f42018-01-19 14:59:54 -0800155class PeerConnectionCryptoTest
156 : public PeerConnectionCryptoBaseTest,
157 public ::testing::WithParamInterface<SdpSemantics> {
158 protected:
159 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
160};
161
Steve Anton6b63cd52017-10-06 11:20:31 -0700162SdpContentMutator RemoveSdesCryptos() {
163 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800164 content->media_description()->set_cryptos({});
Steve Anton6b63cd52017-10-06 11:20:31 -0700165 };
166}
167
168SdpContentMutator RemoveDtlsFingerprint() {
169 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
170 transport->description.identity_fingerprint.reset();
171 };
172}
173
174// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
175// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800176TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700177 RTCConfiguration config;
178 config.enable_dtls_srtp.emplace(true);
179 auto caller = CreatePeerConnectionWithAudioVideo(config);
180
181 auto offer = caller->CreateOffer();
182 ASSERT_TRUE(offer);
183
184 ASSERT_FALSE(offer->description()->contents().empty());
185 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
186 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
187 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
188 offer->description()));
189}
Steve Anton71182f42018-01-19 14:59:54 -0800190TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700191 RTCConfiguration config;
192 config.enable_dtls_srtp.emplace(true);
193 auto caller = CreatePeerConnectionWithAudioVideo(config);
194 auto callee = CreatePeerConnectionWithAudioVideo(config);
195
196 callee->SetRemoteDescription(caller->CreateOffer());
197 auto answer = callee->CreateAnswer();
198 ASSERT_TRUE(answer);
199
200 ASSERT_FALSE(answer->description()->contents().empty());
201 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
202 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
203 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
204 answer->description()));
205}
206
207// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
208// should not have a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800209TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700210 RTCConfiguration config;
211 config.enable_dtls_srtp.emplace(false);
212 auto caller = CreatePeerConnectionWithAudioVideo(config);
213
214 auto offer = caller->CreateOffer();
215 ASSERT_TRUE(offer);
216
217 ASSERT_FALSE(offer->description()->contents().empty());
218 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
219 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
220 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
221 offer->description()));
222}
Steve Anton71182f42018-01-19 14:59:54 -0800223TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700224 RTCConfiguration config;
225 config.enable_dtls_srtp.emplace(false);
226 auto caller = CreatePeerConnectionWithAudioVideo(config);
227 auto callee = CreatePeerConnectionWithAudioVideo(config);
228
229 callee->SetRemoteDescription(caller->CreateOffer());
230 auto answer = callee->CreateAnswer();
231 ASSERT_TRUE(answer);
232
233 ASSERT_FALSE(answer->description()->contents().empty());
234 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
235 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
236 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
237 answer->description()));
238}
239
240// When encryption is disabled, the SDP offer/answer should have neither a DTLS
241// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800242TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700243 PeerConnectionFactoryInterface::Options options;
244 options.disable_encryption = true;
245 pc_factory_->SetOptions(options);
246
247 RTCConfiguration config;
248 config.enable_dtls_srtp.emplace(false);
249 auto caller = CreatePeerConnectionWithAudioVideo(config);
250
251 auto offer = caller->CreateOffer();
252 ASSERT_TRUE(offer);
253
254 ASSERT_FALSE(offer->description()->contents().empty());
255 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
256 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
257 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
258 offer->description()));
259}
Steve Anton71182f42018-01-19 14:59:54 -0800260TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700261 PeerConnectionFactoryInterface::Options options;
262 options.disable_encryption = true;
263 pc_factory_->SetOptions(options);
264
265 RTCConfiguration config;
266 config.enable_dtls_srtp.emplace(false);
267 auto caller = CreatePeerConnectionWithAudioVideo(config);
268 auto callee = CreatePeerConnectionWithAudioVideo(config);
269
270 callee->SetRemoteDescription(caller->CreateOffer());
271 auto answer = callee->CreateAnswer();
272 ASSERT_TRUE(answer);
273
274 ASSERT_FALSE(answer->description()->contents().empty());
275 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
276 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
277 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
278 answer->description()));
279}
280
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700281// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
282// set in the configuration it should overrite the settings set in the factory.
283TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
284 PeerConnectionFactoryInterface::Options options;
285 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
286 pc_factory_->SetOptions(options);
287
288 RTCConfiguration config;
289 config.enable_dtls_srtp.emplace(false);
290 CryptoOptions crypto_options;
291 crypto_options.srtp.enable_gcm_crypto_suites = false;
292 config.crypto_options = crypto_options;
293 auto caller = CreatePeerConnectionWithAudioVideo(config);
294
295 auto offer = caller->CreateOffer();
296 ASSERT_TRUE(offer);
297
298 ASSERT_FALSE(offer->description()->contents().empty());
299 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
300 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
301}
302
Steve Anton6b63cd52017-10-06 11:20:31 -0700303// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
304// should have the correct ciphers in the SDES crypto options.
305// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
306// in the answer.
Steve Anton71182f42018-01-19 14:59:54 -0800307TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700308 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700309 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700310 pc_factory_->SetOptions(options);
311
312 RTCConfiguration config;
313 config.enable_dtls_srtp.emplace(false);
314 auto caller = CreatePeerConnectionWithAudioVideo(config);
315
316 auto offer = caller->CreateOffer();
317 ASSERT_TRUE(offer);
318
319 ASSERT_FALSE(offer->description()->contents().empty());
320 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
321}
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700322
Steve Anton71182f42018-01-19 14:59:54 -0800323TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700324 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700325 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700326 pc_factory_->SetOptions(options);
327
328 RTCConfiguration config;
329 config.enable_dtls_srtp.emplace(false);
330 auto caller = CreatePeerConnectionWithAudioVideo(config);
331 auto callee = CreatePeerConnectionWithAudioVideo(config);
332
333 callee->SetRemoteDescription(caller->CreateOffer());
334 auto answer = callee->CreateAnswer();
335 ASSERT_TRUE(answer);
336
337 ASSERT_FALSE(answer->description()->contents().empty());
338 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
339}
340
Steve Anton71182f42018-01-19 14:59:54 -0800341TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700342 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700343 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700344 pc_factory_->SetOptions(options);
345
346 RTCConfiguration config;
347 config.enable_dtls_srtp.emplace(false);
348 auto caller = CreatePeerConnectionWithAudioVideo(config);
349 auto callee = CreatePeerConnectionWithAudioVideo(config);
350
351 auto offer = caller->CreateOffer();
352 ASSERT_TRUE(offer);
353 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
354
355 auto answer = callee->CreateAnswer();
356 ASSERT_TRUE(answer);
357 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
358}
359
360// The following group tests that two PeerConnections can successfully exchange
361// an offer/answer when DTLS is off and that they will refuse any offer/answer
362// applied locally/remotely if it does not include SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800363TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700364 RTCConfiguration config;
365 config.enable_dtls_srtp.emplace(false);
366 auto caller = CreatePeerConnectionWithAudioVideo(config);
367 auto callee = CreatePeerConnectionWithAudioVideo(config);
368
369 auto offer = caller->CreateOfferAndSetAsLocal();
370 ASSERT_TRUE(offer);
371 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
372
373 auto answer = callee->CreateAnswerAndSetAsLocal();
374 ASSERT_TRUE(answer);
375 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
376}
Steve Anton71182f42018-01-19 14:59:54 -0800377TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700378 RTCConfiguration config;
379 config.enable_dtls_srtp.emplace(false);
380 auto caller = CreatePeerConnectionWithAudioVideo(config);
381
382 auto offer = caller->CreateOffer();
383 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
384
385 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
386}
Steve Anton71182f42018-01-19 14:59:54 -0800387TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700388 RTCConfiguration config;
389 config.enable_dtls_srtp.emplace(false);
390 auto caller = CreatePeerConnectionWithAudioVideo(config);
391 auto callee = CreatePeerConnectionWithAudioVideo(config);
392
393 auto offer = caller->CreateOffer();
394 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
395
396 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
397}
Steve Anton71182f42018-01-19 14:59:54 -0800398TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700399 RTCConfiguration config;
400 config.enable_dtls_srtp.emplace(false);
401 auto caller = CreatePeerConnectionWithAudioVideo(config);
402 auto callee = CreatePeerConnectionWithAudioVideo(config);
403
404 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
405 auto answer = callee->CreateAnswer();
406 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
407
408 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
409}
Steve Anton71182f42018-01-19 14:59:54 -0800410TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700411 RTCConfiguration config;
412 config.enable_dtls_srtp.emplace(false);
413 auto caller = CreatePeerConnectionWithAudioVideo(config);
414 auto callee = CreatePeerConnectionWithAudioVideo(config);
415
416 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
417 auto answer = callee->CreateAnswerAndSetAsLocal();
418 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
419
420 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
421}
422
423// The following group tests that two PeerConnections can successfully exchange
424// an offer/answer when DTLS is on and that they will refuse any offer/answer
425// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800426TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700427 RTCConfiguration config;
428 config.enable_dtls_srtp.emplace(true);
429 auto caller = CreatePeerConnectionWithAudioVideo(config);
430 auto callee = CreatePeerConnectionWithAudioVideo(config);
431
432 auto offer = caller->CreateOfferAndSetAsLocal();
433 ASSERT_TRUE(offer);
434 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
435
436 auto answer = callee->CreateAnswerAndSetAsLocal();
437 ASSERT_TRUE(answer);
438 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
439}
Steve Anton71182f42018-01-19 14:59:54 -0800440TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700441 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
442 RTCConfiguration config;
443 config.enable_dtls_srtp.emplace(true);
444 auto caller = CreatePeerConnectionWithAudioVideo(config);
445
446 auto offer = caller->CreateOffer();
447 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
448
449 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
450}
Steve Anton71182f42018-01-19 14:59:54 -0800451TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700452 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
453 RTCConfiguration config;
454 config.enable_dtls_srtp.emplace(true);
455 auto caller = CreatePeerConnectionWithAudioVideo(config);
456 auto callee = CreatePeerConnectionWithAudioVideo(config);
457
458 auto offer = caller->CreateOffer();
459 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
460
461 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
462}
Steve Anton71182f42018-01-19 14:59:54 -0800463TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700464 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
465 RTCConfiguration config;
466 config.enable_dtls_srtp.emplace(true);
467 auto caller = CreatePeerConnectionWithAudioVideo(config);
468 auto callee = CreatePeerConnectionWithAudioVideo(config);
469
470 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
471 auto answer = callee->CreateAnswer();
472 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
473}
Steve Anton71182f42018-01-19 14:59:54 -0800474TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700475 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
476 RTCConfiguration config;
477 config.enable_dtls_srtp.emplace(true);
478 auto caller = CreatePeerConnectionWithAudioVideo(config);
479 auto callee = CreatePeerConnectionWithAudioVideo(config);
480
481 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
482 auto answer = callee->CreateAnswerAndSetAsLocal();
483 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
484
485 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
486}
487
488// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800489TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700490 PeerConnectionFactoryInterface::Options options;
491 options.disable_encryption = true;
492 pc_factory_->SetOptions(options);
493
494 RTCConfiguration config;
495 config.enable_dtls_srtp.emplace(false);
496 auto caller = CreatePeerConnectionWithAudioVideo(config);
497 auto callee = CreatePeerConnectionWithAudioVideo(config);
498
499 auto offer = caller->CreateOfferAndSetAsLocal();
500 ASSERT_TRUE(offer);
501 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
502
503 auto answer = callee->CreateAnswerAndSetAsLocal();
504 ASSERT_TRUE(answer);
505 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
506}
507
508// Tests that a DTLS call can be established when the certificate is specified
509// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800510TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700511 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
512 RTCConfiguration caller_config;
513 caller_config.enable_dtls_srtp.emplace(true);
514 caller_config.certificates.push_back(
515 FakeRTCCertificateGenerator::GenerateCertificate());
516 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
517
518 RTCConfiguration callee_config;
519 callee_config.enable_dtls_srtp.emplace(true);
520 callee_config.certificates.push_back(
521 FakeRTCCertificateGenerator::GenerateCertificate());
522 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
523
524 auto offer = caller->CreateOfferAndSetAsLocal();
525 ASSERT_TRUE(offer);
526 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
527
528 auto answer = callee->CreateAnswerAndSetAsLocal();
529 ASSERT_TRUE(answer);
530 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
531}
532
533// The following parameterized test verifies that CreateOffer/CreateAnswer
534// returns successfully (or with failure if the underlying certificate generator
535// fails) no matter when the DTLS certificate is generated. If multiple
536// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
537// they all finish after the certificate is generated.
538
Steve Anton6b63cd52017-10-06 11:20:31 -0700539// Whether the certificate will be generated before calling CreateOffer or
540// while CreateOffer is executing.
541enum class CertGenTime { kBefore, kDuring };
542std::ostream& operator<<(std::ostream& out, CertGenTime value) {
543 switch (value) {
544 case CertGenTime::kBefore:
545 return out << "before";
546 case CertGenTime::kDuring:
547 return out << "during";
548 default:
549 return out << "unknown";
550 }
551}
552
553// Whether the fake certificate generator will produce a certificate or fail.
554enum class CertGenResult { kSucceed, kFail };
555std::ostream& operator<<(std::ostream& out, CertGenResult value) {
556 switch (value) {
557 case CertGenResult::kSucceed:
558 return out << "succeed";
559 case CertGenResult::kFail:
560 return out << "fail";
561 default:
562 return out << "unknown";
563 }
564}
565
Steve Anton71182f42018-01-19 14:59:54 -0800566class PeerConnectionCryptoDtlsCertGenTest
567 : public PeerConnectionCryptoBaseTest,
568 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
569 SdpType,
570 CertGenTime,
571 CertGenResult,
572 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700573 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800574 PeerConnectionCryptoDtlsCertGenTest()
575 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
576 sdp_type_ = std::get<1>(GetParam());
577 cert_gen_time_ = std::get<2>(GetParam());
578 cert_gen_result_ = std::get<3>(GetParam());
579 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700580 }
581
582 SdpType sdp_type_;
583 CertGenTime cert_gen_time_;
584 CertGenResult cert_gen_result_;
585 size_t concurrent_calls_;
586};
587
Steve Anton71182f42018-01-19 14:59:54 -0800588TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700589 RTCConfiguration config;
590 config.enable_dtls_srtp.emplace(true);
591 auto owned_fake_certificate_generator =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200592 absl::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700593 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
594 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
595 CertGenResult::kFail);
596 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
597 CertGenTime::kDuring);
598 WrapperPtr pc;
599 if (sdp_type_ == SdpType::kOffer) {
600 pc = CreatePeerConnectionWithAudioVideo(
601 config, std::move(owned_fake_certificate_generator));
602 } else {
603 auto caller = CreatePeerConnectionWithAudioVideo(config);
604 pc = CreatePeerConnectionWithAudioVideo(
605 config, std::move(owned_fake_certificate_generator));
606 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
607 }
608 if (cert_gen_time_ == CertGenTime::kBefore) {
609 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
610 fake_certificate_generator->generated_failures() >
611 0,
612 kGenerateCertTimeout);
613 } else {
614 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
615 fake_certificate_generator->set_should_wait(false);
616 }
617 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
618 observers;
619 for (size_t i = 0; i < concurrent_calls_; i++) {
620 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
621 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
622 observers.push_back(observer);
623 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 12:32:18 +0200624 pc->pc()->CreateOffer(observer,
625 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700626 } else {
Niels Möllerf06f9232018-08-07 12:32:18 +0200627 pc->pc()->CreateAnswer(observer,
628 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700629 }
630 }
631 for (auto& observer : observers) {
632 EXPECT_TRUE_WAIT(observer->called(), 1000);
633 if (cert_gen_result_ == CertGenResult::kSucceed) {
634 EXPECT_TRUE(observer->result());
635 } else {
636 EXPECT_FALSE(observer->result());
637 }
638 }
639}
640
641INSTANTIATE_TEST_CASE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800642 PeerConnectionCryptoTest,
643 PeerConnectionCryptoDtlsCertGenTest,
644 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
645 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700646 Values(CertGenTime::kBefore, CertGenTime::kDuring),
647 Values(CertGenResult::kSucceed, CertGenResult::kFail),
648 Values(1, 3)));
649
Steve Anton8a63f782017-10-23 13:08:53 -0700650// Test that we can create and set an answer correctly when different
651// SSL roles have been negotiated for different transports.
652// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800653TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700654 auto caller = CreatePeerConnectionWithAudioVideo();
655 auto callee = CreatePeerConnectionWithAudioVideo();
656
657 RTCOfferAnswerOptions options_no_bundle;
658 options_no_bundle.use_rtp_mux = false;
659
660 // First, negotiate different SSL roles for audio and video.
661 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
662 auto answer = callee->CreateAnswer(options_no_bundle);
663
664 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
665 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
666
667 ASSERT_TRUE(
668 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
669 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
670
671 // Now create an offer in the reverse direction, and ensure the initial
672 // offerer responds with an answer with the correct SSL roles.
673 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
674 answer = caller->CreateAnswer(options_no_bundle);
675
676 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
677 AudioConnectionRole(answer->description()));
678 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
679 VideoConnectionRole(answer->description()));
680
681 ASSERT_TRUE(
682 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
683 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
684
685 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
686 // audio is transferred over to video in the answer that completes the BUNDLE
687 // negotiation.
688 RTCOfferAnswerOptions options_bundle;
689 options_bundle.use_rtp_mux = true;
690
691 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
692 answer = caller->CreateAnswer(options_bundle);
693
694 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
695 AudioConnectionRole(answer->description()));
696 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
697 VideoConnectionRole(answer->description()));
698
699 ASSERT_TRUE(
700 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
701 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
702}
703
Steve Anton80dd7b52018-02-16 17:08:42 -0800704// Tests that if the DTLS fingerprint is invalid then all future calls to
705// SetLocalDescription and SetRemoteDescription will fail due to a session
706// error.
707// This is a regression test for crbug.com/800775
708TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
709 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
710 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
711
712 auto caller = CreatePeerConnectionWithAudioVideo();
713 RTCConfiguration callee_config;
714 callee_config.enable_dtls_srtp.emplace(true);
715 callee_config.certificates.push_back(callee_certificate);
716 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
717
718 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
719
720 // Create an invalid answer with the other certificate's fingerprint.
721 auto invalid_answer = callee->CreateAnswer();
722 auto* audio_content =
723 cricket::GetFirstAudioContent(invalid_answer->description());
724 ASSERT_TRUE(audio_content);
725 auto* audio_transport_info =
726 invalid_answer->description()->GetTransportInfoByName(
727 audio_content->name);
728 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700729 audio_transport_info->description.identity_fingerprint =
730 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800731
732 // Set the invalid answer and expect a fingerprint error.
733 std::string error;
734 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
735 EXPECT_PRED_FORMAT2(AssertStringContains, error,
736 "Local fingerprint does not match identity.");
737
738 // Make sure that setting a valid remote offer or local answer also fails now.
739 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
740 EXPECT_PRED_FORMAT2(AssertStringContains, error,
741 "Session error code: ERROR_CONTENT.");
742 ASSERT_FALSE(callee->SetLocalDescription(callee->CreateAnswer(), &error));
743 EXPECT_PRED_FORMAT2(AssertStringContains, error,
744 "Session error code: ERROR_CONTENT.");
745}
746
Steve Anton71182f42018-01-19 14:59:54 -0800747INSTANTIATE_TEST_CASE_P(PeerConnectionCryptoTest,
748 PeerConnectionCryptoTest,
749 Values(SdpSemantics::kPlanB,
750 SdpSemantics::kUnifiedPlan));
751
Steve Anton6b63cd52017-10-06 11:20:31 -0700752} // namespace webrtc