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
21using rtc::scoped_ptr;
22using cricket::TransportDescriptionFactory;
23using cricket::TransportDescription;
24using cricket::TransportOptions;
25
26class TransportDescriptionFactoryTest : public testing::Test {
27 public:
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
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.
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
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
122TEST_F(TransportDescriptionFactoryTest, TestOfferDefault) {
123  scoped_ptr<TransportDescription> desc(f1_.CreateOffer(
124      TransportOptions(), NULL));
125  CheckDesc(desc.get(), "", "", "", "");
126}
127
128TEST_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.
144TEST_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.
153TEST_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
168TEST_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.
181TEST_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.
197TEST_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.
210TEST_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.
227TEST_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.
253TEST_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.
259TEST_F(TransportDescriptionFactoryTest, TestIceRestartWithDtls) {
260  TestIceRestart(true);
261}
262