1 /*
2 * Copyright 2012 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 "p2p/base/transport_description_factory.h"
12
13 #include <stddef.h>
14
15 #include <memory>
16 #include <string>
17 #include <vector>
18
19 #include "absl/strings/string_view.h"
20 #include "p2p/base/p2p_constants.h"
21 #include "p2p/base/transport_description.h"
22 #include "rtc_base/copy_on_write_buffer.h"
23 #include "rtc_base/fake_ssl_identity.h"
24 #include "rtc_base/ssl_certificate.h"
25 #include "rtc_base/ssl_fingerprint.h"
26 #include "rtc_base/ssl_identity.h"
27 #include "test/gmock.h"
28 #include "test/gtest.h"
29 #include "test/scoped_key_value_config.h"
30
31 using cricket::TransportDescription;
32 using cricket::TransportDescriptionFactory;
33 using cricket::TransportOptions;
34 using ::testing::Contains;
35 using ::testing::Not;
36
37 class TransportDescriptionFactoryTest : public ::testing::Test {
38 public:
TransportDescriptionFactoryTest()39 TransportDescriptionFactoryTest()
40 : ice_credentials_({}),
41 f1_(field_trials_),
42 f2_(field_trials_),
43 cert1_(rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
44 new rtc::FakeSSLIdentity("User1")))),
45 cert2_(rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
46 new rtc::FakeSSLIdentity("User2")))) {}
47
CheckDesc(const TransportDescription * desc,absl::string_view opt,absl::string_view ice_ufrag,absl::string_view ice_pwd,absl::string_view dtls_alg)48 void CheckDesc(const TransportDescription* desc,
49 absl::string_view opt,
50 absl::string_view ice_ufrag,
51 absl::string_view ice_pwd,
52 absl::string_view dtls_alg) {
53 ASSERT_TRUE(desc != NULL);
54 EXPECT_EQ(!opt.empty(), desc->HasOption(opt));
55 if (ice_ufrag.empty() && ice_pwd.empty()) {
56 EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
57 desc->ice_ufrag.size());
58 EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
59 desc->ice_pwd.size());
60 } else {
61 EXPECT_EQ(ice_ufrag, desc->ice_ufrag);
62 EXPECT_EQ(ice_pwd, desc->ice_pwd);
63 }
64 if (dtls_alg.empty()) {
65 EXPECT_TRUE(desc->identity_fingerprint.get() == NULL);
66 } else {
67 ASSERT_TRUE(desc->identity_fingerprint.get() != NULL);
68 EXPECT_EQ(desc->identity_fingerprint->algorithm, dtls_alg);
69 EXPECT_GT(desc->identity_fingerprint->digest.size(), 0U);
70 }
71 }
72
73 // This test ice restart by doing two offer answer exchanges. On the second
74 // exchange ice is restarted. The test verifies that the ufrag and password
75 // in the offer and answer is changed.
76 // If `dtls` is true, the test verifies that the finger print is not changed.
TestIceRestart(bool dtls)77 void TestIceRestart(bool dtls) {
78 SetDtls(dtls);
79 cricket::TransportOptions options;
80 // The initial offer / answer exchange.
81 std::unique_ptr<TransportDescription> offer =
82 f1_.CreateOffer(options, NULL, &ice_credentials_);
83 std::unique_ptr<TransportDescription> answer =
84 f2_.CreateAnswer(offer.get(), options, true, NULL, &ice_credentials_);
85
86 // Create an updated offer where we restart ice.
87 options.ice_restart = true;
88 std::unique_ptr<TransportDescription> restart_offer =
89 f1_.CreateOffer(options, offer.get(), &ice_credentials_);
90
91 VerifyUfragAndPasswordChanged(dtls, offer.get(), restart_offer.get());
92
93 // Create a new answer. The transport ufrag and password is changed since
94 // |options.ice_restart == true|
95 std::unique_ptr<TransportDescription> restart_answer = f2_.CreateAnswer(
96 restart_offer.get(), options, true, answer.get(), &ice_credentials_);
97 ASSERT_TRUE(restart_answer.get() != NULL);
98
99 VerifyUfragAndPasswordChanged(dtls, answer.get(), restart_answer.get());
100 }
101
VerifyUfragAndPasswordChanged(bool dtls,const TransportDescription * org_desc,const TransportDescription * restart_desc)102 void VerifyUfragAndPasswordChanged(bool dtls,
103 const TransportDescription* org_desc,
104 const TransportDescription* restart_desc) {
105 EXPECT_NE(org_desc->ice_pwd, restart_desc->ice_pwd);
106 EXPECT_NE(org_desc->ice_ufrag, restart_desc->ice_ufrag);
107 EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
108 restart_desc->ice_ufrag.size());
109 EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
110 restart_desc->ice_pwd.size());
111 // If DTLS is enabled, make sure the finger print is unchanged.
112 if (dtls) {
113 EXPECT_FALSE(
114 org_desc->identity_fingerprint->GetRfc4572Fingerprint().empty());
115 EXPECT_EQ(org_desc->identity_fingerprint->GetRfc4572Fingerprint(),
116 restart_desc->identity_fingerprint->GetRfc4572Fingerprint());
117 }
118 }
119
TestIceRenomination(bool dtls)120 void TestIceRenomination(bool dtls) {
121 SetDtls(dtls);
122
123 cricket::TransportOptions options;
124 // The initial offer / answer exchange.
125 std::unique_ptr<TransportDescription> offer =
126 f1_.CreateOffer(options, nullptr, &ice_credentials_);
127 ASSERT_TRUE(offer);
128 EXPECT_THAT(offer->transport_options, Not(Contains("renomination")));
129
130 std::unique_ptr<TransportDescription> answer = f2_.CreateAnswer(
131 offer.get(), options, true, nullptr, &ice_credentials_);
132 ASSERT_TRUE(answer);
133 EXPECT_THAT(answer->transport_options, Not(Contains("renomination")));
134
135 options.enable_ice_renomination = true;
136 std::unique_ptr<TransportDescription> renomination_offer =
137 f1_.CreateOffer(options, offer.get(), &ice_credentials_);
138 ASSERT_TRUE(renomination_offer);
139 EXPECT_THAT(renomination_offer->transport_options,
140 Contains("renomination"));
141
142 std::unique_ptr<TransportDescription> renomination_answer =
143 f2_.CreateAnswer(renomination_offer.get(), options, true, answer.get(),
144 &ice_credentials_);
145 ASSERT_TRUE(renomination_answer);
146 EXPECT_THAT(renomination_answer->transport_options,
147 Contains("renomination"));
148 }
149
150 protected:
SetDtls(bool dtls)151 void SetDtls(bool dtls) {
152 if (dtls) {
153 f1_.set_secure(cricket::SEC_ENABLED);
154 f2_.set_secure(cricket::SEC_ENABLED);
155 f1_.set_certificate(cert1_);
156 f2_.set_certificate(cert2_);
157 } else {
158 f1_.set_secure(cricket::SEC_DISABLED);
159 f2_.set_secure(cricket::SEC_DISABLED);
160 }
161 }
162
163 webrtc::test::ScopedKeyValueConfig field_trials_;
164 cricket::IceCredentialsIterator ice_credentials_;
165 TransportDescriptionFactory f1_;
166 TransportDescriptionFactory f2_;
167
168 rtc::scoped_refptr<rtc::RTCCertificate> cert1_;
169 rtc::scoped_refptr<rtc::RTCCertificate> cert2_;
170 };
171
TEST_F(TransportDescriptionFactoryTest,TestOfferDefault)172 TEST_F(TransportDescriptionFactoryTest, TestOfferDefault) {
173 std::unique_ptr<TransportDescription> desc =
174 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
175 CheckDesc(desc.get(), "", "", "", "");
176 }
177
TEST_F(TransportDescriptionFactoryTest,TestOfferDtls)178 TEST_F(TransportDescriptionFactoryTest, TestOfferDtls) {
179 f1_.set_secure(cricket::SEC_ENABLED);
180 f1_.set_certificate(cert1_);
181 std::string digest_alg;
182 ASSERT_TRUE(
183 cert1_->GetSSLCertificate().GetSignatureDigestAlgorithm(&digest_alg));
184 std::unique_ptr<TransportDescription> desc =
185 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
186 CheckDesc(desc.get(), "", "", "", digest_alg);
187 // Ensure it also works with SEC_REQUIRED.
188 f1_.set_secure(cricket::SEC_REQUIRED);
189 desc = f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
190 CheckDesc(desc.get(), "", "", "", digest_alg);
191 }
192
193 // Test generating an offer with DTLS fails with no identity.
TEST_F(TransportDescriptionFactoryTest,TestOfferDtlsWithNoIdentity)194 TEST_F(TransportDescriptionFactoryTest, TestOfferDtlsWithNoIdentity) {
195 f1_.set_secure(cricket::SEC_ENABLED);
196 std::unique_ptr<TransportDescription> desc =
197 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
198 ASSERT_TRUE(desc.get() == NULL);
199 }
200
201 // Test updating an offer with DTLS to pick ICE.
202 // The ICE credentials should stay the same in the new offer.
TEST_F(TransportDescriptionFactoryTest,TestOfferDtlsReofferDtls)203 TEST_F(TransportDescriptionFactoryTest, TestOfferDtlsReofferDtls) {
204 f1_.set_secure(cricket::SEC_ENABLED);
205 f1_.set_certificate(cert1_);
206 std::string digest_alg;
207 ASSERT_TRUE(
208 cert1_->GetSSLCertificate().GetSignatureDigestAlgorithm(&digest_alg));
209 std::unique_ptr<TransportDescription> old_desc =
210 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
211 ASSERT_TRUE(old_desc.get() != NULL);
212 std::unique_ptr<TransportDescription> desc =
213 f1_.CreateOffer(TransportOptions(), old_desc.get(), &ice_credentials_);
214 CheckDesc(desc.get(), "", old_desc->ice_ufrag, old_desc->ice_pwd, digest_alg);
215 }
216
TEST_F(TransportDescriptionFactoryTest,TestAnswerDefault)217 TEST_F(TransportDescriptionFactoryTest, TestAnswerDefault) {
218 std::unique_ptr<TransportDescription> offer =
219 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
220 ASSERT_TRUE(offer.get() != NULL);
221 std::unique_ptr<TransportDescription> desc = f2_.CreateAnswer(
222 offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
223 CheckDesc(desc.get(), "", "", "", "");
224 desc = f2_.CreateAnswer(offer.get(), TransportOptions(), true, NULL,
225 &ice_credentials_);
226 CheckDesc(desc.get(), "", "", "", "");
227 }
228
229 // Test that we can update an answer properly; ICE credentials shouldn't change.
TEST_F(TransportDescriptionFactoryTest,TestReanswer)230 TEST_F(TransportDescriptionFactoryTest, TestReanswer) {
231 std::unique_ptr<TransportDescription> offer =
232 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
233 ASSERT_TRUE(offer.get() != NULL);
234 std::unique_ptr<TransportDescription> old_desc = f2_.CreateAnswer(
235 offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
236 ASSERT_TRUE(old_desc.get() != NULL);
237 std::unique_ptr<TransportDescription> desc = f2_.CreateAnswer(
238 offer.get(), TransportOptions(), true, old_desc.get(), &ice_credentials_);
239 ASSERT_TRUE(desc.get() != NULL);
240 CheckDesc(desc.get(), "", old_desc->ice_ufrag, old_desc->ice_pwd, "");
241 }
242
243 // Test that we handle answering an offer with DTLS with no DTLS.
TEST_F(TransportDescriptionFactoryTest,TestAnswerDtlsToNoDtls)244 TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToNoDtls) {
245 f1_.set_secure(cricket::SEC_ENABLED);
246 f1_.set_certificate(cert1_);
247 std::unique_ptr<TransportDescription> offer =
248 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
249 ASSERT_TRUE(offer.get() != NULL);
250 std::unique_ptr<TransportDescription> desc = f2_.CreateAnswer(
251 offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
252 CheckDesc(desc.get(), "", "", "", "");
253 }
254
255 // Test that we handle answering an offer without DTLS if we have DTLS enabled,
256 // but fail if we require DTLS.
TEST_F(TransportDescriptionFactoryTest,TestAnswerNoDtlsToDtls)257 TEST_F(TransportDescriptionFactoryTest, TestAnswerNoDtlsToDtls) {
258 f2_.set_secure(cricket::SEC_ENABLED);
259 f2_.set_certificate(cert2_);
260 std::unique_ptr<TransportDescription> offer =
261 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
262 ASSERT_TRUE(offer.get() != NULL);
263 std::unique_ptr<TransportDescription> desc = f2_.CreateAnswer(
264 offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
265 CheckDesc(desc.get(), "", "", "", "");
266 f2_.set_secure(cricket::SEC_REQUIRED);
267 desc = f2_.CreateAnswer(offer.get(), TransportOptions(), true, NULL,
268 &ice_credentials_);
269 ASSERT_TRUE(desc.get() == NULL);
270 }
271
272 // Test that we handle answering an DTLS offer with DTLS, both if we have
273 // DTLS enabled and required.
TEST_F(TransportDescriptionFactoryTest,TestAnswerDtlsToDtls)274 TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToDtls) {
275 f1_.set_secure(cricket::SEC_ENABLED);
276 f1_.set_certificate(cert1_);
277
278 f2_.set_secure(cricket::SEC_ENABLED);
279 f2_.set_certificate(cert2_);
280 // f2_ produces the answer that is being checked in this test, so the
281 // answer must contain fingerprint lines with cert2_'s digest algorithm.
282 std::string digest_alg2;
283 ASSERT_TRUE(
284 cert2_->GetSSLCertificate().GetSignatureDigestAlgorithm(&digest_alg2));
285
286 std::unique_ptr<TransportDescription> offer =
287 f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
288 ASSERT_TRUE(offer.get() != NULL);
289 std::unique_ptr<TransportDescription> desc = f2_.CreateAnswer(
290 offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
291 CheckDesc(desc.get(), "", "", "", digest_alg2);
292 f2_.set_secure(cricket::SEC_REQUIRED);
293 desc = f2_.CreateAnswer(offer.get(), TransportOptions(), true, NULL,
294 &ice_credentials_);
295 CheckDesc(desc.get(), "", "", "", digest_alg2);
296 }
297
298 // Test that ice ufrag and password is changed in an updated offer and answer
299 // if `TransportDescriptionOptions::ice_restart` is true.
TEST_F(TransportDescriptionFactoryTest,TestIceRestart)300 TEST_F(TransportDescriptionFactoryTest, TestIceRestart) {
301 TestIceRestart(false);
302 }
303
304 // Test that ice ufrag and password is changed in an updated offer and answer
305 // if `TransportDescriptionOptions::ice_restart` is true and DTLS is enabled.
TEST_F(TransportDescriptionFactoryTest,TestIceRestartWithDtls)306 TEST_F(TransportDescriptionFactoryTest, TestIceRestartWithDtls) {
307 TestIceRestart(true);
308 }
309
310 // Test that ice renomination is set in an updated offer and answer
311 // if `TransportDescriptionOptions::enable_ice_renomination` is true.
TEST_F(TransportDescriptionFactoryTest,TestIceRenomination)312 TEST_F(TransportDescriptionFactoryTest, TestIceRenomination) {
313 TestIceRenomination(false);
314 }
315
316 // Test that ice renomination is set in an updated offer and answer
317 // if `TransportDescriptionOptions::enable_ice_renomination` is true and DTLS
318 // is enabled.
TEST_F(TransportDescriptionFactoryTest,TestIceRenominationWithDtls)319 TEST_F(TransportDescriptionFactoryTest, TestIceRenominationWithDtls) {
320 TestIceRenomination(true);
321 }
322
323 // Test that offers and answers have ice-option:trickle.
TEST_F(TransportDescriptionFactoryTest,AddsTrickleIceOption)324 TEST_F(TransportDescriptionFactoryTest, AddsTrickleIceOption) {
325 cricket::TransportOptions options;
326 std::unique_ptr<TransportDescription> offer =
327 f1_.CreateOffer(options, nullptr, &ice_credentials_);
328 EXPECT_TRUE(offer->HasOption("trickle"));
329 std::unique_ptr<TransportDescription> answer =
330 f2_.CreateAnswer(offer.get(), options, true, nullptr, &ice_credentials_);
331 EXPECT_TRUE(answer->HasOption("trickle"));
332 }
333
334 // Test CreateOffer with IceCredentialsIterator.
TEST_F(TransportDescriptionFactoryTest,CreateOfferIceCredentialsIterator)335 TEST_F(TransportDescriptionFactoryTest, CreateOfferIceCredentialsIterator) {
336 std::vector<cricket::IceParameters> credentials = {
337 cricket::IceParameters("kalle", "anka", false)};
338 cricket::IceCredentialsIterator credentialsIterator(credentials);
339 cricket::TransportOptions options;
340 std::unique_ptr<TransportDescription> offer =
341 f1_.CreateOffer(options, nullptr, &credentialsIterator);
342 EXPECT_EQ(offer->GetIceParameters().ufrag, credentials[0].ufrag);
343 EXPECT_EQ(offer->GetIceParameters().pwd, credentials[0].pwd);
344 }
345
346 // Test CreateAnswer with IceCredentialsIterator.
TEST_F(TransportDescriptionFactoryTest,CreateAnswerIceCredentialsIterator)347 TEST_F(TransportDescriptionFactoryTest, CreateAnswerIceCredentialsIterator) {
348 cricket::TransportOptions options;
349 std::unique_ptr<TransportDescription> offer =
350 f1_.CreateOffer(options, nullptr, &ice_credentials_);
351
352 std::vector<cricket::IceParameters> credentials = {
353 cricket::IceParameters("kalle", "anka", false)};
354 cricket::IceCredentialsIterator credentialsIterator(credentials);
355 std::unique_ptr<TransportDescription> answer = f1_.CreateAnswer(
356 offer.get(), options, false, nullptr, &credentialsIterator);
357 EXPECT_EQ(answer->GetIceParameters().ufrag, credentials[0].ufrag);
358 EXPECT_EQ(answer->GetIceParameters().pwd, credentials[0].pwd);
359 }
360
TEST_F(TransportDescriptionFactoryTest,CreateAnswerToDtlsActpassOffer)361 TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActpassOffer) {
362 f1_.set_secure(cricket::SEC_ENABLED);
363 f1_.set_certificate(cert1_);
364
365 f2_.set_secure(cricket::SEC_ENABLED);
366 f2_.set_certificate(cert2_);
367 cricket::TransportOptions options;
368 std::unique_ptr<TransportDescription> offer =
369 f1_.CreateOffer(options, nullptr, &ice_credentials_);
370
371 std::unique_ptr<TransportDescription> answer =
372 f2_.CreateAnswer(offer.get(), options, false, nullptr, &ice_credentials_);
373 EXPECT_EQ(answer->connection_role, cricket::CONNECTIONROLE_ACTIVE);
374 }
375
TEST_F(TransportDescriptionFactoryTest,CreateAnswerToDtlsActiveOffer)376 TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActiveOffer) {
377 f1_.set_secure(cricket::SEC_ENABLED);
378 f1_.set_certificate(cert1_);
379
380 f2_.set_secure(cricket::SEC_ENABLED);
381 f2_.set_certificate(cert2_);
382 cricket::TransportOptions options;
383 std::unique_ptr<TransportDescription> offer =
384 f1_.CreateOffer(options, nullptr, &ice_credentials_);
385 offer->connection_role = cricket::CONNECTIONROLE_ACTIVE;
386
387 std::unique_ptr<TransportDescription> answer =
388 f2_.CreateAnswer(offer.get(), options, false, nullptr, &ice_credentials_);
389 EXPECT_EQ(answer->connection_role, cricket::CONNECTIONROLE_PASSIVE);
390 }
391
TEST_F(TransportDescriptionFactoryTest,CreateAnswerToDtlsPassiveOffer)392 TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsPassiveOffer) {
393 f1_.set_secure(cricket::SEC_ENABLED);
394 f1_.set_certificate(cert1_);
395
396 f2_.set_secure(cricket::SEC_ENABLED);
397 f2_.set_certificate(cert2_);
398 cricket::TransportOptions options;
399 std::unique_ptr<TransportDescription> offer =
400 f1_.CreateOffer(options, nullptr, &ice_credentials_);
401 offer->connection_role = cricket::CONNECTIONROLE_PASSIVE;
402
403 std::unique_ptr<TransportDescription> answer =
404 f2_.CreateAnswer(offer.get(), options, false, nullptr, &ice_credentials_);
405 EXPECT_EQ(answer->connection_role, cricket::CONNECTIONROLE_ACTIVE);
406 }
407