blob: 507735825bbbdf783d088ed8018a6ea824022ea8 [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
22#include "pc/test/fakeaudiocapturemodule.h"
23#include "pc/test/fakertccertificategenerator.h"
24#include "rtc_base/gunit.h"
25#include "rtc_base/ptr_util.h"
26#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) {
67 auto fake_port_allocator = rtc::MakeUnique<cricket::FakePortAllocator>(
68 rtc::Thread::Current(), nullptr);
69 auto observer = rtc::MakeUnique<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
79 return rtc::MakeUnique<PeerConnectionWrapper>(pc_factory_, pc,
80 std::move(observer));
81 }
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
281// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
282// should have the correct ciphers in the SDES crypto options.
283// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
284// in the answer.
Steve Anton71182f42018-01-19 14:59:54 -0800285TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700286 PeerConnectionFactoryInterface::Options options;
287 options.crypto_options.enable_gcm_crypto_suites = true;
288 pc_factory_->SetOptions(options);
289
290 RTCConfiguration config;
291 config.enable_dtls_srtp.emplace(false);
292 auto caller = CreatePeerConnectionWithAudioVideo(config);
293
294 auto offer = caller->CreateOffer();
295 ASSERT_TRUE(offer);
296
297 ASSERT_FALSE(offer->description()->contents().empty());
298 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
299}
Steve Anton71182f42018-01-19 14:59:54 -0800300TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700301 PeerConnectionFactoryInterface::Options options;
302 options.crypto_options.enable_gcm_crypto_suites = true;
303 pc_factory_->SetOptions(options);
304
305 RTCConfiguration config;
306 config.enable_dtls_srtp.emplace(false);
307 auto caller = CreatePeerConnectionWithAudioVideo(config);
308 auto callee = CreatePeerConnectionWithAudioVideo(config);
309
310 callee->SetRemoteDescription(caller->CreateOffer());
311 auto answer = callee->CreateAnswer();
312 ASSERT_TRUE(answer);
313
314 ASSERT_FALSE(answer->description()->contents().empty());
315 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
316}
317
Steve Anton71182f42018-01-19 14:59:54 -0800318TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700319 PeerConnectionFactoryInterface::Options options;
320 options.crypto_options.enable_gcm_crypto_suites = true;
321 pc_factory_->SetOptions(options);
322
323 RTCConfiguration config;
324 config.enable_dtls_srtp.emplace(false);
325 auto caller = CreatePeerConnectionWithAudioVideo(config);
326 auto callee = CreatePeerConnectionWithAudioVideo(config);
327
328 auto offer = caller->CreateOffer();
329 ASSERT_TRUE(offer);
330 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
331
332 auto answer = callee->CreateAnswer();
333 ASSERT_TRUE(answer);
334 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
335}
336
337// The following group tests that two PeerConnections can successfully exchange
338// an offer/answer when DTLS is off and that they will refuse any offer/answer
339// applied locally/remotely if it does not include SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800340TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700341 RTCConfiguration config;
342 config.enable_dtls_srtp.emplace(false);
343 auto caller = CreatePeerConnectionWithAudioVideo(config);
344 auto callee = CreatePeerConnectionWithAudioVideo(config);
345
346 auto offer = caller->CreateOfferAndSetAsLocal();
347 ASSERT_TRUE(offer);
348 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
349
350 auto answer = callee->CreateAnswerAndSetAsLocal();
351 ASSERT_TRUE(answer);
352 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
353}
Steve Anton71182f42018-01-19 14:59:54 -0800354TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700355 RTCConfiguration config;
356 config.enable_dtls_srtp.emplace(false);
357 auto caller = CreatePeerConnectionWithAudioVideo(config);
358
359 auto offer = caller->CreateOffer();
360 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
361
362 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
363}
Steve Anton71182f42018-01-19 14:59:54 -0800364TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
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->CreateOffer();
371 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
372
373 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
374}
Steve Anton71182f42018-01-19 14:59:54 -0800375TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700376 RTCConfiguration config;
377 config.enable_dtls_srtp.emplace(false);
378 auto caller = CreatePeerConnectionWithAudioVideo(config);
379 auto callee = CreatePeerConnectionWithAudioVideo(config);
380
381 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
382 auto answer = callee->CreateAnswer();
383 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
384
385 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
386}
Steve Anton71182f42018-01-19 14:59:54 -0800387TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
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 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
394 auto answer = callee->CreateAnswerAndSetAsLocal();
395 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
396
397 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
398}
399
400// The following group tests that two PeerConnections can successfully exchange
401// an offer/answer when DTLS is on and that they will refuse any offer/answer
402// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800403TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700404 RTCConfiguration config;
405 config.enable_dtls_srtp.emplace(true);
406 auto caller = CreatePeerConnectionWithAudioVideo(config);
407 auto callee = CreatePeerConnectionWithAudioVideo(config);
408
409 auto offer = caller->CreateOfferAndSetAsLocal();
410 ASSERT_TRUE(offer);
411 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
412
413 auto answer = callee->CreateAnswerAndSetAsLocal();
414 ASSERT_TRUE(answer);
415 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
416}
Steve Anton71182f42018-01-19 14:59:54 -0800417TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700418 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
419 RTCConfiguration config;
420 config.enable_dtls_srtp.emplace(true);
421 auto caller = CreatePeerConnectionWithAudioVideo(config);
422
423 auto offer = caller->CreateOffer();
424 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
425
426 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
427}
Steve Anton71182f42018-01-19 14:59:54 -0800428TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700429 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
430 RTCConfiguration config;
431 config.enable_dtls_srtp.emplace(true);
432 auto caller = CreatePeerConnectionWithAudioVideo(config);
433 auto callee = CreatePeerConnectionWithAudioVideo(config);
434
435 auto offer = caller->CreateOffer();
436 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
437
438 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
439}
Steve Anton71182f42018-01-19 14:59:54 -0800440TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700441 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
442 RTCConfiguration config;
443 config.enable_dtls_srtp.emplace(true);
444 auto caller = CreatePeerConnectionWithAudioVideo(config);
445 auto callee = CreatePeerConnectionWithAudioVideo(config);
446
447 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
448 auto answer = callee->CreateAnswer();
449 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
450}
Steve Anton71182f42018-01-19 14:59:54 -0800451TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700452 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
453 RTCConfiguration config;
454 config.enable_dtls_srtp.emplace(true);
455 auto caller = CreatePeerConnectionWithAudioVideo(config);
456 auto callee = CreatePeerConnectionWithAudioVideo(config);
457
458 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
459 auto answer = callee->CreateAnswerAndSetAsLocal();
460 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
461
462 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
463}
464
465// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800466TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700467 PeerConnectionFactoryInterface::Options options;
468 options.disable_encryption = true;
469 pc_factory_->SetOptions(options);
470
471 RTCConfiguration config;
472 config.enable_dtls_srtp.emplace(false);
473 auto caller = CreatePeerConnectionWithAudioVideo(config);
474 auto callee = CreatePeerConnectionWithAudioVideo(config);
475
476 auto offer = caller->CreateOfferAndSetAsLocal();
477 ASSERT_TRUE(offer);
478 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
479
480 auto answer = callee->CreateAnswerAndSetAsLocal();
481 ASSERT_TRUE(answer);
482 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
483}
484
485// Tests that a DTLS call can be established when the certificate is specified
486// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800487TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700488 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
489 RTCConfiguration caller_config;
490 caller_config.enable_dtls_srtp.emplace(true);
491 caller_config.certificates.push_back(
492 FakeRTCCertificateGenerator::GenerateCertificate());
493 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
494
495 RTCConfiguration callee_config;
496 callee_config.enable_dtls_srtp.emplace(true);
497 callee_config.certificates.push_back(
498 FakeRTCCertificateGenerator::GenerateCertificate());
499 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
500
501 auto offer = caller->CreateOfferAndSetAsLocal();
502 ASSERT_TRUE(offer);
503 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
504
505 auto answer = callee->CreateAnswerAndSetAsLocal();
506 ASSERT_TRUE(answer);
507 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
508}
509
510// The following parameterized test verifies that CreateOffer/CreateAnswer
511// returns successfully (or with failure if the underlying certificate generator
512// fails) no matter when the DTLS certificate is generated. If multiple
513// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
514// they all finish after the certificate is generated.
515
Steve Anton6b63cd52017-10-06 11:20:31 -0700516// Whether the certificate will be generated before calling CreateOffer or
517// while CreateOffer is executing.
518enum class CertGenTime { kBefore, kDuring };
519std::ostream& operator<<(std::ostream& out, CertGenTime value) {
520 switch (value) {
521 case CertGenTime::kBefore:
522 return out << "before";
523 case CertGenTime::kDuring:
524 return out << "during";
525 default:
526 return out << "unknown";
527 }
528}
529
530// Whether the fake certificate generator will produce a certificate or fail.
531enum class CertGenResult { kSucceed, kFail };
532std::ostream& operator<<(std::ostream& out, CertGenResult value) {
533 switch (value) {
534 case CertGenResult::kSucceed:
535 return out << "succeed";
536 case CertGenResult::kFail:
537 return out << "fail";
538 default:
539 return out << "unknown";
540 }
541}
542
Steve Anton71182f42018-01-19 14:59:54 -0800543class PeerConnectionCryptoDtlsCertGenTest
544 : public PeerConnectionCryptoBaseTest,
545 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
546 SdpType,
547 CertGenTime,
548 CertGenResult,
549 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700550 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800551 PeerConnectionCryptoDtlsCertGenTest()
552 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
553 sdp_type_ = std::get<1>(GetParam());
554 cert_gen_time_ = std::get<2>(GetParam());
555 cert_gen_result_ = std::get<3>(GetParam());
556 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700557 }
558
559 SdpType sdp_type_;
560 CertGenTime cert_gen_time_;
561 CertGenResult cert_gen_result_;
562 size_t concurrent_calls_;
563};
564
Steve Anton71182f42018-01-19 14:59:54 -0800565TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700566 RTCConfiguration config;
567 config.enable_dtls_srtp.emplace(true);
568 auto owned_fake_certificate_generator =
569 rtc::MakeUnique<FakeRTCCertificateGenerator>();
570 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
571 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
572 CertGenResult::kFail);
573 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
574 CertGenTime::kDuring);
575 WrapperPtr pc;
576 if (sdp_type_ == SdpType::kOffer) {
577 pc = CreatePeerConnectionWithAudioVideo(
578 config, std::move(owned_fake_certificate_generator));
579 } else {
580 auto caller = CreatePeerConnectionWithAudioVideo(config);
581 pc = CreatePeerConnectionWithAudioVideo(
582 config, std::move(owned_fake_certificate_generator));
583 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
584 }
585 if (cert_gen_time_ == CertGenTime::kBefore) {
586 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
587 fake_certificate_generator->generated_failures() >
588 0,
589 kGenerateCertTimeout);
590 } else {
591 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
592 fake_certificate_generator->set_should_wait(false);
593 }
594 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
595 observers;
596 for (size_t i = 0; i < concurrent_calls_; i++) {
597 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
598 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
599 observers.push_back(observer);
600 if (sdp_type_ == SdpType::kOffer) {
601 pc->pc()->CreateOffer(observer, nullptr);
602 } else {
603 pc->pc()->CreateAnswer(observer, nullptr);
604 }
605 }
606 for (auto& observer : observers) {
607 EXPECT_TRUE_WAIT(observer->called(), 1000);
608 if (cert_gen_result_ == CertGenResult::kSucceed) {
609 EXPECT_TRUE(observer->result());
610 } else {
611 EXPECT_FALSE(observer->result());
612 }
613 }
614}
615
616INSTANTIATE_TEST_CASE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800617 PeerConnectionCryptoTest,
618 PeerConnectionCryptoDtlsCertGenTest,
619 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
620 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700621 Values(CertGenTime::kBefore, CertGenTime::kDuring),
622 Values(CertGenResult::kSucceed, CertGenResult::kFail),
623 Values(1, 3)));
624
Steve Anton8a63f782017-10-23 13:08:53 -0700625// Test that we can create and set an answer correctly when different
626// SSL roles have been negotiated for different transports.
627// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800628TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700629 auto caller = CreatePeerConnectionWithAudioVideo();
630 auto callee = CreatePeerConnectionWithAudioVideo();
631
632 RTCOfferAnswerOptions options_no_bundle;
633 options_no_bundle.use_rtp_mux = false;
634
635 // First, negotiate different SSL roles for audio and video.
636 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
637 auto answer = callee->CreateAnswer(options_no_bundle);
638
639 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
640 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
641
642 ASSERT_TRUE(
643 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
644 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
645
646 // Now create an offer in the reverse direction, and ensure the initial
647 // offerer responds with an answer with the correct SSL roles.
648 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
649 answer = caller->CreateAnswer(options_no_bundle);
650
651 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
652 AudioConnectionRole(answer->description()));
653 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
654 VideoConnectionRole(answer->description()));
655
656 ASSERT_TRUE(
657 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
658 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
659
660 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
661 // audio is transferred over to video in the answer that completes the BUNDLE
662 // negotiation.
663 RTCOfferAnswerOptions options_bundle;
664 options_bundle.use_rtp_mux = true;
665
666 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
667 answer = caller->CreateAnswer(options_bundle);
668
669 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
670 AudioConnectionRole(answer->description()));
671 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
672 VideoConnectionRole(answer->description()));
673
674 ASSERT_TRUE(
675 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
676 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
677}
678
Steve Anton80dd7b52018-02-16 17:08:42 -0800679// Tests that if the DTLS fingerprint is invalid then all future calls to
680// SetLocalDescription and SetRemoteDescription will fail due to a session
681// error.
682// This is a regression test for crbug.com/800775
683TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
684 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
685 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
686
687 auto caller = CreatePeerConnectionWithAudioVideo();
688 RTCConfiguration callee_config;
689 callee_config.enable_dtls_srtp.emplace(true);
690 callee_config.certificates.push_back(callee_certificate);
691 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
692
693 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
694
695 // Create an invalid answer with the other certificate's fingerprint.
696 auto invalid_answer = callee->CreateAnswer();
697 auto* audio_content =
698 cricket::GetFirstAudioContent(invalid_answer->description());
699 ASSERT_TRUE(audio_content);
700 auto* audio_transport_info =
701 invalid_answer->description()->GetTransportInfoByName(
702 audio_content->name);
703 ASSERT_TRUE(audio_transport_info);
704 audio_transport_info->description.identity_fingerprint.reset(
705 rtc::SSLFingerprint::CreateFromCertificate(other_certificate));
706
707 // Set the invalid answer and expect a fingerprint error.
708 std::string error;
709 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
710 EXPECT_PRED_FORMAT2(AssertStringContains, error,
711 "Local fingerprint does not match identity.");
712
713 // Make sure that setting a valid remote offer or local answer also fails now.
714 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
715 EXPECT_PRED_FORMAT2(AssertStringContains, error,
716 "Session error code: ERROR_CONTENT.");
717 ASSERT_FALSE(callee->SetLocalDescription(callee->CreateAnswer(), &error));
718 EXPECT_PRED_FORMAT2(AssertStringContains, error,
719 "Session error code: ERROR_CONTENT.");
720}
721
Steve Anton71182f42018-01-19 14:59:54 -0800722INSTANTIATE_TEST_CASE_P(PeerConnectionCryptoTest,
723 PeerConnectionCryptoTest,
724 Values(SdpSemantics::kPlanB,
725 SdpSemantics::kUnifiedPlan));
726
Steve Anton6b63cd52017-10-06 11:20:31 -0700727} // namespace webrtc