• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
3  * Copyright 2012, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <string>
29 #include <vector>
30 
31 #include "talk/base/fakesslidentity.h"
32 #include "talk/base/gunit.h"
33 #include "talk/p2p/base/constants.h"
34 #include "talk/p2p/base/transportdescription.h"
35 #include "talk/p2p/base/transportdescriptionfactory.h"
36 
37 using talk_base::scoped_ptr;
38 using cricket::TransportDescriptionFactory;
39 using cricket::TransportDescription;
40 using cricket::TransportOptions;
41 
42 class TransportDescriptionFactoryTest : public testing::Test {
43  public:
TransportDescriptionFactoryTest()44   TransportDescriptionFactoryTest()
45       : id1_(new talk_base::FakeSSLIdentity("User1")),
46         id2_(new talk_base::FakeSSLIdentity("User2")) {
47   }
CheckDesc(const TransportDescription * desc,const std::string & type,const std::string & opt,const std::string & ice_ufrag,const std::string & ice_pwd,const std::string & dtls_alg)48   void CheckDesc(const TransportDescription* desc, const std::string& type,
49                  const std::string& opt, const std::string& ice_ufrag,
50                  const std::string& ice_pwd, const std::string& dtls_alg) {
51     ASSERT_TRUE(desc != NULL);
52     EXPECT_EQ(type, desc->transport_type);
53     EXPECT_EQ(!opt.empty(), desc->HasOption(opt));
54     if (ice_ufrag.empty() && ice_pwd.empty()) {
55       EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
56                 desc->ice_ufrag.size());
57       EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
58                 desc->ice_pwd.size());
59     } else {
60       EXPECT_EQ(ice_ufrag, desc->ice_ufrag);
61       EXPECT_EQ(ice_pwd, desc->ice_pwd);
62     }
63     if (dtls_alg.empty()) {
64       EXPECT_TRUE(desc->identity_fingerprint.get() == NULL);
65     } else {
66       ASSERT_TRUE(desc->identity_fingerprint.get() != NULL);
67       EXPECT_EQ(desc->identity_fingerprint->algorithm, dtls_alg);
68       EXPECT_GT(desc->identity_fingerprint->digest.length(), 0U);
69     }
70   }
71 
72   // This test ice restart by doing two offer answer exchanges. On the second
73   // exchange ice is restarted. The test verifies that the ufrag and password
74   // in the offer and answer is changed.
75   // If |dtls| is true, the test verifies that the finger print is not changed.
TestIceRestart(bool dtls)76   void TestIceRestart(bool dtls) {
77     if (dtls) {
78       f1_.set_secure(cricket::SEC_ENABLED);
79       f2_.set_secure(cricket::SEC_ENABLED);
80       f1_.set_identity(id1_.get());
81       f2_.set_identity(id2_.get());
82     } else {
83       f1_.set_secure(cricket::SEC_DISABLED);
84       f2_.set_secure(cricket::SEC_DISABLED);
85     }
86 
87     cricket::TransportOptions options;
88     // The initial offer / answer exchange.
89     talk_base::scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
90         options, NULL));
91     talk_base::scoped_ptr<TransportDescription> answer(
92         f2_.CreateAnswer(offer.get(),
93                          options, NULL));
94 
95     // Create an updated offer where we restart ice.
96     options.ice_restart = true;
97     talk_base::scoped_ptr<TransportDescription> restart_offer(f1_.CreateOffer(
98         options, offer.get()));
99 
100     VerifyUfragAndPasswordChanged(dtls, offer.get(), restart_offer.get());
101 
102     // Create a new answer. The transport ufrag and password is changed since
103     // |options.ice_restart == true|
104     talk_base::scoped_ptr<TransportDescription> restart_answer(
105         f2_.CreateAnswer(restart_offer.get(), options, answer.get()));
106     ASSERT_TRUE(restart_answer.get() != NULL);
107 
108     VerifyUfragAndPasswordChanged(dtls, answer.get(), restart_answer.get());
109   }
110 
VerifyUfragAndPasswordChanged(bool dtls,const TransportDescription * org_desc,const TransportDescription * restart_desc)111   void VerifyUfragAndPasswordChanged(bool dtls,
112                                      const TransportDescription* org_desc,
113                                      const TransportDescription* restart_desc) {
114     EXPECT_NE(org_desc->ice_pwd, restart_desc->ice_pwd);
115     EXPECT_NE(org_desc->ice_ufrag, restart_desc->ice_ufrag);
116     EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
117               restart_desc->ice_ufrag.size());
118     EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
119               restart_desc->ice_pwd.size());
120     // If DTLS is enabled, make sure the finger print is unchanged.
121     if (dtls) {
122       EXPECT_FALSE(
123           org_desc->identity_fingerprint->GetRfc4572Fingerprint().empty());
124       EXPECT_EQ(org_desc->identity_fingerprint->GetRfc4572Fingerprint(),
125                 restart_desc->identity_fingerprint->GetRfc4572Fingerprint());
126     }
127   }
128 
129  protected:
130   TransportDescriptionFactory f1_;
131   TransportDescriptionFactory f2_;
132   scoped_ptr<talk_base::SSLIdentity> id1_;
133   scoped_ptr<talk_base::SSLIdentity> id2_;
134 };
135 
136 // Test that in the default case, we generate the expected G-ICE offer.
TEST_F(TransportDescriptionFactoryTest,TestOfferGice)137 TEST_F(TransportDescriptionFactoryTest, TestOfferGice) {
138   f1_.set_protocol(cricket::ICEPROTO_GOOGLE);
139   scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
140       TransportOptions(), NULL));
141   CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
142 }
143 
144 // Test generating a hybrid offer.
TEST_F(TransportDescriptionFactoryTest,TestOfferHybrid)145 TEST_F(TransportDescriptionFactoryTest, TestOfferHybrid) {
146   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
147   scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
148       TransportOptions(), NULL));
149   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "", "");
150 }
151 
152 // Test generating an ICE-only offer.
TEST_F(TransportDescriptionFactoryTest,TestOfferIce)153 TEST_F(TransportDescriptionFactoryTest, TestOfferIce) {
154   f1_.set_protocol(cricket::ICEPROTO_RFC5245);
155   scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
156       TransportOptions(), NULL));
157   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
158 }
159 
160 // Test generating a hybrid offer with DTLS.
TEST_F(TransportDescriptionFactoryTest,TestOfferHybridDtls)161 TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtls) {
162   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
163   f1_.set_secure(cricket::SEC_ENABLED);
164   f1_.set_identity(id1_.get());
165   std::string digest_alg;
166   ASSERT_TRUE(id1_->certificate().GetSignatureDigestAlgorithm(&digest_alg));
167   scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
168       TransportOptions(), NULL));
169   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "",
170             digest_alg);
171   // Ensure it also works with SEC_REQUIRED.
172   f1_.set_secure(cricket::SEC_REQUIRED);
173   desc.reset(f1_.CreateOffer(TransportOptions(), NULL));
174   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "",
175             digest_alg);
176 }
177 
178 // Test generating a hybrid offer with DTLS fails with no identity.
TEST_F(TransportDescriptionFactoryTest,TestOfferHybridDtlsWithNoIdentity)179 TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsWithNoIdentity) {
180   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
181   f1_.set_secure(cricket::SEC_ENABLED);
182   scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
183       TransportOptions(), NULL));
184   ASSERT_TRUE(desc.get() == NULL);
185 }
186 
187 // Test updating a hybrid offer with DTLS to pick ICE.
188 // The ICE credentials should stay the same in the new offer.
TEST_F(TransportDescriptionFactoryTest,TestOfferHybridDtlsReofferIceDtls)189 TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsReofferIceDtls) {
190   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
191   f1_.set_secure(cricket::SEC_ENABLED);
192   f1_.set_identity(id1_.get());
193   std::string digest_alg;
194   ASSERT_TRUE(id1_->certificate().GetSignatureDigestAlgorithm(&digest_alg));
195   scoped_ptr<TransportDescription> old_desc(f1_.CreateOffer(
196       TransportOptions(), NULL));
197   ASSERT_TRUE(old_desc.get() != NULL);
198   f1_.set_protocol(cricket::ICEPROTO_RFC5245);
199   scoped_ptr<TransportDescription> desc(
200       f1_.CreateOffer(TransportOptions(), old_desc.get()));
201   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "",
202             old_desc->ice_ufrag, old_desc->ice_pwd, digest_alg);
203 }
204 
205 // Test that we can answer a GICE offer with GICE.
TEST_F(TransportDescriptionFactoryTest,TestAnswerGiceToGice)206 TEST_F(TransportDescriptionFactoryTest, TestAnswerGiceToGice) {
207   f1_.set_protocol(cricket::ICEPROTO_GOOGLE);
208   f2_.set_protocol(cricket::ICEPROTO_GOOGLE);
209   scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
210       TransportOptions(), NULL));
211   ASSERT_TRUE(offer.get() != NULL);
212   scoped_ptr<TransportDescription> desc(f2_.CreateAnswer(
213       offer.get(), TransportOptions(), NULL));
214   CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
215   // Should get the same result when answering as hybrid.
216   f2_.set_protocol(cricket::ICEPROTO_HYBRID);
217   desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
218                               NULL));
219   CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
220 }
221 
222 // Test that we can answer a hybrid offer with GICE.
TEST_F(TransportDescriptionFactoryTest,TestAnswerGiceToHybrid)223 TEST_F(TransportDescriptionFactoryTest, TestAnswerGiceToHybrid) {
224   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
225   f2_.set_protocol(cricket::ICEPROTO_GOOGLE);
226   scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
227       TransportOptions(), NULL));
228   ASSERT_TRUE(offer.get() != NULL);
229   scoped_ptr<TransportDescription> desc(
230       f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
231   CheckDesc(desc.get(), cricket::NS_GINGLE_P2P, "", "", "", "");
232 }
233 
234 // Test that we can answer a hybrid offer with ICE.
TEST_F(TransportDescriptionFactoryTest,TestAnswerIceToHybrid)235 TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToHybrid) {
236   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
237   f2_.set_protocol(cricket::ICEPROTO_RFC5245);
238   scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
239       TransportOptions(), NULL));
240   ASSERT_TRUE(offer.get() != NULL);
241   scoped_ptr<TransportDescription> desc(
242       f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
243   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
244   // Should get the same result when answering as hybrid.
245   f2_.set_protocol(cricket::ICEPROTO_HYBRID);
246   desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
247                               NULL));
248   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
249 }
250 
251 // Test that we can answer an ICE offer with ICE.
TEST_F(TransportDescriptionFactoryTest,TestAnswerIceToIce)252 TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToIce) {
253   f1_.set_protocol(cricket::ICEPROTO_RFC5245);
254   f2_.set_protocol(cricket::ICEPROTO_RFC5245);
255   scoped_ptr<TransportDescription> offer(f1_.CreateOffer(
256       TransportOptions(), NULL));
257   ASSERT_TRUE(offer.get() != NULL);
258   scoped_ptr<TransportDescription> desc(f2_.CreateAnswer(
259       offer.get(), TransportOptions(), NULL));
260   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
261   // Should get the same result when answering as hybrid.
262   f2_.set_protocol(cricket::ICEPROTO_HYBRID);
263   desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
264                               NULL));
265   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
266 }
267 
268 // Test that we can't answer a GICE offer with ICE.
TEST_F(TransportDescriptionFactoryTest,TestAnswerIceToGice)269 TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToGice) {
270   f1_.set_protocol(cricket::ICEPROTO_GOOGLE);
271   f2_.set_protocol(cricket::ICEPROTO_RFC5245);
272   scoped_ptr<TransportDescription> offer(
273       f1_.CreateOffer(TransportOptions(), NULL));
274   ASSERT_TRUE(offer.get() != NULL);
275   scoped_ptr<TransportDescription> desc(
276       f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
277   ASSERT_TRUE(desc.get() == NULL);
278 }
279 
280 // Test that we can't answer an ICE offer with GICE.
TEST_F(TransportDescriptionFactoryTest,TestAnswerGiceToIce)281 TEST_F(TransportDescriptionFactoryTest, TestAnswerGiceToIce) {
282   f1_.set_protocol(cricket::ICEPROTO_RFC5245);
283   f2_.set_protocol(cricket::ICEPROTO_GOOGLE);
284   scoped_ptr<TransportDescription> offer(
285       f1_.CreateOffer(TransportOptions(), NULL));
286   ASSERT_TRUE(offer.get() != NULL);
287   scoped_ptr<TransportDescription> desc(f2_.CreateAnswer(
288       offer.get(), TransportOptions(), NULL));
289   ASSERT_TRUE(desc.get() == NULL);
290 }
291 
292 // Test that we can update an answer properly; ICE credentials shouldn't change.
TEST_F(TransportDescriptionFactoryTest,TestAnswerIceToIceReanswer)293 TEST_F(TransportDescriptionFactoryTest, TestAnswerIceToIceReanswer) {
294   f1_.set_protocol(cricket::ICEPROTO_RFC5245);
295   f2_.set_protocol(cricket::ICEPROTO_RFC5245);
296   scoped_ptr<TransportDescription> offer(
297       f1_.CreateOffer(TransportOptions(), NULL));
298   ASSERT_TRUE(offer.get() != NULL);
299   scoped_ptr<TransportDescription> old_desc(
300       f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
301   ASSERT_TRUE(old_desc.get() != NULL);
302   scoped_ptr<TransportDescription> desc(
303       f2_.CreateAnswer(offer.get(), TransportOptions(),
304                        old_desc.get()));
305   ASSERT_TRUE(desc.get() != NULL);
306   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "",
307             old_desc->ice_ufrag, old_desc->ice_pwd, "");
308 }
309 
310 // Test that we handle answering an offer with DTLS with no DTLS.
TEST_F(TransportDescriptionFactoryTest,TestAnswerHybridToHybridDtls)311 TEST_F(TransportDescriptionFactoryTest, TestAnswerHybridToHybridDtls) {
312   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
313   f1_.set_secure(cricket::SEC_ENABLED);
314   f1_.set_identity(id1_.get());
315   f2_.set_protocol(cricket::ICEPROTO_HYBRID);
316   scoped_ptr<TransportDescription> offer(
317       f1_.CreateOffer(TransportOptions(), NULL));
318   ASSERT_TRUE(offer.get() != NULL);
319   scoped_ptr<TransportDescription> desc(
320       f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
321   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
322 }
323 
324 // Test that we handle answering an offer without DTLS if we have DTLS enabled,
325 // but fail if we require DTLS.
TEST_F(TransportDescriptionFactoryTest,TestAnswerHybridDtlsToHybrid)326 TEST_F(TransportDescriptionFactoryTest, TestAnswerHybridDtlsToHybrid) {
327   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
328   f2_.set_protocol(cricket::ICEPROTO_HYBRID);
329   f2_.set_secure(cricket::SEC_ENABLED);
330   f2_.set_identity(id2_.get());
331   scoped_ptr<TransportDescription> offer(
332       f1_.CreateOffer(TransportOptions(), NULL));
333   ASSERT_TRUE(offer.get() != NULL);
334   scoped_ptr<TransportDescription> desc(
335       f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
336   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", "");
337   f2_.set_secure(cricket::SEC_REQUIRED);
338   desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
339                               NULL));
340   ASSERT_TRUE(desc.get() == NULL);
341 }
342 
343 // Test that we handle answering an DTLS offer with DTLS, both if we have
344 // DTLS enabled and required.
TEST_F(TransportDescriptionFactoryTest,TestAnswerHybridDtlsToHybridDtls)345 TEST_F(TransportDescriptionFactoryTest, TestAnswerHybridDtlsToHybridDtls) {
346   f1_.set_protocol(cricket::ICEPROTO_HYBRID);
347   f1_.set_secure(cricket::SEC_ENABLED);
348   f1_.set_identity(id1_.get());
349 
350   f2_.set_protocol(cricket::ICEPROTO_HYBRID);
351   f2_.set_secure(cricket::SEC_ENABLED);
352   f2_.set_identity(id2_.get());
353   // f2_ produces the answer that is being checked in this test, so the
354   // answer must contain fingerprint lines with id2_'s digest algorithm.
355   std::string digest_alg2;
356   ASSERT_TRUE(id2_->certificate().GetSignatureDigestAlgorithm(&digest_alg2));
357 
358   scoped_ptr<TransportDescription> offer(
359       f1_.CreateOffer(TransportOptions(), NULL));
360   ASSERT_TRUE(offer.get() != NULL);
361   scoped_ptr<TransportDescription> desc(
362       f2_.CreateAnswer(offer.get(), TransportOptions(), NULL));
363   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", digest_alg2);
364   f2_.set_secure(cricket::SEC_REQUIRED);
365   desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(),
366                               NULL));
367   CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", digest_alg2);
368 }
369 
370 // Test that ice ufrag and password is changed in an updated offer and answer
371 // if |TransportDescriptionOptions::ice_restart| is true.
TEST_F(TransportDescriptionFactoryTest,TestIceRestart)372 TEST_F(TransportDescriptionFactoryTest, TestIceRestart) {
373   TestIceRestart(false);
374 }
375 
376 // Test that ice ufrag and password is changed in an updated offer and answer
377 // if |TransportDescriptionOptions::ice_restart| is true and DTLS is enabled.
TEST_F(TransportDescriptionFactoryTest,TestIceRestartWithDtls)378 TEST_F(TransportDescriptionFactoryTest, TestIceRestartWithDtls) {
379   TestIceRestart(true);
380 }
381