blob: 32e8cbd74c0ba6b7326cb79c68172e7f28f8efcc [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
Mirko Bonadei317a1f02019-09-17 17:06:18 +020011#include <memory>
12
Karl Wiberg1b0eae32017-10-17 14:48:54 +020013#include "api/audio_codecs/builtin_audio_decoder_factory.h"
14#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010015#include "api/create_peerconnection_factory.h"
Anders Carlsson67537952018-05-03 11:28:29 +020016#include "api/video_codecs/builtin_video_decoder_factory.h"
17#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton10542f22019-01-11 09:11:00 -080018#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 11:20:31 -070022#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080023#include "pc/test/android_test_initializer.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070024#endif
Steve Anton10542f22019-01-11 09:11:00 -080025#include "pc/test/fake_audio_capture_module.h"
26#include "pc/test/fake_rtc_certificate_generator.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070027#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080028#include "rtc_base/virtual_socket_server.h"
Steve Anton6b63cd52017-10-06 11:20:31 -070029
30namespace webrtc {
31
32using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Anton8a63f782017-10-23 13:08:53 -070033using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
Steve Anton6b63cd52017-10-06 11:20:31 -070034using ::testing::Combine;
Jonas Olssona4d87372019-07-05 19:08:33 +020035using ::testing::Values;
Steve Anton6b63cd52017-10-06 11:20:31 -070036
37constexpr int kGenerateCertTimeout = 1000;
38
Steve Anton71182f42018-01-19 14:59:54 -080039class PeerConnectionCryptoBaseTest : public ::testing::Test {
Steve Anton6b63cd52017-10-06 11:20:31 -070040 protected:
41 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
42
Steve Anton71182f42018-01-19 14:59:54 -080043 explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
44 : vss_(new rtc::VirtualSocketServer()),
45 main_(vss_.get()),
46 sdp_semantics_(sdp_semantics) {
Steve Anton6b63cd52017-10-06 11:20:31 -070047#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 14:48:54 +020052 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
Anders Carlsson67537952018-05-03 11:28:29 +020053 CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
54 CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
55 nullptr /* audio_processing */);
Steve Anton6b63cd52017-10-06 11:20:31 -070056 }
57
Steve Anton8a63f782017-10-23 13:08:53 -070058 WrapperPtr CreatePeerConnection() {
59 return CreatePeerConnection(RTCConfiguration());
60 }
61
Steve Anton6b63cd52017-10-06 11:20:31 -070062 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 17:06:18 +020069 auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
Steve Anton6b63cd52017-10-06 11:20:31 -070070 rtc::Thread::Current(), nullptr);
Mirko Bonadei317a1f02019-09-17 17:06:18 +020071 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton71182f42018-01-19 14:59:54 -080072 RTCConfiguration modified_config = config;
73 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -070074 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton71182f42018-01-19 14:59:54 -080075 modified_config, std::move(fake_port_allocator), std::move(cert_gen),
Steve Anton6b63cd52017-10-06 11:20:31 -070076 observer.get());
77 if (!pc) {
78 return nullptr;
79 }
80
Yves Gerey4e933292018-10-31 15:36:05 +010081 observer->SetPeerConnectionInterface(pc.get());
Mirko Bonadei317a1f02019-09-17 17:06:18 +020082 return std::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
83 std::move(observer));
Steve Anton6b63cd52017-10-06 11:20:31 -070084 }
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 15:30:51 -070094 wrapper->AddAudioTrack("a");
95 wrapper->AddVideoTrack("v");
Steve Anton6b63cd52017-10-06 11:20:31 -070096 return wrapper;
97 }
98
Steve Anton8a63f782017-10-23 13:08:53 -070099 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 11:20:31 -0700118 std::unique_ptr<rtc::VirtualSocketServer> vss_;
119 rtc::AutoSocketServerThread main_;
120 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
Steve Anton71182f42018-01-19 14:59:54 -0800121 const SdpSemantics sdp_semantics_;
Steve Anton6b63cd52017-10-06 11:20:31 -0700122};
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 15:14:30 -0800134 return !content->media_description()->cryptos().empty();
Steve Anton6b63cd52017-10-06 11:20:31 -0700135 };
136}
137
138SdpContentPredicate HaveProtocol(const std::string& protocol) {
139 return [protocol](const cricket::ContentInfo* content,
140 const cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800141 return content->media_description()->protocol() == protocol;
Steve Anton6b63cd52017-10-06 11:20:31 -0700142 };
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 15:14:30 -0800148 const auto& cryptos = content->media_description()->cryptos();
149 if (cryptos.size() != num_crypto_suites) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700150 return false;
151 }
Philipp Hancke1aec2bf2020-05-12 10:11:27 +0200152 for (size_t i = 0; i < cryptos.size(); ++i) {
153 if (cryptos[i].key_params.size() == 67U &&
154 cryptos[i].cipher_suite == "AEAD_AES_256_GCM")
155 return true;
156 }
157 return false;
Steve Anton6b63cd52017-10-06 11:20:31 -0700158 };
159}
160
Steve Anton71182f42018-01-19 14:59:54 -0800161class PeerConnectionCryptoTest
162 : public PeerConnectionCryptoBaseTest,
163 public ::testing::WithParamInterface<SdpSemantics> {
164 protected:
165 PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
166};
167
Steve Anton6b63cd52017-10-06 11:20:31 -0700168SdpContentMutator RemoveSdesCryptos() {
169 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800170 content->media_description()->set_cryptos({});
Steve Anton6b63cd52017-10-06 11:20:31 -0700171 };
172}
173
174SdpContentMutator RemoveDtlsFingerprint() {
175 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
176 transport->description.identity_fingerprint.reset();
177 };
178}
179
180// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
181// no SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800182TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700183 RTCConfiguration config;
184 config.enable_dtls_srtp.emplace(true);
185 auto caller = CreatePeerConnectionWithAudioVideo(config);
186
187 auto offer = caller->CreateOffer();
188 ASSERT_TRUE(offer);
189
190 ASSERT_FALSE(offer->description()->contents().empty());
191 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
192 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
193 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
194 offer->description()));
195}
Steve Anton71182f42018-01-19 14:59:54 -0800196TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700197 RTCConfiguration config;
198 config.enable_dtls_srtp.emplace(true);
199 auto caller = CreatePeerConnectionWithAudioVideo(config);
200 auto callee = CreatePeerConnectionWithAudioVideo(config);
201
202 callee->SetRemoteDescription(caller->CreateOffer());
203 auto answer = callee->CreateAnswer();
204 ASSERT_TRUE(answer);
205
206 ASSERT_FALSE(answer->description()->contents().empty());
207 EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
208 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
209 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
210 answer->description()));
211}
212
213// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
214// should not have a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800215TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700216 RTCConfiguration config;
217 config.enable_dtls_srtp.emplace(false);
218 auto caller = CreatePeerConnectionWithAudioVideo(config);
219
220 auto offer = caller->CreateOffer();
221 ASSERT_TRUE(offer);
222
223 ASSERT_FALSE(offer->description()->contents().empty());
224 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
225 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
226 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
227 offer->description()));
228}
Steve Anton71182f42018-01-19 14:59:54 -0800229TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700230 RTCConfiguration config;
231 config.enable_dtls_srtp.emplace(false);
232 auto caller = CreatePeerConnectionWithAudioVideo(config);
233 auto callee = CreatePeerConnectionWithAudioVideo(config);
234
235 callee->SetRemoteDescription(caller->CreateOffer());
236 auto answer = callee->CreateAnswer();
237 ASSERT_TRUE(answer);
238
239 ASSERT_FALSE(answer->description()->contents().empty());
240 EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
241 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
242 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
243 answer->description()));
244}
245
246// When encryption is disabled, the SDP offer/answer should have neither a DTLS
247// fingerprint nor any SDES crypto options.
Steve Anton71182f42018-01-19 14:59:54 -0800248TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700249 PeerConnectionFactoryInterface::Options options;
250 options.disable_encryption = true;
251 pc_factory_->SetOptions(options);
252
253 RTCConfiguration config;
254 config.enable_dtls_srtp.emplace(false);
255 auto caller = CreatePeerConnectionWithAudioVideo(config);
256
257 auto offer = caller->CreateOffer();
258 ASSERT_TRUE(offer);
259
260 ASSERT_FALSE(offer->description()->contents().empty());
261 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
262 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
263 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
264 offer->description()));
265}
Steve Anton71182f42018-01-19 14:59:54 -0800266TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700267 PeerConnectionFactoryInterface::Options options;
268 options.disable_encryption = true;
269 pc_factory_->SetOptions(options);
270
271 RTCConfiguration config;
272 config.enable_dtls_srtp.emplace(false);
273 auto caller = CreatePeerConnectionWithAudioVideo(config);
274 auto callee = CreatePeerConnectionWithAudioVideo(config);
275
276 callee->SetRemoteDescription(caller->CreateOffer());
277 auto answer = callee->CreateAnswer();
278 ASSERT_TRUE(answer);
279
280 ASSERT_FALSE(answer->description()->contents().empty());
281 EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
282 EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
283 EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
284 answer->description()));
285}
286
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700287// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
288// set in the configuration it should overrite the settings set in the factory.
289TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
290 PeerConnectionFactoryInterface::Options options;
291 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
292 pc_factory_->SetOptions(options);
293
294 RTCConfiguration config;
295 config.enable_dtls_srtp.emplace(false);
296 CryptoOptions crypto_options;
297 crypto_options.srtp.enable_gcm_crypto_suites = false;
298 config.crypto_options = crypto_options;
299 auto caller = CreatePeerConnectionWithAudioVideo(config);
300
301 auto offer = caller->CreateOffer();
302 ASSERT_TRUE(offer);
303
304 ASSERT_FALSE(offer->description()->contents().empty());
305 // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
306 EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
307}
308
Steve Anton6b63cd52017-10-06 11:20:31 -0700309// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
310// should have the correct ciphers in the SDES crypto options.
311// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
312// in the answer.
Steve Anton71182f42018-01-19 14:59:54 -0800313TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700314 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700315 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700316 pc_factory_->SetOptions(options);
317
318 RTCConfiguration config;
319 config.enable_dtls_srtp.emplace(false);
320 auto caller = CreatePeerConnectionWithAudioVideo(config);
321
322 auto offer = caller->CreateOffer();
323 ASSERT_TRUE(offer);
324
325 ASSERT_FALSE(offer->description()->contents().empty());
326 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
327}
Benjamin Wright8c27cca2018-10-25 10:16:44 -0700328
Steve Anton71182f42018-01-19 14:59:54 -0800329TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700330 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700331 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700332 pc_factory_->SetOptions(options);
333
334 RTCConfiguration config;
335 config.enable_dtls_srtp.emplace(false);
336 auto caller = CreatePeerConnectionWithAudioVideo(config);
337 auto callee = CreatePeerConnectionWithAudioVideo(config);
338
Philipp Hancke1aec2bf2020-05-12 10:11:27 +0200339 auto offer = caller->CreateOffer();
340 for (cricket::ContentInfo& content : offer->description()->contents()) {
341 auto cryptos = content.media_description()->cryptos();
342 cryptos.erase(cryptos.begin()); // Assumes that non-GCM is the default.
343 content.media_description()->set_cryptos(cryptos);
344 }
345
346 callee->SetRemoteDescription(std::move(offer));
Steve Anton6b63cd52017-10-06 11:20:31 -0700347 auto answer = callee->CreateAnswer();
348 ASSERT_TRUE(answer);
349
350 ASSERT_FALSE(answer->description()->contents().empty());
351 EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
352}
353
Steve Anton71182f42018-01-19 14:59:54 -0800354TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700355 PeerConnectionFactoryInterface::Options options;
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700356 options.crypto_options.srtp.enable_gcm_crypto_suites = true;
Steve Anton6b63cd52017-10-06 11:20:31 -0700357 pc_factory_->SetOptions(options);
358
359 RTCConfiguration config;
360 config.enable_dtls_srtp.emplace(false);
361 auto caller = CreatePeerConnectionWithAudioVideo(config);
362 auto callee = CreatePeerConnectionWithAudioVideo(config);
363
364 auto offer = caller->CreateOffer();
365 ASSERT_TRUE(offer);
366 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
367
368 auto answer = callee->CreateAnswer();
369 ASSERT_TRUE(answer);
370 ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
371}
372
373// The following group tests that two PeerConnections can successfully exchange
374// an offer/answer when DTLS is off and that they will refuse any offer/answer
375// applied locally/remotely if it does not include SDES cryptos.
Steve Anton71182f42018-01-19 14:59:54 -0800376TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700377 RTCConfiguration config;
378 config.enable_dtls_srtp.emplace(false);
379 auto caller = CreatePeerConnectionWithAudioVideo(config);
380 auto callee = CreatePeerConnectionWithAudioVideo(config);
381
382 auto offer = caller->CreateOfferAndSetAsLocal();
383 ASSERT_TRUE(offer);
384 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
385
386 auto answer = callee->CreateAnswerAndSetAsLocal();
387 ASSERT_TRUE(answer);
388 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
389}
Steve Anton71182f42018-01-19 14:59:54 -0800390TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700391 RTCConfiguration config;
392 config.enable_dtls_srtp.emplace(false);
393 auto caller = CreatePeerConnectionWithAudioVideo(config);
394
395 auto offer = caller->CreateOffer();
396 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
397
398 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
399}
Steve Anton71182f42018-01-19 14:59:54 -0800400TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
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 auto offer = caller->CreateOffer();
407 SdpContentsForEach(RemoveSdesCryptos(), offer->description());
408
409 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
410}
Steve Anton71182f42018-01-19 14:59:54 -0800411TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700412 RTCConfiguration config;
413 config.enable_dtls_srtp.emplace(false);
414 auto caller = CreatePeerConnectionWithAudioVideo(config);
415 auto callee = CreatePeerConnectionWithAudioVideo(config);
416
417 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
418 auto answer = callee->CreateAnswer();
419 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
420
421 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
422}
Steve Anton71182f42018-01-19 14:59:54 -0800423TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700424 RTCConfiguration config;
425 config.enable_dtls_srtp.emplace(false);
426 auto caller = CreatePeerConnectionWithAudioVideo(config);
427 auto callee = CreatePeerConnectionWithAudioVideo(config);
428
429 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
430 auto answer = callee->CreateAnswerAndSetAsLocal();
431 SdpContentsForEach(RemoveSdesCryptos(), answer->description());
432
433 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
434}
435
436// The following group tests that two PeerConnections can successfully exchange
437// an offer/answer when DTLS is on and that they will refuse any offer/answer
438// applied locally/remotely if it does not include a DTLS fingerprint.
Steve Anton71182f42018-01-19 14:59:54 -0800439TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700440 RTCConfiguration config;
441 config.enable_dtls_srtp.emplace(true);
442 auto caller = CreatePeerConnectionWithAudioVideo(config);
443 auto callee = CreatePeerConnectionWithAudioVideo(config);
444
445 auto offer = caller->CreateOfferAndSetAsLocal();
446 ASSERT_TRUE(offer);
447 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
448
449 auto answer = callee->CreateAnswerAndSetAsLocal();
450 ASSERT_TRUE(answer);
451 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
452}
Steve Anton71182f42018-01-19 14:59:54 -0800453TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700454 FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
455 RTCConfiguration config;
456 config.enable_dtls_srtp.emplace(true);
457 auto caller = CreatePeerConnectionWithAudioVideo(config);
458
459 auto offer = caller->CreateOffer();
460 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
461
462 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
463}
Steve Anton71182f42018-01-19 14:59:54 -0800464TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700465 FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
466 RTCConfiguration config;
467 config.enable_dtls_srtp.emplace(true);
468 auto caller = CreatePeerConnectionWithAudioVideo(config);
469 auto callee = CreatePeerConnectionWithAudioVideo(config);
470
471 auto offer = caller->CreateOffer();
472 SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
473
474 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
475}
Steve Anton71182f42018-01-19 14:59:54 -0800476TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700477 FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
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->CreateAnswer();
485 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
486}
Steve Anton71182f42018-01-19 14:59:54 -0800487TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700488 FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
489 RTCConfiguration config;
490 config.enable_dtls_srtp.emplace(true);
491 auto caller = CreatePeerConnectionWithAudioVideo(config);
492 auto callee = CreatePeerConnectionWithAudioVideo(config);
493
494 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
495 auto answer = callee->CreateAnswerAndSetAsLocal();
496 SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
497
498 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
499}
500
501// Test that an offer/answer can be exchanged when encryption is disabled.
Steve Anton71182f42018-01-19 14:59:54 -0800502TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700503 PeerConnectionFactoryInterface::Options options;
504 options.disable_encryption = true;
505 pc_factory_->SetOptions(options);
506
507 RTCConfiguration config;
508 config.enable_dtls_srtp.emplace(false);
509 auto caller = CreatePeerConnectionWithAudioVideo(config);
510 auto callee = CreatePeerConnectionWithAudioVideo(config);
511
512 auto offer = caller->CreateOfferAndSetAsLocal();
513 ASSERT_TRUE(offer);
514 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
515
516 auto answer = callee->CreateAnswerAndSetAsLocal();
517 ASSERT_TRUE(answer);
518 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
519}
520
521// Tests that a DTLS call can be established when the certificate is specified
522// in the PeerConnection config and no certificate generator is specified.
Steve Anton71182f42018-01-19 14:59:54 -0800523TEST_P(PeerConnectionCryptoTest,
Steve Anton6b63cd52017-10-06 11:20:31 -0700524 ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
525 RTCConfiguration caller_config;
526 caller_config.enable_dtls_srtp.emplace(true);
527 caller_config.certificates.push_back(
528 FakeRTCCertificateGenerator::GenerateCertificate());
529 auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
530
531 RTCConfiguration callee_config;
532 callee_config.enable_dtls_srtp.emplace(true);
533 callee_config.certificates.push_back(
534 FakeRTCCertificateGenerator::GenerateCertificate());
535 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
536
537 auto offer = caller->CreateOfferAndSetAsLocal();
538 ASSERT_TRUE(offer);
539 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
540
541 auto answer = callee->CreateAnswerAndSetAsLocal();
542 ASSERT_TRUE(answer);
543 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
544}
545
546// The following parameterized test verifies that CreateOffer/CreateAnswer
547// returns successfully (or with failure if the underlying certificate generator
548// fails) no matter when the DTLS certificate is generated. If multiple
549// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
550// they all finish after the certificate is generated.
551
Steve Anton6b63cd52017-10-06 11:20:31 -0700552// Whether the certificate will be generated before calling CreateOffer or
553// while CreateOffer is executing.
554enum class CertGenTime { kBefore, kDuring };
555std::ostream& operator<<(std::ostream& out, CertGenTime value) {
556 switch (value) {
557 case CertGenTime::kBefore:
558 return out << "before";
559 case CertGenTime::kDuring:
560 return out << "during";
561 default:
562 return out << "unknown";
563 }
564}
565
566// Whether the fake certificate generator will produce a certificate or fail.
567enum class CertGenResult { kSucceed, kFail };
568std::ostream& operator<<(std::ostream& out, CertGenResult value) {
569 switch (value) {
570 case CertGenResult::kSucceed:
571 return out << "succeed";
572 case CertGenResult::kFail:
573 return out << "fail";
574 default:
575 return out << "unknown";
576 }
577}
578
Steve Anton71182f42018-01-19 14:59:54 -0800579class PeerConnectionCryptoDtlsCertGenTest
580 : public PeerConnectionCryptoBaseTest,
581 public ::testing::WithParamInterface<std::tuple<SdpSemantics,
582 SdpType,
583 CertGenTime,
584 CertGenResult,
585 size_t>> {
Steve Anton6b63cd52017-10-06 11:20:31 -0700586 protected:
Steve Anton71182f42018-01-19 14:59:54 -0800587 PeerConnectionCryptoDtlsCertGenTest()
588 : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
589 sdp_type_ = std::get<1>(GetParam());
590 cert_gen_time_ = std::get<2>(GetParam());
591 cert_gen_result_ = std::get<3>(GetParam());
592 concurrent_calls_ = std::get<4>(GetParam());
Steve Anton6b63cd52017-10-06 11:20:31 -0700593 }
594
595 SdpType sdp_type_;
596 CertGenTime cert_gen_time_;
597 CertGenResult cert_gen_result_;
598 size_t concurrent_calls_;
599};
600
Steve Anton71182f42018-01-19 14:59:54 -0800601TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
Steve Anton6b63cd52017-10-06 11:20:31 -0700602 RTCConfiguration config;
603 config.enable_dtls_srtp.emplace(true);
604 auto owned_fake_certificate_generator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200605 std::make_unique<FakeRTCCertificateGenerator>();
Steve Anton6b63cd52017-10-06 11:20:31 -0700606 auto* fake_certificate_generator = owned_fake_certificate_generator.get();
607 fake_certificate_generator->set_should_fail(cert_gen_result_ ==
608 CertGenResult::kFail);
609 fake_certificate_generator->set_should_wait(cert_gen_time_ ==
610 CertGenTime::kDuring);
611 WrapperPtr pc;
612 if (sdp_type_ == SdpType::kOffer) {
613 pc = CreatePeerConnectionWithAudioVideo(
614 config, std::move(owned_fake_certificate_generator));
615 } else {
616 auto caller = CreatePeerConnectionWithAudioVideo(config);
617 pc = CreatePeerConnectionWithAudioVideo(
618 config, std::move(owned_fake_certificate_generator));
619 pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
620 }
621 if (cert_gen_time_ == CertGenTime::kBefore) {
622 ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
623 fake_certificate_generator->generated_failures() >
624 0,
625 kGenerateCertTimeout);
626 } else {
627 ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
628 fake_certificate_generator->set_should_wait(false);
629 }
630 std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
631 observers;
632 for (size_t i = 0; i < concurrent_calls_; i++) {
633 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
634 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
635 observers.push_back(observer);
636 if (sdp_type_ == SdpType::kOffer) {
Niels Möllerf06f9232018-08-07 12:32:18 +0200637 pc->pc()->CreateOffer(observer,
638 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700639 } else {
Niels Möllerf06f9232018-08-07 12:32:18 +0200640 pc->pc()->CreateAnswer(observer,
641 PeerConnectionInterface::RTCOfferAnswerOptions());
Steve Anton6b63cd52017-10-06 11:20:31 -0700642 }
643 }
644 for (auto& observer : observers) {
645 EXPECT_TRUE_WAIT(observer->called(), 1000);
646 if (cert_gen_result_ == CertGenResult::kSucceed) {
647 EXPECT_TRUE(observer->result());
648 } else {
649 EXPECT_FALSE(observer->result());
650 }
651 }
652}
653
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100654INSTANTIATE_TEST_SUITE_P(
Steve Anton71182f42018-01-19 14:59:54 -0800655 PeerConnectionCryptoTest,
656 PeerConnectionCryptoDtlsCertGenTest,
657 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
658 Values(SdpType::kOffer, SdpType::kAnswer),
Steve Anton6b63cd52017-10-06 11:20:31 -0700659 Values(CertGenTime::kBefore, CertGenTime::kDuring),
660 Values(CertGenResult::kSucceed, CertGenResult::kFail),
661 Values(1, 3)));
662
Steve Anton8a63f782017-10-23 13:08:53 -0700663// Test that we can create and set an answer correctly when different
664// SSL roles have been negotiated for different transports.
665// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
Steve Anton71182f42018-01-19 14:59:54 -0800666TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
Steve Anton8a63f782017-10-23 13:08:53 -0700667 auto caller = CreatePeerConnectionWithAudioVideo();
668 auto callee = CreatePeerConnectionWithAudioVideo();
669
670 RTCOfferAnswerOptions options_no_bundle;
671 options_no_bundle.use_rtp_mux = false;
672
673 // First, negotiate different SSL roles for audio and video.
674 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
675 auto answer = callee->CreateAnswer(options_no_bundle);
676
677 AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
678 VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
679
680 ASSERT_TRUE(
681 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
682 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
683
684 // Now create an offer in the reverse direction, and ensure the initial
685 // offerer responds with an answer with the correct SSL roles.
686 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
687 answer = caller->CreateAnswer(options_no_bundle);
688
689 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
690 AudioConnectionRole(answer->description()));
691 EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
692 VideoConnectionRole(answer->description()));
693
694 ASSERT_TRUE(
695 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
696 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
697
698 // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
699 // audio is transferred over to video in the answer that completes the BUNDLE
700 // negotiation.
701 RTCOfferAnswerOptions options_bundle;
702 options_bundle.use_rtp_mux = true;
703
704 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
705 answer = caller->CreateAnswer(options_bundle);
706
707 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
708 AudioConnectionRole(answer->description()));
709 EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
710 VideoConnectionRole(answer->description()));
711
712 ASSERT_TRUE(
713 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
714 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
715}
716
Steve Anton80dd7b52018-02-16 17:08:42 -0800717// Tests that if the DTLS fingerprint is invalid then all future calls to
718// SetLocalDescription and SetRemoteDescription will fail due to a session
719// error.
720// This is a regression test for crbug.com/800775
721TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
722 auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
723 auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
724
725 auto caller = CreatePeerConnectionWithAudioVideo();
726 RTCConfiguration callee_config;
727 callee_config.enable_dtls_srtp.emplace(true);
728 callee_config.certificates.push_back(callee_certificate);
729 auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
730
731 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
732
733 // Create an invalid answer with the other certificate's fingerprint.
Steve Anton25ca0ac2019-06-25 10:40:48 -0700734 auto valid_answer = callee->CreateAnswer();
735 auto invalid_answer = CloneSessionDescription(valid_answer.get());
Steve Anton80dd7b52018-02-16 17:08:42 -0800736 auto* audio_content =
737 cricket::GetFirstAudioContent(invalid_answer->description());
738 ASSERT_TRUE(audio_content);
739 auto* audio_transport_info =
740 invalid_answer->description()->GetTransportInfoByName(
741 audio_content->name);
742 ASSERT_TRUE(audio_transport_info);
Steve Anton4905edb2018-10-15 19:27:44 -0700743 audio_transport_info->description.identity_fingerprint =
744 rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
Steve Anton80dd7b52018-02-16 17:08:42 -0800745
746 // Set the invalid answer and expect a fingerprint error.
747 std::string error;
748 ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
749 EXPECT_PRED_FORMAT2(AssertStringContains, error,
750 "Local fingerprint does not match identity.");
751
752 // Make sure that setting a valid remote offer or local answer also fails now.
753 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
754 EXPECT_PRED_FORMAT2(AssertStringContains, error,
755 "Session error code: ERROR_CONTENT.");
Steve Anton25ca0ac2019-06-25 10:40:48 -0700756 ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
Steve Anton80dd7b52018-02-16 17:08:42 -0800757 EXPECT_PRED_FORMAT2(AssertStringContains, error,
758 "Session error code: ERROR_CONTENT.");
759}
760
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100761INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
762 PeerConnectionCryptoTest,
763 Values(SdpSemantics::kPlanB,
764 SdpSemantics::kUnifiedPlan));
Steve Anton71182f42018-01-19 14:59:54 -0800765
Steve Anton6b63cd52017-10-06 11:20:31 -0700766} // namespace webrtc