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