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 <string>
12 #include <vector>
13
14 #include "webrtc/p2p/base/constants.h"
15 #include "webrtc/p2p/base/transportdescription.h"
16 #include "webrtc/p2p/base/transportdescriptionfactory.h"
17 #include "webrtc/base/fakesslidentity.h"
18 #include "webrtc/base/gunit.h"
19 #include "webrtc/base/ssladapter.h"
20
21 using rtc::scoped_ptr;
22 using cricket::TransportDescriptionFactory;
23 using cricket::TransportDescription;
24 using cricket::TransportOptions;
25
26 class TransportDescriptionFactoryTest : public testing::Test {
27 public:
TransportDescriptionFactoryTest()28 TransportDescriptionFactoryTest()
29 : cert1_(rtc::RTCCertificate::Create(
30 scoped_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("User1")))),
31 cert2_(rtc::RTCCertificate::Create(
32 scoped_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("User2")))) {}
33
CheckDesc(const TransportDescription * desc,const std::string & opt,const std::string & ice_ufrag,const std::string & ice_pwd,const std::string & dtls_alg)34 void CheckDesc(const TransportDescription* desc,
35 const std::string& opt, const std::string& ice_ufrag,
36 const std::string& ice_pwd, const std::string& dtls_alg) {
37 ASSERT_TRUE(desc != NULL);
38 EXPECT_EQ(!opt.empty(), desc->HasOption(opt));
39 if (ice_ufrag.empty() && ice_pwd.empty()) {
40 EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
41 desc->ice_ufrag.size());
42 EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
43 desc->ice_pwd.size());
44 } else {
45 EXPECT_EQ(ice_ufrag, desc->ice_ufrag);
46 EXPECT_EQ(ice_pwd, desc->ice_pwd);
47 }
48 if (dtls_alg.empty()) {
49 EXPECT_TRUE(desc->identity_fingerprint.get() == NULL);
50 } else {
51 ASSERT_TRUE(desc->identity_fingerprint.get() != NULL);
52 EXPECT_EQ(desc->identity_fingerprint->algorithm, dtls_alg);
53 EXPECT_GT(desc->identity_fingerprint->digest.size(), 0U);
54 }
55 }
56
57 // This test ice restart by doing two offer answer exchanges. On the second
58 // exchange ice is restarted. The test verifies that the ufrag and password
59 // in the offer and answer is changed.
60 // If |dtls| is true, the test verifies that the finger print is not changed.
TestIceRestart(bool dtls)61 void TestIceRestart(bool dtls) {
62 if (dtls) {
63 f1_.set_secure(cricket::SEC_ENABLED);
64 f2_.set_secure(cricket::SEC_ENABLED);
65 f1_.set_certificate(cert1_);
66 f2_.set_certificate(cert2_);
67 } else {
68 f1_.set_secure(cricket::SEC_DISABLED);
69 f2_.set_secure(cricket::SEC_DISABLED);
70 }
71
72 cricket::TransportOptions options;
73 // The initial offer / answer exchange.
74 rtc::scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
75 options, NULL));
76 rtc::scoped_ptr<TransportDescription> answer(
77 f2_.CreateAnswer(offer.get(),
78 options, NULL));
79
80 // Create an updated offer where we restart ice.
81 options.ice_restart = true;
82 rtc::scoped_ptr<TransportDescription> restart_offer(f1_.CreateOffer(
83 options, offer.get()));
84
85 VerifyUfragAndPasswordChanged(dtls, offer.get(), restart_offer.get());
86
87 // Create a new answer. The transport ufrag and password is changed since
88 // |options.ice_restart == true|
89 rtc::scoped_ptr<TransportDescription> restart_answer(
90 f2_.CreateAnswer(restart_offer.get(), options, answer.get()));
91 ASSERT_TRUE(restart_answer.get() != NULL);
92
93 VerifyUfragAndPasswordChanged(dtls, answer.get(), restart_answer.get());
94 }
95
VerifyUfragAndPasswordChanged(bool dtls,const TransportDescription * org_desc,const TransportDescription * restart_desc)96 void VerifyUfragAndPasswordChanged(bool dtls,
97 const TransportDescription* org_desc,
98 const TransportDescription* restart_desc) {
99 EXPECT_NE(org_desc->ice_pwd, restart_desc->ice_pwd);
100 EXPECT_NE(org_desc->ice_ufrag, restart_desc->ice_ufrag);
101 EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
102 restart_desc->ice_ufrag.size());
103 EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
104 restart_desc->ice_pwd.size());
105 // If DTLS is enabled, make sure the finger print is unchanged.
106 if (dtls) {
107 EXPECT_FALSE(
108 org_desc->identity_fingerprint->GetRfc4572Fingerprint().empty());
109 EXPECT_EQ(org_desc->identity_fingerprint->GetRfc4572Fingerprint(),
110 restart_desc->identity_fingerprint->GetRfc4572Fingerprint());
111 }
112 }
113
114 protected:
115 TransportDescriptionFactory f1_;
116 TransportDescriptionFactory f2_;
117
118 rtc::scoped_refptr<rtc::RTCCertificate> cert1_;
119 rtc::scoped_refptr<rtc::RTCCertificate> cert2_;
120 };
121
TEST_F(TransportDescriptionFactoryTest,TestOfferDefault)122 TEST_F(TransportDescriptionFactoryTest, TestOfferDefault) {
123 scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
124 TransportOptions(), NULL));
125 CheckDesc(desc.get(), "", "", "", "");
126 }
127
TEST_F(TransportDescriptionFactoryTest,TestOfferDtls)128 TEST_F(TransportDescriptionFactoryTest, TestOfferDtls) {
129 f1_.set_secure(cricket::SEC_ENABLED);
130 f1_.set_certificate(cert1_);
131 std::string digest_alg;
132 ASSERT_TRUE(cert1_->ssl_certificate().GetSignatureDigestAlgorithm(
133 &digest_alg));
134 scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
135 TransportOptions(), NULL));
136 CheckDesc(desc.get(), "", "", "", digest_alg);
137 // Ensure it also works with SEC_REQUIRED.
138 f1_.set_secure(cricket::SEC_REQUIRED);
139 desc.reset(f1_.CreateOffer(TransportOptions(), NULL));
140 CheckDesc(desc.get(), "", "", "", digest_alg);
141 }
142
143 // Test generating an offer with DTLS fails with no identity.
TEST_F(TransportDescriptionFactoryTest,TestOfferDtlsWithNoIdentity)144 TEST_F(TransportDescriptionFactoryTest, TestOfferDtlsWithNoIdentity) {
145 f1_.set_secure(cricket::SEC_ENABLED);
146 scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
147 TransportOptions(), NULL));
148 ASSERT_TRUE(desc.get() == NULL);
149 }
150
151 // Test updating an offer with DTLS to pick ICE.
152 // The ICE credentials should stay the same in the new offer.
TEST_F(TransportDescriptionFactoryTest,TestOfferDtlsReofferDtls)153 TEST_F(TransportDescriptionFactoryTest, TestOfferDtlsReofferDtls) {
154 f1_.set_secure(cricket::SEC_ENABLED);
155 f1_.set_certificate(cert1_);
156 std::string digest_alg;
157 ASSERT_TRUE(cert1_->ssl_certificate().GetSignatureDigestAlgorithm(
158 &digest_alg));
159 scoped_ptr<TransportDescription> old_desc(f1_.CreateOffer(
160 TransportOptions(), NULL));
161 ASSERT_TRUE(old_desc.get() != NULL);
162 scoped_ptr<TransportDescription> desc(
163 f1_.CreateOffer(TransportOptions(), old_desc.get()));
164 CheckDesc(desc.get(), "",
165 old_desc->ice_ufrag, old_desc->ice_pwd, digest_alg);
166 }
167
TEST_F(TransportDescriptionFactoryTest,TestAnswerDefault)168 TEST_F(TransportDescriptionFactoryTest, TestAnswerDefault) {
169 scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
170 TransportOptions(), NULL));
171 ASSERT_TRUE(offer.get() != NULL);
172 scoped_ptr<TransportDescription> desc(f2_.CreateAnswer(
173 offer.get(), TransportOptions(), NULL));
174 CheckDesc(desc.get(), "", "", "", "");
175 desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
176 NULL));
177 CheckDesc(desc.get(), "", "", "", "");
178 }
179
180 // Test that we can update an answer properly; ICE credentials shouldn't change.
TEST_F(TransportDescriptionFactoryTest,TestReanswer)181 TEST_F(TransportDescriptionFactoryTest, TestReanswer) {
182 scoped_ptr<TransportDescription> offer(
183 f1_.CreateOffer(TransportOptions(), NULL));
184 ASSERT_TRUE(offer.get() != NULL);
185 scoped_ptr<TransportDescription> old_desc(
186 f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
187 ASSERT_TRUE(old_desc.get() != NULL);
188 scoped_ptr<TransportDescription> desc(
189 f2_.CreateAnswer(offer.get(), TransportOptions(),
190 old_desc.get()));
191 ASSERT_TRUE(desc.get() != NULL);
192 CheckDesc(desc.get(), "",
193 old_desc->ice_ufrag, old_desc->ice_pwd, "");
194 }
195
196 // Test that we handle answering an offer with DTLS with no DTLS.
TEST_F(TransportDescriptionFactoryTest,TestAnswerDtlsToNoDtls)197 TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToNoDtls) {
198 f1_.set_secure(cricket::SEC_ENABLED);
199 f1_.set_certificate(cert1_);
200 scoped_ptr<TransportDescription> offer(
201 f1_.CreateOffer(TransportOptions(), NULL));
202 ASSERT_TRUE(offer.get() != NULL);
203 scoped_ptr<TransportDescription> desc(
204 f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
205 CheckDesc(desc.get(), "", "", "", "");
206 }
207
208 // Test that we handle answering an offer without DTLS if we have DTLS enabled,
209 // but fail if we require DTLS.
TEST_F(TransportDescriptionFactoryTest,TestAnswerNoDtlsToDtls)210 TEST_F(TransportDescriptionFactoryTest, TestAnswerNoDtlsToDtls) {
211 f2_.set_secure(cricket::SEC_ENABLED);
212 f2_.set_certificate(cert2_);
213 scoped_ptr<TransportDescription> offer(
214 f1_.CreateOffer(TransportOptions(), NULL));
215 ASSERT_TRUE(offer.get() != NULL);
216 scoped_ptr<TransportDescription> desc(
217 f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
218 CheckDesc(desc.get(), "", "", "", "");
219 f2_.set_secure(cricket::SEC_REQUIRED);
220 desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
221 NULL));
222 ASSERT_TRUE(desc.get() == NULL);
223 }
224
225 // Test that we handle answering an DTLS offer with DTLS, both if we have
226 // DTLS enabled and required.
TEST_F(TransportDescriptionFactoryTest,TestAnswerDtlsToDtls)227 TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToDtls) {
228 f1_.set_secure(cricket::SEC_ENABLED);
229 f1_.set_certificate(cert1_);
230
231 f2_.set_secure(cricket::SEC_ENABLED);
232 f2_.set_certificate(cert2_);
233 // f2_ produces the answer that is being checked in this test, so the
234 // answer must contain fingerprint lines with cert2_'s digest algorithm.
235 std::string digest_alg2;
236 ASSERT_TRUE(cert2_->ssl_certificate().GetSignatureDigestAlgorithm(
237 &digest_alg2));
238
239 scoped_ptr<TransportDescription> offer(
240 f1_.CreateOffer(TransportOptions(), NULL));
241 ASSERT_TRUE(offer.get() != NULL);
242 scoped_ptr<TransportDescription> desc(
243 f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
244 CheckDesc(desc.get(), "", "", "", digest_alg2);
245 f2_.set_secure(cricket::SEC_REQUIRED);
246 desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
247 NULL));
248 CheckDesc(desc.get(), "", "", "", digest_alg2);
249 }
250
251 // Test that ice ufrag and password is changed in an updated offer and answer
252 // if |TransportDescriptionOptions::ice_restart| is true.
TEST_F(TransportDescriptionFactoryTest,TestIceRestart)253 TEST_F(TransportDescriptionFactoryTest, TestIceRestart) {
254 TestIceRestart(false);
255 }
256
257 // Test that ice ufrag and password is changed in an updated offer and answer
258 // if |TransportDescriptionOptions::ice_restart| is true and DTLS is enabled.
TEST_F(TransportDescriptionFactoryTest,TestIceRestartWithDtls)259 TEST_F(TransportDescriptionFactoryTest, TestIceRestartWithDtls) {
260 TestIceRestart(true);
261 }
262