blob: 99eb5cd7ac8ed08016cd3caea3ba09cd8bbe6feb [file] [log] [blame]
Steve Anton6b63cd52017-10-06 18:20:311/*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei317a1f02019-09-17 15:06:1811#include <memory>
12
Karl Wiberg1b0eae32017-10-17 12:48:5413#include "api/audio_codecs/builtin_audio_decoder_factory.h"
14#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Mirko Bonadei2ff3f492018-11-22 08:00:1315#include "api/create_peerconnection_factory.h"
Anders Carlsson67537952018-05-03 09:28:2916#include "api/video_codecs/builtin_video_decoder_factory.h"
17#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton10542f22019-01-11 17:11:0018#include "p2p/base/fake_port_allocator.h"
19#include "pc/media_session.h"
20#include "pc/peer_connection_wrapper.h"
21#include "pc/sdp_utils.h"
Steve Anton6b63cd52017-10-06 18:20:3122#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 17:11:0023#include "pc/test/android_test_initializer.h"
Steve Anton6b63cd52017-10-06 18:20:3124#endif
Steve Anton10542f22019-01-11 17:11:0025#include "pc/test/fake_audio_capture_module.h"
26#include "pc/test/fake_rtc_certificate_generator.h"
Steve Anton6b63cd52017-10-06 18:20:3127#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 17:11:0028#include "rtc_base/virtual_socket_server.h"
Steve Anton6b63cd52017-10-06 18:20:3129
30namespace webrtc {
31
32using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 20:08:5333using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 18:20:3134using ::testing::Combine;
Jonas Olssona4d87372019-07-05 17:08:3335using ::testing::Values;
Steve Anton6b63cd52017-10-06 18:20:3136
37constexpr int kGenerateCertTimeout = 1000;
38
Steve Anton71182f42018-01-19 22:59:5439class PeerConnectionCryptoBaseTest : public ::testing::Test {
Steve Anton6b63cd52017-10-06 18:20:3140 protected:
41 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
42
Steve Anton71182f42018-01-19 22:59:5443 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
44 : vss_(new rtc::VirtualSocketServer()),
45 main_(vss_.get()),
46 sdp_semantics_(sdp_semantics) {
Steve Anton6b63cd52017-10-06 18:20:3147#ifdef WEBRTC_ANDROID
48 InitializeAndroidObjects();
49#endif
50 pc_factory_ = CreatePeerConnectionFactory(
51 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 12:48:5452 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
Anders Carlsson67537952018-05-03 09:28:2953 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
54 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
55 nullptr /* audio_processing */);
Steve Anton6b63cd52017-10-06 18:20:3156 }
57
Steve Anton8a63f782017-10-23 20:08:5358 WrapperPtr CreatePeerConnection() {
59 return CreatePeerConnection(RTCConfiguration());
60 }
61
Steve Anton6b63cd52017-10-06 18:20:3162 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
63 return CreatePeerConnection(config, nullptr);
64 }
65
66 WrapperPtr CreatePeerConnection(
67 const RTCConfiguration& config,
68 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
Mirko Bonadei317a1f02019-09-17 15:06:1869 auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
Steve Anton6b63cd52017-10-06 18:20:3170 rtc::Thread::Current(), nullptr);
Mirko Bonadei317a1f02019-09-17 15:06:1871 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton71182f42018-01-19 22:59:5472 RTCConfiguration modified_config = config;
73 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6b63cd52017-10-06 18:20:3174 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton71182f42018-01-19 22:59:5475 modified_config, std::move(fake_port_allocator), std::move(cert_gen),
Steve Anton6b63cd52017-10-06 18:20:3176 observer.get());
77 if (!pc) {
78 return nullptr;
79 }
80
Yves Gerey4e933292018-10-31 14:36:0581 observer->SetPeerConnectionInterface(pc.get());
Mirko Bonadei317a1f02019-09-17 15:06:1882 return std::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
83 std::move(observer));
Steve Anton6b63cd52017-10-06 18:20:3184 }
85
86 // Accepts the same arguments as CreatePeerConnection and adds default audio
87 // and video tracks.
88 template <typename... Args>
89 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
90 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
91 if (!wrapper) {
92 return nullptr;
93 }
Steve Anton8d3444d2017-10-20 22:30:5194 wrapper->AddAudioTrack("a");
95 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 18:20:3196 return wrapper;
97 }
98
Steve Anton8a63f782017-10-23 20:08:5399 cricket::ConnectionRole& AudioConnectionRole(
100 cricket::SessionDescription* desc) {
101 return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
102 }
103
104 cricket::ConnectionRole& VideoConnectionRole(
105 cricket::SessionDescription* desc) {
106 return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
107 }
108
109 cricket::ConnectionRole& ConnectionRoleFromContent(
110 cricket::SessionDescription* desc,
111 cricket::ContentInfo* content) {
112 RTC_DCHECK(content);
113 auto* transport_info = desc->GetTransportInfoByName(content->name);
114 RTC_DCHECK(transport_info);
115 return transport_info->description.connection_role;
116 }
117
Steve Anton6b63cd52017-10-06 18:20:31118 std::unique_ptr<rtc::VirtualSocketServer> vss_;
119 rtc::AutoSocketServerThread main_;
120 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 22:59:54121 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 18:20:31122};
123
124SdpContentPredicate HaveDtlsFingerprint() {
125 return [](const cricket::ContentInfo* content,
126 const cricket::TransportInfo* transport) {
127 return transport->description.identity_fingerprint != nullptr;
128 };
129}
130
131SdpContentPredicate HaveSdesCryptos() {
132 return [](const cricket::ContentInfo* content,
133 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 23:14:30134 return !content->media_description()->cryptos().empty();
Steve Anton6b63cd52017-10-06 18:20:31135 };
136}
137
138SdpContentPredicate HaveProtocol(const std::string& protocol) {
139 return [protocol](const cricket::ContentInfo* content,
140 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 23:14:30141 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 18:20:31142 };
143}
144
145SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
146 return [num_crypto_suites](const cricket::ContentInfo* content,
147 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 23:14:30148 const auto& cryptos = content->media_description()->cryptos();
149 if (cryptos.size() != num_crypto_suites) {
Steve Anton6b63cd52017-10-06 18:20:31150 return false;
151 }
Steve Antonb1c1de12017-12-21 23:14:30152 const cricket::CryptoParams first_params = cryptos[0];
Steve Anton6b63cd52017-10-06 18:20:31153 return first_params.key_params.size() == 67U &&
154 first_params.cipher_suite == "AEAD_AES_256_GCM";
155 };
156}
157
Steve Anton71182f42018-01-19 22:59:54158class PeerConnectionCryptoTest
159 : public PeerConnectionCryptoBaseTest,
160 public ::testing::WithParamInterface<SdpSemantics> {
161 protected:
162 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
163};
164
Steve Anton6b63cd52017-10-06 18:20:31165SdpContentMutator RemoveSdesCryptos() {
166 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 23:14:30167 content->media_description()->set_cryptos({});
Steve Anton6b63cd52017-10-06 18:20:31168 };
169}
170
171SdpContentMutator RemoveDtlsFingerprint() {
172 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
173 transport->description.identity_fingerprint.reset();
174 };
175}
176
177// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
178// no SDES cryptos.
Steve Anton71182f42018-01-19 22:59:54179TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 18:20:31180 RTCConfiguration config;
181 config.enable_dtls_srtp.emplace(true);
182 auto caller = CreatePeerConnectionWithAudioVideo(config);
183
184 auto offer = caller->CreateOffer();
185 ASSERT_TRUE(offer);
186
187 ASSERT_FALSE(offer->description()->contents().empty());
188 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
189 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
190 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
191 offer->description()));
192}
Steve Anton71182f42018-01-19 22:59:54193TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 18:20:31194 RTCConfiguration config;
195 config.enable_dtls_srtp.emplace(true);
196 auto caller = CreatePeerConnectionWithAudioVideo(config);
197 auto callee = CreatePeerConnectionWithAudioVideo(config);
198
199 callee->SetRemoteDescription(caller->CreateOffer());
200 auto answer = callee->CreateAnswer();
201 ASSERT_TRUE(answer);
202
203 ASSERT_FALSE(answer->description()->contents().empty());
204 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
205 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
206 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
207 answer->description()));
208}
209
210// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
211// should not have a DTLS fingerprint.
Steve Anton71182f42018-01-19 22:59:54212TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 18:20:31213 RTCConfiguration config;
214 config.enable_dtls_srtp.emplace(false);
215 auto caller = CreatePeerConnectionWithAudioVideo(config);
216
217 auto offer = caller->CreateOffer();
218 ASSERT_TRUE(offer);
219
220 ASSERT_FALSE(offer->description()->contents().empty());
221 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
222 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
223 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
224 offer->description()));
225}
Steve Anton71182f42018-01-19 22:59:54226TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 18:20:31227 RTCConfiguration config;
228 config.enable_dtls_srtp.emplace(false);
229 auto caller = CreatePeerConnectionWithAudioVideo(config);
230 auto callee = CreatePeerConnectionWithAudioVideo(config);
231
232 callee->SetRemoteDescription(caller->CreateOffer());
233 auto answer = callee->CreateAnswer();
234 ASSERT_TRUE(answer);
235
236 ASSERT_FALSE(answer->description()->contents().empty());
237 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
238 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
239 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
240 answer->description()));
241}
242
243// When encryption is disabled, the SDP offer/answer should have neither a DTLS
244// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 22:59:54245TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 18:20:31246 PeerConnectionFactoryInterface::Options options;
247 options.disable_encryption = true;
248 pc_factory_->SetOptions(options);
249
250 RTCConfiguration config;
251 config.enable_dtls_srtp.emplace(false);
252 auto caller = CreatePeerConnectionWithAudioVideo(config);
253
254 auto offer = caller->CreateOffer();
255 ASSERT_TRUE(offer);
256
257 ASSERT_FALSE(offer->description()->contents().empty());
258 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
259 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
260 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
261 offer->description()));
262}
Steve Anton71182f42018-01-19 22:59:54263TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 18:20:31264 PeerConnectionFactoryInterface::Options options;
265 options.disable_encryption = true;
266 pc_factory_->SetOptions(options);
267
268 RTCConfiguration config;
269 config.enable_dtls_srtp.emplace(false);
270 auto caller = CreatePeerConnectionWithAudioVideo(config);
271 auto callee = CreatePeerConnectionWithAudioVideo(config);
272
273 callee->SetRemoteDescription(caller->CreateOffer());
274 auto answer = callee->CreateAnswer();
275 ASSERT_TRUE(answer);
276
277 ASSERT_FALSE(answer->description()->contents().empty());
278 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
279 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
280 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
281 answer->description()));
282}
283
Benjamin Wright8c27cca2018-10-25 17:16:44284// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
285// set in the configuration it should overrite the settings set in the factory.
286TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
287 PeerConnectionFactoryInterface::Options options;
288 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
289 pc_factory_->SetOptions(options);
290
291 RTCConfiguration config;
292 config.enable_dtls_srtp.emplace(false);
293 CryptoOptions crypto_options;
294 crypto_options.srtp.enable_gcm_crypto_suites = false;
295 config.crypto_options = crypto_options;
296 auto caller = CreatePeerConnectionWithAudioVideo(config);
297
298 auto offer = caller->CreateOffer();
299 ASSERT_TRUE(offer);
300
301 ASSERT_FALSE(offer->description()->contents().empty());
302 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
303 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
304}
305
Steve Anton6b63cd52017-10-06 18:20:31306// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
307// should have the correct ciphers in the SDES crypto options.
308// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
309// in the answer.
Steve Anton71182f42018-01-19 22:59:54310TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 18:20:31311 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 22:33:17312 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 18:20:31313 pc_factory_->SetOptions(options);
314
315 RTCConfiguration config;
316 config.enable_dtls_srtp.emplace(false);
317 auto caller = CreatePeerConnectionWithAudioVideo(config);
318
319 auto offer = caller->CreateOffer();
320 ASSERT_TRUE(offer);
321
322 ASSERT_FALSE(offer->description()->contents().empty());
323 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
324}
Benjamin Wright8c27cca2018-10-25 17:16:44325
Steve Anton71182f42018-01-19 22:59:54326TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 18:20:31327 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 22:33:17328 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 18:20:31329 pc_factory_->SetOptions(options);
330
331 RTCConfiguration config;
332 config.enable_dtls_srtp.emplace(false);
333 auto caller = CreatePeerConnectionWithAudioVideo(config);
334 auto callee = CreatePeerConnectionWithAudioVideo(config);
335
336 callee->SetRemoteDescription(caller->CreateOffer());
337 auto answer = callee->CreateAnswer();
338 ASSERT_TRUE(answer);
339
340 ASSERT_FALSE(answer->description()->contents().empty());
341 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
342}
343
Steve Anton71182f42018-01-19 22:59:54344TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 18:20:31345 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 22:33:17346 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 18:20:31347 pc_factory_->SetOptions(options);
348
349 RTCConfiguration config;
350 config.enable_dtls_srtp.emplace(false);
351 auto caller = CreatePeerConnectionWithAudioVideo(config);
352 auto callee = CreatePeerConnectionWithAudioVideo(config);
353
354 auto offer = caller->CreateOffer();
355 ASSERT_TRUE(offer);
356 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
357
358 auto answer = callee->CreateAnswer();
359 ASSERT_TRUE(answer);
360 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
361}
362
363// The following group tests that two PeerConnections can successfully exchange
364// an offer/answer when DTLS is off and that they will refuse any offer/answer
365// applied locally/remotely if it does not include SDES cryptos.
Steve Anton71182f42018-01-19 22:59:54366TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
Steve Anton6b63cd52017-10-06 18:20:31367 RTCConfiguration config;
368 config.enable_dtls_srtp.emplace(false);
369 auto caller = CreatePeerConnectionWithAudioVideo(config);
370 auto callee = CreatePeerConnectionWithAudioVideo(config);
371
372 auto offer = caller->CreateOfferAndSetAsLocal();
373 ASSERT_TRUE(offer);
374 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
375
376 auto answer = callee->CreateAnswerAndSetAsLocal();
377 ASSERT_TRUE(answer);
378 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
379}
Steve Anton71182f42018-01-19 22:59:54380TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 18:20:31381 RTCConfiguration config;
382 config.enable_dtls_srtp.emplace(false);
383 auto caller = CreatePeerConnectionWithAudioVideo(config);
384
385 auto offer = caller->CreateOffer();
386 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
387
388 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
389}
Steve Anton71182f42018-01-19 22:59:54390TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 18:20:31391 RTCConfiguration config;
392 config.enable_dtls_srtp.emplace(false);
393 auto caller = CreatePeerConnectionWithAudioVideo(config);
394 auto callee = CreatePeerConnectionWithAudioVideo(config);
395
396 auto offer = caller->CreateOffer();
397 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
398
399 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
400}
Steve Anton71182f42018-01-19 22:59:54401TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 18:20:31402 RTCConfiguration config;
403 config.enable_dtls_srtp.emplace(false);
404 auto caller = CreatePeerConnectionWithAudioVideo(config);
405 auto callee = CreatePeerConnectionWithAudioVideo(config);
406
407 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
408 auto answer = callee->CreateAnswer();
409 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
410
411 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
412}
Steve Anton71182f42018-01-19 22:59:54413TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 18:20:31414 RTCConfiguration config;
415 config.enable_dtls_srtp.emplace(false);
416 auto caller = CreatePeerConnectionWithAudioVideo(config);
417 auto callee = CreatePeerConnectionWithAudioVideo(config);
418
419 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
420 auto answer = callee->CreateAnswerAndSetAsLocal();
421 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
422
423 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
424}
425
426// The following group tests that two PeerConnections can successfully exchange
427// an offer/answer when DTLS is on and that they will refuse any offer/answer
428// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 22:59:54429TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 18:20:31430 RTCConfiguration config;
431 config.enable_dtls_srtp.emplace(true);
432 auto caller = CreatePeerConnectionWithAudioVideo(config);
433 auto callee = CreatePeerConnectionWithAudioVideo(config);
434
435 auto offer = caller->CreateOfferAndSetAsLocal();
436 ASSERT_TRUE(offer);
437 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
438
439 auto answer = callee->CreateAnswerAndSetAsLocal();
440 ASSERT_TRUE(answer);
441 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
442}
Steve Anton71182f42018-01-19 22:59:54443TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 18:20:31444 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
445 RTCConfiguration config;
446 config.enable_dtls_srtp.emplace(true);
447 auto caller = CreatePeerConnectionWithAudioVideo(config);
448
449 auto offer = caller->CreateOffer();
450 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
451
452 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
453}
Steve Anton71182f42018-01-19 22:59:54454TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 18:20:31455 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
456 RTCConfiguration config;
457 config.enable_dtls_srtp.emplace(true);
458 auto caller = CreatePeerConnectionWithAudioVideo(config);
459 auto callee = CreatePeerConnectionWithAudioVideo(config);
460
461 auto offer = caller->CreateOffer();
462 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
463
464 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
465}
Steve Anton71182f42018-01-19 22:59:54466TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 18:20:31467 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
468 RTCConfiguration config;
469 config.enable_dtls_srtp.emplace(true);
470 auto caller = CreatePeerConnectionWithAudioVideo(config);
471 auto callee = CreatePeerConnectionWithAudioVideo(config);
472
473 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
474 auto answer = callee->CreateAnswer();
475 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
476}
Steve Anton71182f42018-01-19 22:59:54477TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 18:20:31478 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
479 RTCConfiguration config;
480 config.enable_dtls_srtp.emplace(true);
481 auto caller = CreatePeerConnectionWithAudioVideo(config);
482 auto callee = CreatePeerConnectionWithAudioVideo(config);
483
484 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
485 auto answer = callee->CreateAnswerAndSetAsLocal();
486 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
487
488 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
489}
490
491// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 22:59:54492TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 18:20:31493 PeerConnectionFactoryInterface::Options options;
494 options.disable_encryption = true;
495 pc_factory_->SetOptions(options);
496
497 RTCConfiguration config;
498 config.enable_dtls_srtp.emplace(false);
499 auto caller = CreatePeerConnectionWithAudioVideo(config);
500 auto callee = CreatePeerConnectionWithAudioVideo(config);
501
502 auto offer = caller->CreateOfferAndSetAsLocal();
503 ASSERT_TRUE(offer);
504 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
505
506 auto answer = callee->CreateAnswerAndSetAsLocal();
507 ASSERT_TRUE(answer);
508 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
509}
510
511// Tests that a DTLS call can be established when the certificate is specified
512// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 22:59:54513TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 18:20:31514 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
515 RTCConfiguration caller_config;
516 caller_config.enable_dtls_srtp.emplace(true);
517 caller_config.certificates.push_back(
518 FakeRTCCertificateGenerator::GenerateCertificate());
519 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
520
521 RTCConfiguration callee_config;
522 callee_config.enable_dtls_srtp.emplace(true);
523 callee_config.certificates.push_back(
524 FakeRTCCertificateGenerator::GenerateCertificate());
525 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
526
527 auto offer = caller->CreateOfferAndSetAsLocal();
528 ASSERT_TRUE(offer);
529 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
530
531 auto answer = callee->CreateAnswerAndSetAsLocal();
532 ASSERT_TRUE(answer);
533 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
534}
535
536// The following parameterized test verifies that CreateOffer/CreateAnswer
537// returns successfully (or with failure if the underlying certificate generator
538// fails) no matter when the DTLS certificate is generated. If multiple
539// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
540// they all finish after the certificate is generated.
541
Steve Anton6b63cd52017-10-06 18:20:31542// Whether the certificate will be generated before calling CreateOffer or
543// while CreateOffer is executing.
544enum class CertGenTime { kBefore, kDuring };
545std::ostream& operator<<(std::ostream& out, CertGenTime value) {
546 switch (value) {
547 case CertGenTime::kBefore:
548 return out << "before";
549 case CertGenTime::kDuring:
550 return out << "during";
551 default:
552 return out << "unknown";
553 }
554}
555
556// Whether the fake certificate generator will produce a certificate or fail.
557enum class CertGenResult { kSucceed, kFail };
558std::ostream& operator<<(std::ostream& out, CertGenResult value) {
559 switch (value) {
560 case CertGenResult::kSucceed:
561 return out << "succeed";
562 case CertGenResult::kFail:
563 return out << "fail";
564 default:
565 return out << "unknown";
566 }
567}
568
Steve Anton71182f42018-01-19 22:59:54569class PeerConnectionCryptoDtlsCertGenTest
570 : public PeerConnectionCryptoBaseTest,
571 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
572 SdpType,
573 CertGenTime,
574 CertGenResult,
575 size_t>> {
Steve Anton6b63cd52017-10-06 18:20:31576 protected:
Steve Anton71182f42018-01-19 22:59:54577 PeerConnectionCryptoDtlsCertGenTest()
578 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
579 sdp_type_ = std::get<1>(GetParam());
580 cert_gen_time_ = std::get<2>(GetParam());
581 cert_gen_result_ = std::get<3>(GetParam());
582 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 18:20:31583 }
584
585 SdpType sdp_type_;
586 CertGenTime cert_gen_time_;
587 CertGenResult cert_gen_result_;
588 size_t concurrent_calls_;
589};
590
Steve Anton71182f42018-01-19 22:59:54591TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 18:20:31592 RTCConfiguration config;
593 config.enable_dtls_srtp.emplace(true);
594 auto owned_fake_certificate_generator =
Mirko Bonadei317a1f02019-09-17 15:06:18595 std::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 18:20:31596 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
597 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
598 CertGenResult::kFail);
599 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
600 CertGenTime::kDuring);
601 WrapperPtr pc;
602 if (sdp_type_ == SdpType::kOffer) {
603 pc = CreatePeerConnectionWithAudioVideo(
604 config, std::move(owned_fake_certificate_generator));
605 } else {
606 auto caller = CreatePeerConnectionWithAudioVideo(config);
607 pc = CreatePeerConnectionWithAudioVideo(
608 config, std::move(owned_fake_certificate_generator));
609 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
610 }
611 if (cert_gen_time_ == CertGenTime::kBefore) {
612 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
613 fake_certificate_generator->generated_failures() >
614 0,
615 kGenerateCertTimeout);
616 } else {
617 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
618 fake_certificate_generator->set_should_wait(false);
619 }
620 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
621 observers;
622 for (size_t i = 0; i < concurrent_calls_; i++) {
623 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
624 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
625 observers.push_back(observer);
626 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 10:32:18627 pc->pc()->CreateOffer(observer,
628 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 18:20:31629 } else {
Niels Möllerf06f9232018-08-07 10:32:18630 pc->pc()->CreateAnswer(observer,
631 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 18:20:31632 }
633 }
634 for (auto& observer : observers) {
635 EXPECT_TRUE_WAIT(observer->called(), 1000);
636 if (cert_gen_result_ == CertGenResult::kSucceed) {
637 EXPECT_TRUE(observer->result());
638 } else {
639 EXPECT_FALSE(observer->result());
640 }
641 }
642}
643
Mirko Bonadeic84f6612019-01-31 11:20:57644INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 22:59:54645 PeerConnectionCryptoTest,
646 PeerConnectionCryptoDtlsCertGenTest,
647 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
648 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 18:20:31649 Values(CertGenTime::kBefore, CertGenTime::kDuring),
650 Values(CertGenResult::kSucceed, CertGenResult::kFail),
651 Values(1, 3)));
652
Steve Anton8a63f782017-10-23 20:08:53653// Test that we can create and set an answer correctly when different
654// SSL roles have been negotiated for different transports.
655// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 22:59:54656TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 20:08:53657 auto caller = CreatePeerConnectionWithAudioVideo();
658 auto callee = CreatePeerConnectionWithAudioVideo();
659
660 RTCOfferAnswerOptions options_no_bundle;
661 options_no_bundle.use_rtp_mux = false;
662
663 // First, negotiate different SSL roles for audio and video.
664 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
665 auto answer = callee->CreateAnswer(options_no_bundle);
666
667 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
668 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
669
670 ASSERT_TRUE(
671 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
672 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
673
674 // Now create an offer in the reverse direction, and ensure the initial
675 // offerer responds with an answer with the correct SSL roles.
676 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
677 answer = caller->CreateAnswer(options_no_bundle);
678
679 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
680 AudioConnectionRole(answer->description()));
681 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
682 VideoConnectionRole(answer->description()));
683
684 ASSERT_TRUE(
685 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
686 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
687
688 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
689 // audio is transferred over to video in the answer that completes the BUNDLE
690 // negotiation.
691 RTCOfferAnswerOptions options_bundle;
692 options_bundle.use_rtp_mux = true;
693
694 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
695 answer = caller->CreateAnswer(options_bundle);
696
697 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
698 AudioConnectionRole(answer->description()));
699 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
700 VideoConnectionRole(answer->description()));
701
702 ASSERT_TRUE(
703 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
704 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
705}
706
Steve Anton80dd7b52018-02-17 01:08:42707// Tests that if the DTLS fingerprint is invalid then all future calls to
708// SetLocalDescription and SetRemoteDescription will fail due to a session
709// error.
710// This is a regression test for crbug.com/800775
711TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
712 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
713 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
714
715 auto caller = CreatePeerConnectionWithAudioVideo();
716 RTCConfiguration callee_config;
717 callee_config.enable_dtls_srtp.emplace(true);
718 callee_config.certificates.push_back(callee_certificate);
719 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
720
721 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
722
723 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 17:40:48724 auto valid_answer = callee->CreateAnswer();
725 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-17 01:08:42726 auto* audio_content =
727 cricket::GetFirstAudioContent(invalid_answer->description());
728 ASSERT_TRUE(audio_content);
729 auto* audio_transport_info =
730 invalid_answer->description()->GetTransportInfoByName(
731 audio_content->name);
732 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-16 02:27:44733 audio_transport_info->description.identity_fingerprint =
734 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-17 01:08:42735
736 // Set the invalid answer and expect a fingerprint error.
737 std::string error;
738 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
739 EXPECT_PRED_FORMAT2(AssertStringContains, error,
740 "Local fingerprint does not match identity.");
741
742 // Make sure that setting a valid remote offer or local answer also fails now.
743 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
744 EXPECT_PRED_FORMAT2(AssertStringContains, error,
745 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 17:40:48746 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-17 01:08:42747 EXPECT_PRED_FORMAT2(AssertStringContains, error,
748 "Session error code: ERROR_CONTENT.");
749}
750
Mirko Bonadeic84f6612019-01-31 11:20:57751INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
752 PeerConnectionCryptoTest,
753 Values(SdpSemantics::kPlanB,
754 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 22:59:54755
Steve Anton6b63cd52017-10-06 18:20:31756} // namespace webrtc