blob: 077ac36aa6161f16c0fd729472e4a5d8a568922e [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"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010013#include "api/create_peerconnection_factory.h"
Anders Carlsson67537952018-05-03 11:28:29 +020014#include "api/video_codecs/builtin_video_decoder_factory.h"
15#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton10542f22019-01-11 09:11:00 -080016#include "p2p/base/fake_port_allocator.h"
17#include "pc/media_session.h"
18#include "pc/peer_connection_wrapper.h"
19#include "pc/sdp_utils.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070020#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080021#include "pc/test/android_test_initializer.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070022#endif
Karl Wiberg918f50c2018-07-05 11:40:33 +020023#include "absl/memory/memory.h"
Steve Anton10542f22019-01-11 09:11:00 -080024#include "pc/test/fake_audio_capture_module.h"
25#include "pc/test/fake_rtc_certificate_generator.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070026#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080027#include "rtc_base/virtual_socket_server.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070028
29namespace webrtc {
30
31using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 13:08:53 -070032using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 11:20:31 -070033using ::testing::Combine;
Jonas Olssona4d87372019-07-05 19:08:33 +020034using ::testing::Values;
Steve Anton6b63cd52017-10-06 11:20:31 -070035
36constexpr int kGenerateCertTimeout = 1000;
37
Steve Anton71182f42018-01-19 14:59:54 -080038class PeerConnectionCryptoBaseTest : public ::testing::Test {
Steve Anton6b63cd52017-10-06 11:20:31 -070039 protected:
40 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
41
Steve Anton71182f42018-01-19 14:59:54 -080042 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
43 : vss_(new rtc::VirtualSocketServer()),
44 main_(vss_.get()),
45 sdp_semantics_(sdp_semantics) {
Steve Anton6b63cd52017-10-06 11:20:31 -070046#ifdef WEBRTC_ANDROID
47 InitializeAndroidObjects();
48#endif
49 pc_factory_ = CreatePeerConnectionFactory(
50 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020051 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
Anders Carlsson67537952018-05-03 11:28:29 +020052 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
53 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
54 nullptr /* audio_processing */);
Steve Anton6b63cd52017-10-06 11:20:31 -070055 }
56
Steve Anton8a63f782017-10-23 13:08:53 -070057 WrapperPtr CreatePeerConnection() {
58 return CreatePeerConnection(RTCConfiguration());
59 }
60
Steve Anton6b63cd52017-10-06 11:20:31 -070061 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
62 return CreatePeerConnection(config, nullptr);
63 }
64
65 WrapperPtr CreatePeerConnection(
66 const RTCConfiguration& config,
67 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
Karl Wiberg918f50c2018-07-05 11:40:33 +020068 auto fake_port_allocator = absl::make_unique<cricket::FakePortAllocator>(
Steve Anton6b63cd52017-10-06 11:20:31 -070069 rtc::Thread::Current(), nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +020070 auto observer = absl::make_unique<MockPeerConnectionObserver>();
Steve Anton71182f42018-01-19 14:59:54 -080071 RTCConfiguration modified_config = config;
72 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -070073 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton71182f42018-01-19 14:59:54 -080074 modified_config, std::move(fake_port_allocator), std::move(cert_gen),
Steve Anton6b63cd52017-10-06 11:20:31 -070075 observer.get());
76 if (!pc) {
77 return nullptr;
78 }
79
Yves Gerey4e933292018-10-31 15:36:05 +010080 observer->SetPeerConnectionInterface(pc.get());
Karl Wiberg918f50c2018-07-05 11:40:33 +020081 return absl::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
82 std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -070083 }
84
85 // Accepts the same arguments as CreatePeerConnection and adds default audio
86 // and video tracks.
87 template <typename... Args>
88 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
89 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
90 if (!wrapper) {
91 return nullptr;
92 }
Steve Anton8d3444d2017-10-20 15:30:51 -070093 wrapper->AddAudioTrack("a");
94 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -070095 return wrapper;
96 }
97
Steve Anton8a63f782017-10-23 13:08:53 -070098 cricket::ConnectionRole& AudioConnectionRole(
99 cricket::SessionDescription* desc) {
100 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
101 }
102
103 cricket::ConnectionRole& VideoConnectionRole(
104 cricket::SessionDescription* desc) {
105 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
106 }
107
108 cricket::ConnectionRole& ConnectionRoleFromContent(
109 cricket::SessionDescription* desc,
110 cricket::ContentInfo* content) {
111 RTC_DCHECK(content);
112 auto* transport_info = desc->GetTransportInfoByName(content->name);
113 RTC_DCHECK(transport_info);
114 return transport_info->description.connection_role;
115 }
116
Steve Anton6b63cd52017-10-06 11:20:31 -0700117 std::unique_ptr<rtc::VirtualSocketServer> vss_;
118 rtc::AutoSocketServerThread main_;
119 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800120 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700121};
122
123SdpContentPredicate HaveDtlsFingerprint() {
124 return [](const cricket::ContentInfo* content,
125 const cricket::TransportInfo* transport) {
126 return transport->description.identity_fingerprint != nullptr;
127 };
128}
129
130SdpContentPredicate HaveSdesCryptos() {
131 return [](const cricket::ContentInfo* content,
132 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800133 return !content->media_description()->cryptos().empty();
Steve Anton6b63cd52017-10-06 11:20:31 -0700134 };
135}
136
137SdpContentPredicate HaveProtocol(const std::string& protocol) {
138 return [protocol](const cricket::ContentInfo* content,
139 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800140 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700141 };
142}
143
144SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
145 return [num_crypto_suites](const cricket::ContentInfo* content,
146 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800147 const auto& cryptos = content->media_description()->cryptos();
148 if (cryptos.size() != num_crypto_suites) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700149 return false;
150 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800151 const cricket::CryptoParams first_params = cryptos[0];
Steve Anton6b63cd52017-10-06 11:20:31 -0700152 return first_params.key_params.size() == 67U &&
153 first_params.cipher_suite == "AEAD_AES_256_GCM";
154 };
155}
156
Steve Anton71182f42018-01-19 14:59:54 -0800157class PeerConnectionCryptoTest
158 : public PeerConnectionCryptoBaseTest,
159 public ::testing::WithParamInterface<SdpSemantics> {
160 protected:
161 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
162};
163
Steve Anton6b63cd52017-10-06 11:20:31 -0700164SdpContentMutator RemoveSdesCryptos() {
165 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800166 content->media_description()->set_cryptos({});
Steve Anton6b63cd52017-10-06 11:20:31 -0700167 };
168}
169
170SdpContentMutator RemoveDtlsFingerprint() {
171 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
172 transport->description.identity_fingerprint.reset();
173 };
174}
175
176// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
177// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800178TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700179 RTCConfiguration config;
180 config.enable_dtls_srtp.emplace(true);
181 auto caller = CreatePeerConnectionWithAudioVideo(config);
182
183 auto offer = caller->CreateOffer();
184 ASSERT_TRUE(offer);
185
186 ASSERT_FALSE(offer->description()->contents().empty());
187 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
188 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
189 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
190 offer->description()));
191}
Steve Anton71182f42018-01-19 14:59:54 -0800192TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700193 RTCConfiguration config;
194 config.enable_dtls_srtp.emplace(true);
195 auto caller = CreatePeerConnectionWithAudioVideo(config);
196 auto callee = CreatePeerConnectionWithAudioVideo(config);
197
198 callee->SetRemoteDescription(caller->CreateOffer());
199 auto answer = callee->CreateAnswer();
200 ASSERT_TRUE(answer);
201
202 ASSERT_FALSE(answer->description()->contents().empty());
203 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
204 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
205 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
206 answer->description()));
207}
208
209// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
210// should not have a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800211TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700212 RTCConfiguration config;
213 config.enable_dtls_srtp.emplace(false);
214 auto caller = CreatePeerConnectionWithAudioVideo(config);
215
216 auto offer = caller->CreateOffer();
217 ASSERT_TRUE(offer);
218
219 ASSERT_FALSE(offer->description()->contents().empty());
220 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
221 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
222 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
223 offer->description()));
224}
Steve Anton71182f42018-01-19 14:59:54 -0800225TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700226 RTCConfiguration config;
227 config.enable_dtls_srtp.emplace(false);
228 auto caller = CreatePeerConnectionWithAudioVideo(config);
229 auto callee = CreatePeerConnectionWithAudioVideo(config);
230
231 callee->SetRemoteDescription(caller->CreateOffer());
232 auto answer = callee->CreateAnswer();
233 ASSERT_TRUE(answer);
234
235 ASSERT_FALSE(answer->description()->contents().empty());
236 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
237 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
238 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
239 answer->description()));
240}
241
242// When encryption is disabled, the SDP offer/answer should have neither a DTLS
243// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800244TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700245 PeerConnectionFactoryInterface::Options options;
246 options.disable_encryption = true;
247 pc_factory_->SetOptions(options);
248
249 RTCConfiguration config;
250 config.enable_dtls_srtp.emplace(false);
251 auto caller = CreatePeerConnectionWithAudioVideo(config);
252
253 auto offer = caller->CreateOffer();
254 ASSERT_TRUE(offer);
255
256 ASSERT_FALSE(offer->description()->contents().empty());
257 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
258 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
259 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
260 offer->description()));
261}
Steve Anton71182f42018-01-19 14:59:54 -0800262TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700263 PeerConnectionFactoryInterface::Options options;
264 options.disable_encryption = true;
265 pc_factory_->SetOptions(options);
266
267 RTCConfiguration config;
268 config.enable_dtls_srtp.emplace(false);
269 auto caller = CreatePeerConnectionWithAudioVideo(config);
270 auto callee = CreatePeerConnectionWithAudioVideo(config);
271
272 callee->SetRemoteDescription(caller->CreateOffer());
273 auto answer = callee->CreateAnswer();
274 ASSERT_TRUE(answer);
275
276 ASSERT_FALSE(answer->description()->contents().empty());
277 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
278 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
279 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
280 answer->description()));
281}
282
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700283// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
284// set in the configuration it should overrite the settings set in the factory.
285TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
286 PeerConnectionFactoryInterface::Options options;
287 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
288 pc_factory_->SetOptions(options);
289
290 RTCConfiguration config;
291 config.enable_dtls_srtp.emplace(false);
292 CryptoOptions crypto_options;
293 crypto_options.srtp.enable_gcm_crypto_suites = false;
294 config.crypto_options = crypto_options;
295 auto caller = CreatePeerConnectionWithAudioVideo(config);
296
297 auto offer = caller->CreateOffer();
298 ASSERT_TRUE(offer);
299
300 ASSERT_FALSE(offer->description()->contents().empty());
301 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
302 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
303}
304
Steve Anton6b63cd52017-10-06 11:20:31 -0700305// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
306// should have the correct ciphers in the SDES crypto options.
307// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
308// in the answer.
Steve Anton71182f42018-01-19 14:59:54 -0800309TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700310 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700311 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700312 pc_factory_->SetOptions(options);
313
314 RTCConfiguration config;
315 config.enable_dtls_srtp.emplace(false);
316 auto caller = CreatePeerConnectionWithAudioVideo(config);
317
318 auto offer = caller->CreateOffer();
319 ASSERT_TRUE(offer);
320
321 ASSERT_FALSE(offer->description()->contents().empty());
322 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
323}
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700324
Steve Anton71182f42018-01-19 14:59:54 -0800325TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700326 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700327 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700328 pc_factory_->SetOptions(options);
329
330 RTCConfiguration config;
331 config.enable_dtls_srtp.emplace(false);
332 auto caller = CreatePeerConnectionWithAudioVideo(config);
333 auto callee = CreatePeerConnectionWithAudioVideo(config);
334
335 callee->SetRemoteDescription(caller->CreateOffer());
336 auto answer = callee->CreateAnswer();
337 ASSERT_TRUE(answer);
338
339 ASSERT_FALSE(answer->description()->contents().empty());
340 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
341}
342
Steve Anton71182f42018-01-19 14:59:54 -0800343TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700344 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700345 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700346 pc_factory_->SetOptions(options);
347
348 RTCConfiguration config;
349 config.enable_dtls_srtp.emplace(false);
350 auto caller = CreatePeerConnectionWithAudioVideo(config);
351 auto callee = CreatePeerConnectionWithAudioVideo(config);
352
353 auto offer = caller->CreateOffer();
354 ASSERT_TRUE(offer);
355 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
356
357 auto answer = callee->CreateAnswer();
358 ASSERT_TRUE(answer);
359 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
360}
361
362// The following group tests that two PeerConnections can successfully exchange
363// an offer/answer when DTLS is off and that they will refuse any offer/answer
364// applied locally/remotely if it does not include SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800365TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700366 RTCConfiguration config;
367 config.enable_dtls_srtp.emplace(false);
368 auto caller = CreatePeerConnectionWithAudioVideo(config);
369 auto callee = CreatePeerConnectionWithAudioVideo(config);
370
371 auto offer = caller->CreateOfferAndSetAsLocal();
372 ASSERT_TRUE(offer);
373 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
374
375 auto answer = callee->CreateAnswerAndSetAsLocal();
376 ASSERT_TRUE(answer);
377 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
378}
Steve Anton71182f42018-01-19 14:59:54 -0800379TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700380 RTCConfiguration config;
381 config.enable_dtls_srtp.emplace(false);
382 auto caller = CreatePeerConnectionWithAudioVideo(config);
383
384 auto offer = caller->CreateOffer();
385 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
386
387 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
388}
Steve Anton71182f42018-01-19 14:59:54 -0800389TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700390 RTCConfiguration config;
391 config.enable_dtls_srtp.emplace(false);
392 auto caller = CreatePeerConnectionWithAudioVideo(config);
393 auto callee = CreatePeerConnectionWithAudioVideo(config);
394
395 auto offer = caller->CreateOffer();
396 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
397
398 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
399}
Steve Anton71182f42018-01-19 14:59:54 -0800400TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700401 RTCConfiguration config;
402 config.enable_dtls_srtp.emplace(false);
403 auto caller = CreatePeerConnectionWithAudioVideo(config);
404 auto callee = CreatePeerConnectionWithAudioVideo(config);
405
406 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
407 auto answer = callee->CreateAnswer();
408 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
409
410 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
411}
Steve Anton71182f42018-01-19 14:59:54 -0800412TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700413 RTCConfiguration config;
414 config.enable_dtls_srtp.emplace(false);
415 auto caller = CreatePeerConnectionWithAudioVideo(config);
416 auto callee = CreatePeerConnectionWithAudioVideo(config);
417
418 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
419 auto answer = callee->CreateAnswerAndSetAsLocal();
420 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
421
422 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
423}
424
425// The following group tests that two PeerConnections can successfully exchange
426// an offer/answer when DTLS is on and that they will refuse any offer/answer
427// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800428TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700429 RTCConfiguration config;
430 config.enable_dtls_srtp.emplace(true);
431 auto caller = CreatePeerConnectionWithAudioVideo(config);
432 auto callee = CreatePeerConnectionWithAudioVideo(config);
433
434 auto offer = caller->CreateOfferAndSetAsLocal();
435 ASSERT_TRUE(offer);
436 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
437
438 auto answer = callee->CreateAnswerAndSetAsLocal();
439 ASSERT_TRUE(answer);
440 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
441}
Steve Anton71182f42018-01-19 14:59:54 -0800442TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700443 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
444 RTCConfiguration config;
445 config.enable_dtls_srtp.emplace(true);
446 auto caller = CreatePeerConnectionWithAudioVideo(config);
447
448 auto offer = caller->CreateOffer();
449 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
450
451 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
452}
Steve Anton71182f42018-01-19 14:59:54 -0800453TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700454 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
455 RTCConfiguration config;
456 config.enable_dtls_srtp.emplace(true);
457 auto caller = CreatePeerConnectionWithAudioVideo(config);
458 auto callee = CreatePeerConnectionWithAudioVideo(config);
459
460 auto offer = caller->CreateOffer();
461 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
462
463 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
464}
Steve Anton71182f42018-01-19 14:59:54 -0800465TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700466 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
467 RTCConfiguration config;
468 config.enable_dtls_srtp.emplace(true);
469 auto caller = CreatePeerConnectionWithAudioVideo(config);
470 auto callee = CreatePeerConnectionWithAudioVideo(config);
471
472 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
473 auto answer = callee->CreateAnswer();
474 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
475}
Steve Anton71182f42018-01-19 14:59:54 -0800476TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700477 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
478 RTCConfiguration config;
479 config.enable_dtls_srtp.emplace(true);
480 auto caller = CreatePeerConnectionWithAudioVideo(config);
481 auto callee = CreatePeerConnectionWithAudioVideo(config);
482
483 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
484 auto answer = callee->CreateAnswerAndSetAsLocal();
485 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
486
487 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
488}
489
490// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800491TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700492 PeerConnectionFactoryInterface::Options options;
493 options.disable_encryption = true;
494 pc_factory_->SetOptions(options);
495
496 RTCConfiguration config;
497 config.enable_dtls_srtp.emplace(false);
498 auto caller = CreatePeerConnectionWithAudioVideo(config);
499 auto callee = CreatePeerConnectionWithAudioVideo(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// Tests that a DTLS call can be established when the certificate is specified
511// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800512TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700513 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
514 RTCConfiguration caller_config;
515 caller_config.enable_dtls_srtp.emplace(true);
516 caller_config.certificates.push_back(
517 FakeRTCCertificateGenerator::GenerateCertificate());
518 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
519
520 RTCConfiguration callee_config;
521 callee_config.enable_dtls_srtp.emplace(true);
522 callee_config.certificates.push_back(
523 FakeRTCCertificateGenerator::GenerateCertificate());
524 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
525
526 auto offer = caller->CreateOfferAndSetAsLocal();
527 ASSERT_TRUE(offer);
528 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
529
530 auto answer = callee->CreateAnswerAndSetAsLocal();
531 ASSERT_TRUE(answer);
532 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
533}
534
535// The following parameterized test verifies that CreateOffer/CreateAnswer
536// returns successfully (or with failure if the underlying certificate generator
537// fails) no matter when the DTLS certificate is generated. If multiple
538// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
539// they all finish after the certificate is generated.
540
Steve Anton6b63cd52017-10-06 11:20:31 -0700541// Whether the certificate will be generated before calling CreateOffer or
542// while CreateOffer is executing.
543enum class CertGenTime { kBefore, kDuring };
544std::ostream& operator<<(std::ostream& out, CertGenTime value) {
545 switch (value) {
546 case CertGenTime::kBefore:
547 return out << "before";
548 case CertGenTime::kDuring:
549 return out << "during";
550 default:
551 return out << "unknown";
552 }
553}
554
555// Whether the fake certificate generator will produce a certificate or fail.
556enum class CertGenResult { kSucceed, kFail };
557std::ostream& operator<<(std::ostream& out, CertGenResult value) {
558 switch (value) {
559 case CertGenResult::kSucceed:
560 return out << "succeed";
561 case CertGenResult::kFail:
562 return out << "fail";
563 default:
564 return out << "unknown";
565 }
566}
567
Steve Anton71182f42018-01-19 14:59:54 -0800568class PeerConnectionCryptoDtlsCertGenTest
569 : public PeerConnectionCryptoBaseTest,
570 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
571 SdpType,
572 CertGenTime,
573 CertGenResult,
574 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700575 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800576 PeerConnectionCryptoDtlsCertGenTest()
577 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
578 sdp_type_ = std::get<1>(GetParam());
579 cert_gen_time_ = std::get<2>(GetParam());
580 cert_gen_result_ = std::get<3>(GetParam());
581 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700582 }
583
584 SdpType sdp_type_;
585 CertGenTime cert_gen_time_;
586 CertGenResult cert_gen_result_;
587 size_t concurrent_calls_;
588};
589
Steve Anton71182f42018-01-19 14:59:54 -0800590TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700591 RTCConfiguration config;
592 config.enable_dtls_srtp.emplace(true);
593 auto owned_fake_certificate_generator =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200594 absl::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700595 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
596 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
597 CertGenResult::kFail);
598 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
599 CertGenTime::kDuring);
600 WrapperPtr pc;
601 if (sdp_type_ == SdpType::kOffer) {
602 pc = CreatePeerConnectionWithAudioVideo(
603 config, std::move(owned_fake_certificate_generator));
604 } else {
605 auto caller = CreatePeerConnectionWithAudioVideo(config);
606 pc = CreatePeerConnectionWithAudioVideo(
607 config, std::move(owned_fake_certificate_generator));
608 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
609 }
610 if (cert_gen_time_ == CertGenTime::kBefore) {
611 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
612 fake_certificate_generator->generated_failures() >
613 0,
614 kGenerateCertTimeout);
615 } else {
616 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
617 fake_certificate_generator->set_should_wait(false);
618 }
619 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
620 observers;
621 for (size_t i = 0; i < concurrent_calls_; i++) {
622 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
623 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
624 observers.push_back(observer);
625 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 12:32:18 +0200626 pc->pc()->CreateOffer(observer,
627 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700628 } else {
Niels Möllerf06f9232018-08-07 12:32:18 +0200629 pc->pc()->CreateAnswer(observer,
630 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700631 }
632 }
633 for (auto& observer : observers) {
634 EXPECT_TRUE_WAIT(observer->called(), 1000);
635 if (cert_gen_result_ == CertGenResult::kSucceed) {
636 EXPECT_TRUE(observer->result());
637 } else {
638 EXPECT_FALSE(observer->result());
639 }
640 }
641}
642
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100643INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800644 PeerConnectionCryptoTest,
645 PeerConnectionCryptoDtlsCertGenTest,
646 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
647 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700648 Values(CertGenTime::kBefore, CertGenTime::kDuring),
649 Values(CertGenResult::kSucceed, CertGenResult::kFail),
650 Values(1, 3)));
651
Steve Anton8a63f782017-10-23 13:08:53 -0700652// Test that we can create and set an answer correctly when different
653// SSL roles have been negotiated for different transports.
654// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800655TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700656 auto caller = CreatePeerConnectionWithAudioVideo();
657 auto callee = CreatePeerConnectionWithAudioVideo();
658
659 RTCOfferAnswerOptions options_no_bundle;
660 options_no_bundle.use_rtp_mux = false;
661
662 // First, negotiate different SSL roles for audio and video.
663 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
664 auto answer = callee->CreateAnswer(options_no_bundle);
665
666 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
667 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
668
669 ASSERT_TRUE(
670 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
671 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
672
673 // Now create an offer in the reverse direction, and ensure the initial
674 // offerer responds with an answer with the correct SSL roles.
675 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
676 answer = caller->CreateAnswer(options_no_bundle);
677
678 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
679 AudioConnectionRole(answer->description()));
680 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
681 VideoConnectionRole(answer->description()));
682
683 ASSERT_TRUE(
684 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
685 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
686
687 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
688 // audio is transferred over to video in the answer that completes the BUNDLE
689 // negotiation.
690 RTCOfferAnswerOptions options_bundle;
691 options_bundle.use_rtp_mux = true;
692
693 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
694 answer = caller->CreateAnswer(options_bundle);
695
696 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
697 AudioConnectionRole(answer->description()));
698 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
699 VideoConnectionRole(answer->description()));
700
701 ASSERT_TRUE(
702 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
703 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
704}
705
Steve Anton80dd7b52018-02-16 17:08:42 -0800706// Tests that if the DTLS fingerprint is invalid then all future calls to
707// SetLocalDescription and SetRemoteDescription will fail due to a session
708// error.
709// This is a regression test for crbug.com/800775
710TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
711 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
712 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
713
714 auto caller = CreatePeerConnectionWithAudioVideo();
715 RTCConfiguration callee_config;
716 callee_config.enable_dtls_srtp.emplace(true);
717 callee_config.certificates.push_back(callee_certificate);
718 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
719
720 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
721
722 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 10:40:48 -0700723 auto valid_answer = callee->CreateAnswer();
724 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-16 17:08:42 -0800725 auto* audio_content =
726 cricket::GetFirstAudioContent(invalid_answer->description());
727 ASSERT_TRUE(audio_content);
728 auto* audio_transport_info =
729 invalid_answer->description()->GetTransportInfoByName(
730 audio_content->name);
731 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700732 audio_transport_info->description.identity_fingerprint =
733 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800734
735 // Set the invalid answer and expect a fingerprint error.
736 std::string error;
737 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
738 EXPECT_PRED_FORMAT2(AssertStringContains, error,
739 "Local fingerprint does not match identity.");
740
741 // Make sure that setting a valid remote offer or local answer also fails now.
742 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
743 EXPECT_PRED_FORMAT2(AssertStringContains, error,
744 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 10:40:48 -0700745 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-16 17:08:42 -0800746 EXPECT_PRED_FORMAT2(AssertStringContains, error,
747 "Session error code: ERROR_CONTENT.");
748}
749
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100750INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
751 PeerConnectionCryptoTest,
752 Values(SdpSemantics::kPlanB,
753 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 14:59:54 -0800754
Steve Anton6b63cd52017-10-06 11:20:31 -0700755} // namespace webrtc