• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory>
12 
13 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
14 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
15 #include "api/create_peerconnection_factory.h"
16 #include "api/video_codecs/builtin_video_decoder_factory.h"
17 #include "api/video_codecs/builtin_video_encoder_factory.h"
18 #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"
22 #ifdef WEBRTC_ANDROID
23 #include "pc/test/android_test_initializer.h"
24 #endif
25 #include "pc/test/fake_audio_capture_module.h"
26 #include "pc/test/fake_rtc_certificate_generator.h"
27 #include "rtc_base/gunit.h"
28 #include "rtc_base/virtual_socket_server.h"
29 
30 namespace webrtc {
31 
32 using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
33 using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
34 using ::testing::Combine;
35 using ::testing::Values;
36 
37 constexpr int kGenerateCertTimeout = 1000;
38 
39 class PeerConnectionCryptoBaseTest : public ::testing::Test {
40  protected:
41   typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
42 
PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)43   explicit PeerConnectionCryptoBaseTest(SdpSemantics sdp_semantics)
44       : vss_(new rtc::VirtualSocketServer()),
45         main_(vss_.get()),
46         sdp_semantics_(sdp_semantics) {
47 #ifdef WEBRTC_ANDROID
48     InitializeAndroidObjects();
49 #endif
50     pc_factory_ = CreatePeerConnectionFactory(
51         rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
52         FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
53         CreateBuiltinAudioDecoderFactory(), CreateBuiltinVideoEncoderFactory(),
54         CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
55         nullptr /* audio_processing */);
56   }
57 
CreatePeerConnection()58   WrapperPtr CreatePeerConnection() {
59     return CreatePeerConnection(RTCConfiguration());
60   }
61 
CreatePeerConnection(const RTCConfiguration & config)62   WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
63     return CreatePeerConnection(config, nullptr);
64   }
65 
CreatePeerConnection(const RTCConfiguration & config,std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen)66   WrapperPtr CreatePeerConnection(
67       const RTCConfiguration& config,
68       std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
69     auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
70         rtc::Thread::Current(), nullptr);
71     auto observer = std::make_unique<MockPeerConnectionObserver>();
72     RTCConfiguration modified_config = config;
73     modified_config.sdp_semantics = sdp_semantics_;
74     auto pc = pc_factory_->CreatePeerConnection(
75         modified_config, std::move(fake_port_allocator), std::move(cert_gen),
76         observer.get());
77     if (!pc) {
78       return nullptr;
79     }
80 
81     observer->SetPeerConnectionInterface(pc.get());
82     return std::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
83                                                    std::move(observer));
84   }
85 
86   // Accepts the same arguments as CreatePeerConnection and adds default audio
87   // and video tracks.
88   template <typename... Args>
CreatePeerConnectionWithAudioVideo(Args &&...args)89   WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
90     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
91     if (!wrapper) {
92       return nullptr;
93     }
94     wrapper->AddAudioTrack("a");
95     wrapper->AddVideoTrack("v");
96     return wrapper;
97   }
98 
AudioConnectionRole(cricket::SessionDescription * desc)99   cricket::ConnectionRole& AudioConnectionRole(
100       cricket::SessionDescription* desc) {
101     return ConnectionRoleFromContent(desc, cricket::GetFirstAudioContent(desc));
102   }
103 
VideoConnectionRole(cricket::SessionDescription * desc)104   cricket::ConnectionRole& VideoConnectionRole(
105       cricket::SessionDescription* desc) {
106     return ConnectionRoleFromContent(desc, cricket::GetFirstVideoContent(desc));
107   }
108 
ConnectionRoleFromContent(cricket::SessionDescription * desc,cricket::ContentInfo * content)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 
118   std::unique_ptr<rtc::VirtualSocketServer> vss_;
119   rtc::AutoSocketServerThread main_;
120   rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
121   const SdpSemantics sdp_semantics_;
122 };
123 
HaveDtlsFingerprint()124 SdpContentPredicate HaveDtlsFingerprint() {
125   return [](const cricket::ContentInfo* content,
126             const cricket::TransportInfo* transport) {
127     return transport->description.identity_fingerprint != nullptr;
128   };
129 }
130 
HaveSdesCryptos()131 SdpContentPredicate HaveSdesCryptos() {
132   return [](const cricket::ContentInfo* content,
133             const cricket::TransportInfo* transport) {
134     return !content->media_description()->cryptos().empty();
135   };
136 }
137 
HaveProtocol(const std::string & protocol)138 SdpContentPredicate HaveProtocol(const std::string& protocol) {
139   return [protocol](const cricket::ContentInfo* content,
140                     const cricket::TransportInfo* transport) {
141     return content->media_description()->protocol() == protocol;
142   };
143 }
144 
HaveSdesGcmCryptos(size_t num_crypto_suites)145 SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
146   return [num_crypto_suites](const cricket::ContentInfo* content,
147                              const cricket::TransportInfo* transport) {
148     const auto& cryptos = content->media_description()->cryptos();
149     if (cryptos.size() != num_crypto_suites) {
150       return false;
151     }
152     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;
158   };
159 }
160 
161 class PeerConnectionCryptoTest
162     : public PeerConnectionCryptoBaseTest,
163       public ::testing::WithParamInterface<SdpSemantics> {
164  protected:
PeerConnectionCryptoTest()165   PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
166 };
167 
RemoveSdesCryptos()168 SdpContentMutator RemoveSdesCryptos() {
169   return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
170     content->media_description()->set_cryptos({});
171   };
172 }
173 
RemoveDtlsFingerprint()174 SdpContentMutator 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.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenDtlsEnabled)182 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
183   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 }
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenDtlsEnabled)196 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
197   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.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenDtlsDisabled)215 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) {
216   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 }
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenDtlsDisabled)229 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
230   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.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWhenEncryptionDisabled)248 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
249   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 }
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWhenEncryptionDisabled)266 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
267   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 
287 // 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.
TEST_P(PeerConnectionCryptoTest,RTCConfigurationCryptoOptionOverridesFactory)289 TEST_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 
309 // 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.
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInOfferWithSdesAndGcm)313 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) {
314   PeerConnectionFactoryInterface::Options options;
315   options.crypto_options.srtp.enable_gcm_crypto_suites = true;
316   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 }
328 
TEST_P(PeerConnectionCryptoTest,CorrectCryptoInAnswerWithSdesAndGcm)329 TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) {
330   PeerConnectionFactoryInterface::Options options;
331   options.crypto_options.srtp.enable_gcm_crypto_suites = true;
332   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 
339   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));
347   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 
TEST_P(PeerConnectionCryptoTest,CanSetSdesGcmRemoteOfferAndLocalAnswer)354 TEST_P(PeerConnectionCryptoTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
355   PeerConnectionFactoryInterface::Options options;
356   options.crypto_options.srtp.enable_gcm_crypto_suites = true;
357   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.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenSdesOn)376 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenSdesOn) {
377   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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalOfferWithNoCryptosWhenSdesOn)390 TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
391   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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteOfferWithNoCryptosWhenSdesOn)400 TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
401   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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalAnswerWithNoCryptosWhenSdesOn)411 TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
412   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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteAnswerWithNoCryptosWhenSdesOn)423 TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
424   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.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenDtlsOn)439 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) {
440   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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalOfferWithNoFingerprintWhenDtlsOn)453 TEST_P(PeerConnectionCryptoTest,
454        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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn)464 TEST_P(PeerConnectionCryptoTest,
465        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 }
TEST_P(PeerConnectionCryptoTest,FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn)476 TEST_P(PeerConnectionCryptoTest,
477        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 }
TEST_P(PeerConnectionCryptoTest,FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn)487 TEST_P(PeerConnectionCryptoTest,
488        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.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenNoEncryption)502 TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) {
503   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.
TEST_P(PeerConnectionCryptoTest,ExchangeOfferAnswerWhenDtlsCertificateInConfig)523 TEST_P(PeerConnectionCryptoTest,
524        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 
552 // Whether the certificate will be generated before calling CreateOffer or
553 // while CreateOffer is executing.
554 enum class CertGenTime { kBefore, kDuring };
operator <<(std::ostream & out,CertGenTime value)555 std::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.
567 enum class CertGenResult { kSucceed, kFail };
operator <<(std::ostream & out,CertGenResult value)568 std::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 
579 class PeerConnectionCryptoDtlsCertGenTest
580     : public PeerConnectionCryptoBaseTest,
581       public ::testing::WithParamInterface<std::tuple<SdpSemantics,
582                                                       SdpType,
583                                                       CertGenTime,
584                                                       CertGenResult,
585                                                       size_t>> {
586  protected:
PeerConnectionCryptoDtlsCertGenTest()587   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());
593   }
594 
595   SdpType sdp_type_;
596   CertGenTime cert_gen_time_;
597   CertGenResult cert_gen_result_;
598   size_t concurrent_calls_;
599 };
600 
TEST_P(PeerConnectionCryptoDtlsCertGenTest,TestCertificateGeneration)601 TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) {
602   RTCConfiguration config;
603   config.enable_dtls_srtp.emplace(true);
604   auto owned_fake_certificate_generator =
605       std::make_unique<FakeRTCCertificateGenerator>();
606   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) {
637       pc->pc()->CreateOffer(observer,
638                             PeerConnectionInterface::RTCOfferAnswerOptions());
639     } else {
640       pc->pc()->CreateAnswer(observer,
641                              PeerConnectionInterface::RTCOfferAnswerOptions());
642     }
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 
654 INSTANTIATE_TEST_SUITE_P(
655     PeerConnectionCryptoTest,
656     PeerConnectionCryptoDtlsCertGenTest,
657     Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
658             Values(SdpType::kOffer, SdpType::kAnswer),
659             Values(CertGenTime::kBefore, CertGenTime::kDuring),
660             Values(CertGenResult::kSucceed, CertGenResult::kFail),
661             Values(1, 3)));
662 
663 // 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
TEST_P(PeerConnectionCryptoTest,CreateAnswerWithDifferentSslRoles)666 TEST_P(PeerConnectionCryptoTest, CreateAnswerWithDifferentSslRoles) {
667   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 
717 // 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
TEST_P(PeerConnectionCryptoTest,SessionErrorIfFingerprintInvalid)721 TEST_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.
734   auto valid_answer = callee->CreateAnswer();
735   auto invalid_answer = CloneSessionDescription(valid_answer.get());
736   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);
743   audio_transport_info->description.identity_fingerprint =
744       rtc::SSLFingerprint::CreateFromCertificate(*other_certificate);
745 
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.");
756   ASSERT_FALSE(callee->SetLocalDescription(std::move(valid_answer), &error));
757   EXPECT_PRED_FORMAT2(AssertStringContains, error,
758                       "Session error code: ERROR_CONTENT.");
759 }
760 
761 INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest,
762                          PeerConnectionCryptoTest,
763                          Values(SdpSemantics::kPlanB,
764                                 SdpSemantics::kUnifiedPlan));
765 
766 }  // namespace webrtc
767