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