xref: /aosp_15_r20/external/webrtc/pc/peer_connection_crypto_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
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 
11 #include <stddef.h>
12 
13 #include <memory>
14 #include <ostream>
15 #include <string>
16 #include <tuple>
17 #include <type_traits>
18 #include <utility>
19 #include <vector>
20 
21 #include "absl/types/optional.h"
22 #include "api/audio/audio_mixer.h"
23 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
24 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
25 #include "api/create_peerconnection_factory.h"
26 #include "api/crypto/crypto_options.h"
27 #include "api/crypto_params.h"
28 #include "api/jsep.h"
29 #include "api/peer_connection_interface.h"
30 #include "api/scoped_refptr.h"
31 #include "api/video_codecs/builtin_video_decoder_factory.h"
32 #include "api/video_codecs/builtin_video_encoder_factory.h"
33 #include "modules/audio_device/include/audio_device.h"
34 #include "modules/audio_processing/include/audio_processing.h"
35 #include "p2p/base/fake_port_allocator.h"
36 #include "p2p/base/port_allocator.h"
37 #include "p2p/base/transport_description.h"
38 #include "p2p/base/transport_info.h"
39 #include "pc/media_protocol_names.h"
40 #include "pc/media_session.h"
41 #include "pc/peer_connection_wrapper.h"
42 #include "pc/sdp_utils.h"
43 #include "pc/session_description.h"
44 #include "pc/test/mock_peer_connection_observers.h"
45 #include "rtc_base/checks.h"
46 #include "rtc_base/rtc_certificate.h"
47 #include "rtc_base/rtc_certificate_generator.h"
48 #include "rtc_base/ssl_fingerprint.h"
49 #include "rtc_base/thread.h"
50 #include "test/gtest.h"
51 #ifdef WEBRTC_ANDROID
52 #include "pc/test/android_test_initializer.h"
53 #endif
54 #include "pc/test/fake_audio_capture_module.h"
55 #include "pc/test/fake_rtc_certificate_generator.h"
56 #include "rtc_base/gunit.h"
57 #include "rtc_base/virtual_socket_server.h"
58 
59 namespace webrtc {
60 
61 using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
62 using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
63 using ::testing::Combine;
64 using ::testing::Values;
65 
66 constexpr int kGenerateCertTimeout = 1000;
67 
68 class PeerConnectionCryptoBaseTest : public ::testing::Test {
69  protected:
70   typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
71 
PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)72   explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
73       : vss_(new rtc::VirtualSocketServer()),
74         main_(vss_.get()),
75         sdp_semantics_(sdp_semantics) {
76 #ifdef WEBRTC_ANDROID
77     InitializeAndroidObjects();
78 #endif
79     pc_factory_ = CreatePeerConnectionFactory(
80         rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
81         FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
82         CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
83         CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
84         nullptr /* audio_processing */);
85   }
86 
CreatePeerConnection()87   WrapperPtr CreatePeerConnection() {
88     return CreatePeerConnection(RTCConfiguration());
89   }
90 
CreatePeerConnection(const RTCConfiguration & config)91   WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
92     return CreatePeerConnection(config, nullptr);
93   }
94 
CreatePeerConnection(const RTCConfiguration & config,std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen)95   WrapperPtr CreatePeerConnection(
96       const RTCConfiguration& config,
97       std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
98     auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
99         rtc::Thread::Current(),
100         std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get()));
101     auto observer = std::make_unique<MockPeerConnectionObserver>();
102     RTCConfiguration modified_config = config;
103     modified_config.sdp_semantics = sdp_semantics_;
104     PeerConnectionDependencies pc_dependencies(observer.get());
105     pc_dependencies.allocator = std::move(fake_port_allocator);
106     pc_dependencies.cert_generator = std::move(cert_gen);
107     auto result = pc_factory_->CreatePeerConnectionOrError(
108         modified_config, std::move(pc_dependencies));
109     if (!result.ok()) {
110       return nullptr;
111     }
112 
113     observer->SetPeerConnectionInterface(result.value().get());
114     return std::make_unique<PeerConnectionWrapper>(
115         pc_factory_, result.MoveValue(), std::move(observer));
116   }
117 
118   // Accepts the same arguments as CreatePeerConnection and adds default audio
119   // and video tracks.
120   template <typename... Args>
CreatePeerConnectionWithAudioVideo(Args &&...args)121   WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
122     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
123     if (!wrapper) {
124       return nullptr;
125     }
126     wrapper->AddAudioTrack("a");
127     wrapper->AddVideoTrack("v");
128     return wrapper;
129   }
130 
AudioConnectionRole(cricket::SessionDescription * desc)131   cricket::ConnectionRole& AudioConnectionRole(
132       cricket::SessionDescription* desc) {
133     return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
134   }
135 
VideoConnectionRole(cricket::SessionDescription * desc)136   cricket::ConnectionRole& VideoConnectionRole(
137       cricket::SessionDescription* desc) {
138     return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
139   }
140 
ConnectionRoleFromContent(cricket::SessionDescription * desc,cricket::ContentInfo * content)141   cricket::ConnectionRole& ConnectionRoleFromContent(
142       cricket::SessionDescription* desc,
143       cricket::ContentInfo* content) {
144     RTC_DCHECK(content);
145     auto* transport_info = desc->GetTransportInfoByName(content->name);
146     RTC_DCHECK(transport_info);
147     return transport_info->description.connection_role;
148   }
149 
150   std::unique_ptr<rtc::VirtualSocketServer> vss_;
151   rtc::AutoSocketServerThread main_;
152   rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
153   const SdpSemantics sdp_semantics_;
154 };
155 
HaveDtlsFingerprint()156 SdpContentPredicate HaveDtlsFingerprint() {
157   return [](const cricket::ContentInfo* content,
158             const cricket::TransportInfo* transport) {
159     return transport->description.identity_fingerprint != nullptr;
160   };
161 }
162 
HaveSdesCryptos()163 SdpContentPredicate HaveSdesCryptos() {
164   return [](const cricket::ContentInfo* content,
165             const cricket::TransportInfo* transport) {
166     return !content->media_description()->cryptos().empty();
167   };
168 }
169 
HaveProtocol(const std::string & protocol)170 SdpContentPredicate HaveProtocol(const std::string& protocol) {
171   return [protocol](const cricket::ContentInfo* content,
172                     const cricket::TransportInfo* transport) {
173     return content->media_description()->protocol() == protocol;
174   };
175 }
176 
HaveSdesGcmCryptos(size_t num_crypto_suites)177 SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
178   return [num_crypto_suites](const cricket::ContentInfo* content,
179                              const cricket::TransportInfo* transport) {
180     const auto& cryptos = content->media_description()->cryptos();
181     if (cryptos.size() != num_crypto_suites) {
182       return false;
183     }
184     for (size_t i = 0; i < cryptos.size(); ++i) {
185       if (cryptos[i].key_params.size() == 67U &&
186           cryptos[i].cipher_suite == "AEAD_AES_256_GCM")
187         return true;
188     }
189     return false;
190   };
191 }
192 
193 class PeerConnectionCryptoTest
194     : public PeerConnectionCryptoBaseTest,
195       public ::testing::WithParamInterface<SdpSemantics> {
196  protected:
PeerConnectionCryptoTest()197   PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
198 };
199 
RemoveSdesCryptos()200 SdpContentMutator RemoveSdesCryptos() {
201   return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
202     content->media_description()->set_cryptos({});
203   };
204 }
205 
RemoveDtlsFingerprint()206 SdpContentMutator RemoveDtlsFingerprint() {
207   return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
208     transport->description.identity_fingerprint.reset();
209   };
210 }
211 
212 // When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
213 // no SDES cryptos.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenDtlsEnabled)214 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
215   RTCConfiguration config;
216   auto caller = CreatePeerConnectionWithAudioVideo(config);
217 
218   auto offer = caller->CreateOffer();
219   ASSERT_TRUE(offer);
220 
221   ASSERT_FALSE(offer->description()->contents().empty());
222   EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
223   EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
224   EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
225                              offer->description()));
226 }
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenDtlsEnabled)227 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
228   RTCConfiguration config;
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(HaveDtlsFingerprint(), answer->description()));
238   EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
239   EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
240                              answer->description()));
241 }
242 
243 #if defined(WEBRTC_FUCHSIA)
244 // When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
245 // should not have a DTLS fingerprint.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenDtlsDisabled)246 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
247   RTCConfiguration config;
248   config.enable_dtls_srtp.emplace(false);
249   auto caller = CreatePeerConnectionWithAudioVideo(config);
250 
251   auto offer = caller->CreateOffer();
252   ASSERT_TRUE(offer);
253 
254   ASSERT_FALSE(offer->description()->contents().empty());
255   EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
256   EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
257   EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
258                              offer->description()));
259 }
260 
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenDtlsDisabled)261 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
262   RTCConfiguration config;
263   config.enable_dtls_srtp.emplace(false);
264   auto caller = CreatePeerConnectionWithAudioVideo(config);
265   auto callee = CreatePeerConnectionWithAudioVideo(config);
266 
267   callee->SetRemoteDescription(caller->CreateOffer());
268   auto answer = callee->CreateAnswer();
269   ASSERT_TRUE(answer);
270 
271   ASSERT_FALSE(answer->description()->contents().empty());
272   EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
273   EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
274   EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
275                              answer->description()));
276 }
277 
278 // When encryption is disabled, the SDP offer/answer should have neither a DTLS
279 // fingerprint nor any SDES crypto options.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenEncryptionDisabled)280 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
281   PeerConnectionFactoryInterface::Options options;
282   options.disable_encryption = true;
283   pc_factory_->SetOptions(options);
284 
285   RTCConfiguration config;
286   config.enable_dtls_srtp.emplace(false);
287   auto caller = CreatePeerConnectionWithAudioVideo(config);
288 
289   auto offer = caller->CreateOffer();
290   ASSERT_TRUE(offer);
291 
292   ASSERT_FALSE(offer->description()->contents().empty());
293   EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
294   EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
295   EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
296                              offer->description()));
297 }
298 
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenEncryptionDisabled)299 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
300   PeerConnectionFactoryInterface::Options options;
301   options.disable_encryption = true;
302   pc_factory_->SetOptions(options);
303 
304   RTCConfiguration config;
305   config.enable_dtls_srtp.emplace(false);
306   auto caller = CreatePeerConnectionWithAudioVideo(config);
307   auto callee = CreatePeerConnectionWithAudioVideo(config);
308 
309   callee->SetRemoteDescription(caller->CreateOffer());
310   auto answer = callee->CreateAnswer();
311   ASSERT_TRUE(answer);
312 
313   ASSERT_FALSE(answer->description()->contents().empty());
314   EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
315   EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
316   EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
317                              answer->description()));
318 }
319 
320 // CryptoOptions has been promoted to RTCConfiguration. As such if it is ever
321 // set in the configuration it should overrite the settings set in the factory.
TEST_P(PeerConnectionCryptoTest,RTCConfigurationCryptoOptionOverridesFactory)322 TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) {
323   PeerConnectionFactoryInterface::Options options;
324   options.crypto_options.srtp.enable_gcm_crypto_suites = true;
325   pc_factory_->SetOptions(options);
326 
327   RTCConfiguration config;
328   config.enable_dtls_srtp.emplace(false);
329   CryptoOptions crypto_options;
330   crypto_options.srtp.enable_gcm_crypto_suites = false;
331   config.crypto_options = crypto_options;
332   auto caller = CreatePeerConnectionWithAudioVideo(config);
333 
334   auto offer = caller->CreateOffer();
335   ASSERT_TRUE(offer);
336 
337   ASSERT_FALSE(offer->description()->contents().empty());
338   // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm
339   EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
340 }
341 
342 // When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
343 // should have the correct ciphers in the SDES crypto options.
344 // With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
345 // in the answer.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWithSdesAndGcm)346 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
347   PeerConnectionFactoryInterface::Options options;
348   options.crypto_options.srtp.enable_gcm_crypto_suites = true;
349   pc_factory_->SetOptions(options);
350 
351   RTCConfiguration config;
352   config.enable_dtls_srtp.emplace(false);
353   auto caller = CreatePeerConnectionWithAudioVideo(config);
354 
355   auto offer = caller->CreateOffer();
356   ASSERT_TRUE(offer);
357 
358   ASSERT_FALSE(offer->description()->contents().empty());
359   EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
360 }
361 
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWithSdesAndGcm)362 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
363   PeerConnectionFactoryInterface::Options options;
364   options.crypto_options.srtp.enable_gcm_crypto_suites = true;
365   pc_factory_->SetOptions(options);
366 
367   RTCConfiguration config;
368   config.enable_dtls_srtp.emplace(false);
369   auto caller = CreatePeerConnectionWithAudioVideo(config);
370   auto callee = CreatePeerConnectionWithAudioVideo(config);
371 
372   auto offer = caller->CreateOffer();
373   for (cricket::ContentInfo& content : offer->description()->contents()) {
374     auto cryptos = content.media_description()->cryptos();
375     cryptos.erase(cryptos.begin());  // Assumes that non-GCM is the default.
376     content.media_description()->set_cryptos(cryptos);
377   }
378 
379   callee->SetRemoteDescription(std::move(offer));
380   auto answer = callee->CreateAnswer();
381   ASSERT_TRUE(answer);
382 
383   ASSERT_FALSE(answer->description()->contents().empty());
384   EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
385 }
386 
TEST_P(PeerConnectionCryptoTest,CanSetSdesGcmRemoteOfferAndLocalAnswer)387 TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
388   PeerConnectionFactoryInterface::Options options;
389   options.crypto_options.srtp.enable_gcm_crypto_suites = true;
390   pc_factory_->SetOptions(options);
391 
392   RTCConfiguration config;
393   config.enable_dtls_srtp.emplace(false);
394   auto caller = CreatePeerConnectionWithAudioVideo(config);
395   auto callee = CreatePeerConnectionWithAudioVideo(config);
396 
397   auto offer = caller->CreateOffer();
398   ASSERT_TRUE(offer);
399   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
400 
401   auto answer = callee->CreateAnswer();
402   ASSERT_TRUE(answer);
403   ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
404 }
405 
406 // The following group tests that two PeerConnections can successfully exchange
407 // an offer/answer when DTLS is off and that they will refuse any offer/answer
408 // applied locally/remotely if it does not include SDES cryptos.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenSdesOn)409 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
410   RTCConfiguration config;
411   config.enable_dtls_srtp.emplace(false);
412   auto caller = CreatePeerConnectionWithAudioVideo(config);
413   auto callee = CreatePeerConnectionWithAudioVideo(config);
414 
415   auto offer = caller->CreateOfferAndSetAsLocal();
416   ASSERT_TRUE(offer);
417   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
418 
419   auto answer = callee->CreateAnswerAndSetAsLocal();
420   ASSERT_TRUE(answer);
421   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
422 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalOfferWithNoCryptosWhenSdesOn)423 TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
424   RTCConfiguration config;
425   config.enable_dtls_srtp.emplace(false);
426   auto caller = CreatePeerConnectionWithAudioVideo(config);
427 
428   auto offer = caller->CreateOffer();
429   SdpContentsForEach(RemoveSdesCryptos(), offer->description());
430 
431   EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
432 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteOfferWithNoCryptosWhenSdesOn)433 TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
434   RTCConfiguration config;
435   config.enable_dtls_srtp.emplace(false);
436   auto caller = CreatePeerConnectionWithAudioVideo(config);
437   auto callee = CreatePeerConnectionWithAudioVideo(config);
438 
439   auto offer = caller->CreateOffer();
440   SdpContentsForEach(RemoveSdesCryptos(), offer->description());
441 
442   EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
443 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalAnswerWithNoCryptosWhenSdesOn)444 TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
445   RTCConfiguration config;
446   config.enable_dtls_srtp.emplace(false);
447   auto caller = CreatePeerConnectionWithAudioVideo(config);
448   auto callee = CreatePeerConnectionWithAudioVideo(config);
449 
450   callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
451   auto answer = callee->CreateAnswer();
452   SdpContentsForEach(RemoveSdesCryptos(), answer->description());
453 
454   EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
455 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteAnswerWithNoCryptosWhenSdesOn)456 TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
457   RTCConfiguration config;
458   config.enable_dtls_srtp.emplace(false);
459   auto caller = CreatePeerConnectionWithAudioVideo(config);
460   auto callee = CreatePeerConnectionWithAudioVideo(config);
461 
462   callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
463   auto answer = callee->CreateAnswerAndSetAsLocal();
464   SdpContentsForEach(RemoveSdesCryptos(), answer->description());
465 
466   EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
467 }
468 #endif
469 
470 // The following group tests that two PeerConnections can successfully exchange
471 // an offer/answer when DTLS is on and that they will refuse any offer/answer
472 // applied locally/remotely if it does not include a DTLS fingerprint.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenDtlsOn)473 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
474   RTCConfiguration config;
475   auto caller = CreatePeerConnectionWithAudioVideo(config);
476   auto callee = CreatePeerConnectionWithAudioVideo(config);
477 
478   auto offer = caller->CreateOfferAndSetAsLocal();
479   ASSERT_TRUE(offer);
480   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
481 
482   auto answer = callee->CreateAnswerAndSetAsLocal();
483   ASSERT_TRUE(answer);
484   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
485 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalOfferWithNoFingerprintWhenDtlsOn)486 TEST_P(PeerConnectionCryptoTest,
487        FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
488   RTCConfiguration config;
489   auto caller = CreatePeerConnectionWithAudioVideo(config);
490 
491   auto offer = caller->CreateOffer();
492   SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
493 
494   EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
495 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn)496 TEST_P(PeerConnectionCryptoTest,
497        FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
498   RTCConfiguration config;
499   auto caller = CreatePeerConnectionWithAudioVideo(config);
500   auto callee = CreatePeerConnectionWithAudioVideo(config);
501 
502   auto offer = caller->CreateOffer();
503   SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
504 
505   EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
506 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn)507 TEST_P(PeerConnectionCryptoTest,
508        FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
509   RTCConfiguration config;
510   auto caller = CreatePeerConnectionWithAudioVideo(config);
511   auto callee = CreatePeerConnectionWithAudioVideo(config);
512 
513   callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
514   auto answer = callee->CreateAnswer();
515   SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
516 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn)517 TEST_P(PeerConnectionCryptoTest,
518        FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
519   RTCConfiguration config;
520   auto caller = CreatePeerConnectionWithAudioVideo(config);
521   auto callee = CreatePeerConnectionWithAudioVideo(config);
522 
523   callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
524   auto answer = callee->CreateAnswerAndSetAsLocal();
525   SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
526 
527   EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
528 }
529 
530 #if defined(WEBRTC_FUCHSIA)
531 // Test that an offer/answer can be exchanged when encryption is disabled.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenNoEncryption)532 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
533   PeerConnectionFactoryInterface::Options options;
534   options.disable_encryption = true;
535   pc_factory_->SetOptions(options);
536 
537   RTCConfiguration config;
538   config.enable_dtls_srtp.emplace(false);
539   auto caller = CreatePeerConnectionWithAudioVideo(config);
540   auto callee = CreatePeerConnectionWithAudioVideo(config);
541 
542   auto offer = caller->CreateOfferAndSetAsLocal();
543   ASSERT_TRUE(offer);
544   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
545 
546   auto answer = callee->CreateAnswerAndSetAsLocal();
547   ASSERT_TRUE(answer);
548   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
549 }
550 #endif
551 
552 // Tests that a DTLS call can be established when the certificate is specified
553 // in the PeerConnection config and no certificate generator is specified.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenDtlsCertificateInConfig)554 TEST_P(PeerConnectionCryptoTest,
555        ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
556   RTCConfiguration caller_config;
557   caller_config.certificates.push_back(
558       FakeRTCCertificateGenerator::GenerateCertificate());
559   auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
560 
561   RTCConfiguration callee_config;
562   callee_config.certificates.push_back(
563       FakeRTCCertificateGenerator::GenerateCertificate());
564   auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
565 
566   auto offer = caller->CreateOfferAndSetAsLocal();
567   ASSERT_TRUE(offer);
568   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
569 
570   auto answer = callee->CreateAnswerAndSetAsLocal();
571   ASSERT_TRUE(answer);
572   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
573 }
574 
575 // The following parameterized test verifies that CreateOffer/CreateAnswer
576 // returns successfully (or with failure if the underlying certificate generator
577 // fails) no matter when the DTLS certificate is generated. If multiple
578 // CreateOffer/CreateAnswer calls are made while waiting for the certificate,
579 // they all finish after the certificate is generated.
580 
581 // Whether the certificate will be generated before calling CreateOffer or
582 // while CreateOffer is executing.
583 enum class CertGenTime { kBefore, kDuring };
operator <<(std::ostream & out,CertGenTime value)584 std::ostream& operator<<(std::ostream& out, CertGenTime value) {
585   switch (value) {
586     case CertGenTime::kBefore:
587       return out << "before";
588     case CertGenTime::kDuring:
589       return out << "during";
590     default:
591       return out << "unknown";
592   }
593 }
594 
595 // Whether the fake certificate generator will produce a certificate or fail.
596 enum class CertGenResult { kSucceed, kFail };
operator <<(std::ostream & out,CertGenResult value)597 std::ostream& operator<<(std::ostream& out, CertGenResult value) {
598   switch (value) {
599     case CertGenResult::kSucceed:
600       return out << "succeed";
601     case CertGenResult::kFail:
602       return out << "fail";
603     default:
604       return out << "unknown";
605   }
606 }
607 
608 class PeerConnectionCryptoDtlsCertGenTest
609     : public PeerConnectionCryptoBaseTest,
610       public ::testing::WithParamInterface<std::tuple<SdpSemantics,
611                                                       SdpType,
612                                                       CertGenTime,
613                                                       CertGenResult,
614                                                       size_t>> {
615  protected:
PeerConnectionCryptoDtlsCertGenTest()616   PeerConnectionCryptoDtlsCertGenTest()
617       : PeerConnectionCryptoBaseTest(std::get<0>(GetParam())) {
618     sdp_type_ = std::get<1>(GetParam());
619     cert_gen_time_ = std::get<2>(GetParam());
620     cert_gen_result_ = std::get<3>(GetParam());
621     concurrent_calls_ = std::get<4>(GetParam());
622   }
623 
624   SdpType sdp_type_;
625   CertGenTime cert_gen_time_;
626   CertGenResult cert_gen_result_;
627   size_t concurrent_calls_;
628 };
629 
TEST_P(PeerConnectionCryptoDtlsCertGenTest,TestCertificateGeneration)630 TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
631   RTCConfiguration config;
632   auto owned_fake_certificate_generator =
633       std::make_unique<FakeRTCCertificateGenerator>();
634   auto* fake_certificate_generator = owned_fake_certificate_generator.get();
635   fake_certificate_generator->set_should_fail(cert_gen_result_ ==
636                                               CertGenResult::kFail);
637   fake_certificate_generator->set_should_wait(cert_gen_time_ ==
638                                               CertGenTime::kDuring);
639   WrapperPtr pc;
640   if (sdp_type_ == SdpType::kOffer) {
641     pc = CreatePeerConnectionWithAudioVideo(
642         config, std::move(owned_fake_certificate_generator));
643   } else {
644     auto caller = CreatePeerConnectionWithAudioVideo(config);
645     pc = CreatePeerConnectionWithAudioVideo(
646         config, std::move(owned_fake_certificate_generator));
647     pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
648   }
649   if (cert_gen_time_ == CertGenTime::kBefore) {
650     ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
651                              fake_certificate_generator->generated_failures() >
652                          0,
653                      kGenerateCertTimeout);
654   } else {
655     ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
656     fake_certificate_generator->set_should_wait(false);
657   }
658   std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
659       observers;
660   for (size_t i = 0; i < concurrent_calls_; i++) {
661     rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
662         rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
663     observers.push_back(observer);
664     if (sdp_type_ == SdpType::kOffer) {
665       pc->pc()->CreateOffer(observer.get(),
666                             PeerConnectionInterface::RTCOfferAnswerOptions());
667     } else {
668       pc->pc()->CreateAnswer(observer.get(),
669                              PeerConnectionInterface::RTCOfferAnswerOptions());
670     }
671   }
672   for (auto& observer : observers) {
673     EXPECT_TRUE_WAIT(observer->called(), 1000);
674     if (cert_gen_result_ == CertGenResult::kSucceed) {
675       EXPECT_TRUE(observer->result());
676     } else {
677       EXPECT_FALSE(observer->result());
678     }
679   }
680 }
681 
682 INSTANTIATE_TEST_SUITE_P(
683     PeerConnectionCryptoTest,
684     PeerConnectionCryptoDtlsCertGenTest,
685     Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
686             Values(SdpType::kOffer, SdpType::kAnswer),
687             Values(CertGenTime::kBefore, CertGenTime::kDuring),
688             Values(CertGenResult::kSucceed, CertGenResult::kFail),
689             Values(1, 3)));
690 
691 // Test that we can create and set an answer correctly when different
692 // SSL roles have been negotiated for different transports.
693 // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
TEST_P(PeerConnectionCryptoTest,CreateAnswerWithDifferentSslRoles)694 TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
695   auto caller = CreatePeerConnectionWithAudioVideo();
696   auto callee = CreatePeerConnectionWithAudioVideo();
697 
698   RTCOfferAnswerOptions options_no_bundle;
699   options_no_bundle.use_rtp_mux = false;
700 
701   // First, negotiate different SSL roles for audio and video.
702   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
703   auto answer = callee->CreateAnswer(options_no_bundle);
704 
705   AudioConnectionRole(answer->description()) = cricket::CONNECTIONROLE_ACTIVE;
706   VideoConnectionRole(answer->description()) = cricket::CONNECTIONROLE_PASSIVE;
707 
708   ASSERT_TRUE(
709       callee->SetLocalDescription(CloneSessionDescription(answer.get())));
710   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
711 
712   // Now create an offer in the reverse direction, and ensure the initial
713   // offerer responds with an answer with the correct SSL roles.
714   ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
715   answer = caller->CreateAnswer(options_no_bundle);
716 
717   EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
718             AudioConnectionRole(answer->description()));
719   EXPECT_EQ(cricket::CONNECTIONROLE_ACTIVE,
720             VideoConnectionRole(answer->description()));
721 
722   ASSERT_TRUE(
723       caller->SetLocalDescription(CloneSessionDescription(answer.get())));
724   ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
725 
726   // Lastly, start BUNDLE-ing on "audio", expecting that the "passive" role of
727   // audio is transferred over to video in the answer that completes the BUNDLE
728   // negotiation.
729   RTCOfferAnswerOptions options_bundle;
730   options_bundle.use_rtp_mux = true;
731 
732   ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateOfferAndSetAsLocal()));
733   answer = caller->CreateAnswer(options_bundle);
734 
735   EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
736             AudioConnectionRole(answer->description()));
737   EXPECT_EQ(cricket::CONNECTIONROLE_PASSIVE,
738             VideoConnectionRole(answer->description()));
739 
740   ASSERT_TRUE(
741       caller->SetLocalDescription(CloneSessionDescription(answer.get())));
742   ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
743 }
744 
745 // Tests that if the DTLS fingerprint is invalid then all future calls to
746 // SetLocalDescription and SetRemoteDescription will fail due to a session
747 // error.
748 // This is a regression test for crbug.com/800775
TEST_P(PeerConnectionCryptoTest,SessionErrorIfFingerprintInvalid)749 TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) {
750   auto callee_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[0]);
751   auto other_certificate = rtc::RTCCertificate::FromPEM(kRsaPems[1]);
752 
753   auto caller = CreatePeerConnectionWithAudioVideo();
754   RTCConfiguration callee_config;
755   callee_config.certificates.push_back(callee_certificate);
756   auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
757 
758   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
759 
760   // Create an invalid answer with the other certificate's fingerprint.
761   auto valid_answer = callee->CreateAnswer();
762   auto invalid_answer = CloneSessionDescription(valid_answer.get());
763   auto* audio_content =
764       cricket::GetFirstAudioContent(invalid_answer->description());
765   ASSERT_TRUE(audio_content);
766   auto* audio_transport_info =
767       invalid_answer->description()->GetTransportInfoByName(
768           audio_content->name);
769   ASSERT_TRUE(audio_transport_info);
770   audio_transport_info->description.identity_fingerprint =
771       rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
772 
773   // Set the invalid answer and expect a fingerprint error.
774   std::string error;
775   ASSERT_FALSE(callee->SetLocalDescription(std::move(invalid_answer), &error));
776   EXPECT_PRED_FORMAT2(AssertStringContains, error,
777                       "Local fingerprint does not match identity.");
778 
779   // Make sure that setting a valid remote offer or local answer also fails now.
780   ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
781   EXPECT_PRED_FORMAT2(AssertStringContains, error,
782                       "Session error code: ERROR_CONTENT.");
783   ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
784   EXPECT_PRED_FORMAT2(AssertStringContains, error,
785                       "Session error code: ERROR_CONTENT.");
786 }
787 
788 INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
789                          PeerConnectionCryptoTest,
790                          Values(SdpSemantics::kPlanB_DEPRECATED,
791                                 SdpSemantics::kUnifiedPlan));
792 
793 }  // namespace webrtc
794