diff options
Diffstat (limited to 'webrtc/p2p/base/dtlstransportchannel_unittest.cc')
-rw-r--r-- | webrtc/p2p/base/dtlstransportchannel_unittest.cc | 894 |
1 files changed, 894 insertions, 0 deletions
diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc new file mode 100644 index 0000000000..07e3b87847 --- /dev/null +++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc @@ -0,0 +1,894 @@ +/* + * Copyright 2011 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <set> + +#include "webrtc/p2p/base/dtlstransport.h" +#include "webrtc/p2p/base/faketransportcontroller.h" +#include "webrtc/base/common.h" +#include "webrtc/base/dscp.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/sslidentity.h" +#include "webrtc/base/sslstreamadapter.h" +#include "webrtc/base/stringutils.h" + +#define MAYBE_SKIP_TEST(feature) \ + if (!(rtc::SSLStreamAdapter::feature())) { \ + LOG(LS_INFO) << "Feature disabled... skipping"; \ + return; \ + } + +static const char AES_CM_128_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80"; +static const char kIceUfrag1[] = "TESTICEUFRAG0001"; +static const char kIcePwd1[] = "TESTICEPWD00000000000001"; +static const size_t kPacketNumOffset = 8; +static const size_t kPacketHeaderLen = 12; +static const int kFakePacketId = 0x1234; + +static bool IsRtpLeadByte(uint8_t b) { + return ((b & 0xC0) == 0x80); +} + +using cricket::ConnectionRole; + +enum Flags { NF_REOFFER = 0x1, NF_EXPECT_FAILURE = 0x2 }; + +class DtlsTestClient : public sigslot::has_slots<> { + public: + DtlsTestClient(const std::string& name) + : name_(name), + packet_size_(0), + use_dtls_srtp_(false), + ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10), + negotiated_dtls_(false), + received_dtls_client_hello_(false), + received_dtls_server_hello_(false) {} + void CreateCertificate(rtc::KeyType key_type) { + certificate_ = rtc::RTCCertificate::Create( + rtc::scoped_ptr<rtc::SSLIdentity>( + rtc::SSLIdentity::Generate(name_, key_type)).Pass()); + } + const rtc::scoped_refptr<rtc::RTCCertificate>& certificate() { + return certificate_; + } + void SetupSrtp() { + ASSERT(certificate_); + use_dtls_srtp_ = true; + } + void SetupMaxProtocolVersion(rtc::SSLProtocolVersion version) { + ASSERT(!transport_); + ssl_max_version_ = version; + } + void SetupChannels(int count, cricket::IceRole role) { + transport_.reset(new cricket::DtlsTransport<cricket::FakeTransport>( + "dtls content name", nullptr, certificate_)); + transport_->SetAsync(true); + transport_->SetIceRole(role); + transport_->SetIceTiebreaker( + (role == cricket::ICEROLE_CONTROLLING) ? 1 : 2); + + for (int i = 0; i < count; ++i) { + cricket::DtlsTransportChannelWrapper* channel = + static_cast<cricket::DtlsTransportChannelWrapper*>( + transport_->CreateChannel(i)); + ASSERT_TRUE(channel != NULL); + channel->SetSslMaxProtocolVersion(ssl_max_version_); + channel->SignalWritableState.connect(this, + &DtlsTestClient::OnTransportChannelWritableState); + channel->SignalReadPacket.connect(this, + &DtlsTestClient::OnTransportChannelReadPacket); + channel->SignalSentPacket.connect( + this, &DtlsTestClient::OnTransportChannelSentPacket); + channels_.push_back(channel); + + // Hook the raw packets so that we can verify they are encrypted. + channel->channel()->SignalReadPacket.connect( + this, &DtlsTestClient::OnFakeTransportChannelReadPacket); + } + } + + cricket::Transport* transport() { return transport_.get(); } + + cricket::FakeTransportChannel* GetFakeChannel(int component) { + cricket::TransportChannelImpl* ch = transport_->GetChannel(component); + cricket::DtlsTransportChannelWrapper* wrapper = + static_cast<cricket::DtlsTransportChannelWrapper*>(ch); + return (wrapper) ? + static_cast<cricket::FakeTransportChannel*>(wrapper->channel()) : NULL; + } + + // Offer DTLS if we have an identity; pass in a remote fingerprint only if + // both sides support DTLS. + void Negotiate(DtlsTestClient* peer, cricket::ContentAction action, + ConnectionRole local_role, ConnectionRole remote_role, + int flags) { + Negotiate(certificate_, certificate_ ? peer->certificate_ : nullptr, action, + local_role, remote_role, flags); + } + + // Allow any DTLS configuration to be specified (including invalid ones). + void Negotiate(const rtc::scoped_refptr<rtc::RTCCertificate>& local_cert, + const rtc::scoped_refptr<rtc::RTCCertificate>& remote_cert, + cricket::ContentAction action, + ConnectionRole local_role, + ConnectionRole remote_role, + int flags) { + rtc::scoped_ptr<rtc::SSLFingerprint> local_fingerprint; + rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint; + if (local_cert) { + std::string digest_algorithm; + ASSERT_TRUE(local_cert->ssl_certificate().GetSignatureDigestAlgorithm( + &digest_algorithm)); + ASSERT_FALSE(digest_algorithm.empty()); + local_fingerprint.reset(rtc::SSLFingerprint::Create( + digest_algorithm, local_cert->identity())); + ASSERT_TRUE(local_fingerprint.get() != NULL); + EXPECT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); + } + if (remote_cert) { + std::string digest_algorithm; + ASSERT_TRUE(remote_cert->ssl_certificate().GetSignatureDigestAlgorithm( + &digest_algorithm)); + ASSERT_FALSE(digest_algorithm.empty()); + remote_fingerprint.reset(rtc::SSLFingerprint::Create( + digest_algorithm, remote_cert->identity())); + ASSERT_TRUE(remote_fingerprint.get() != NULL); + EXPECT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); + } + + if (use_dtls_srtp_ && !(flags & NF_REOFFER)) { + // SRTP ciphers will be set only in the beginning. + for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it = + channels_.begin(); it != channels_.end(); ++it) { + std::vector<std::string> ciphers; + ciphers.push_back(AES_CM_128_HMAC_SHA1_80); + ASSERT_TRUE((*it)->SetSrtpCiphers(ciphers)); + } + } + + cricket::TransportDescription local_desc( + std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + local_role, + // If remote if the offerer and has no DTLS support, answer will be + // without any fingerprint. + (action == cricket::CA_ANSWER && !remote_cert) + ? nullptr + : local_fingerprint.get(), + cricket::Candidates()); + + cricket::TransportDescription remote_desc( + std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + remote_role, remote_fingerprint.get(), cricket::Candidates()); + + bool expect_success = (flags & NF_EXPECT_FAILURE) ? false : true; + // If |expect_success| is false, expect SRTD or SLTD to fail when + // content action is CA_ANSWER. + if (action == cricket::CA_OFFER) { + ASSERT_TRUE(transport_->SetLocalTransportDescription( + local_desc, cricket::CA_OFFER, NULL)); + ASSERT_EQ(expect_success, transport_->SetRemoteTransportDescription( + remote_desc, cricket::CA_ANSWER, NULL)); + } else { + ASSERT_TRUE(transport_->SetRemoteTransportDescription( + remote_desc, cricket::CA_OFFER, NULL)); + ASSERT_EQ(expect_success, transport_->SetLocalTransportDescription( + local_desc, cricket::CA_ANSWER, NULL)); + } + negotiated_dtls_ = (local_cert && remote_cert); + } + + bool Connect(DtlsTestClient* peer) { + transport_->ConnectChannels(); + transport_->SetDestination(peer->transport_.get()); + return true; + } + + bool all_channels_writable() const { + if (channels_.empty()) { + return false; + } + for (cricket::DtlsTransportChannelWrapper* channel : channels_) { + if (!channel->writable()) { + return false; + } + } + return true; + } + + void CheckRole(rtc::SSLRole role) { + if (role == rtc::SSL_CLIENT) { + ASSERT_FALSE(received_dtls_client_hello_); + ASSERT_TRUE(received_dtls_server_hello_); + } else { + ASSERT_TRUE(received_dtls_client_hello_); + ASSERT_FALSE(received_dtls_server_hello_); + } + } + + void CheckSrtp(const std::string& expected_cipher) { + for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it = + channels_.begin(); it != channels_.end(); ++it) { + std::string cipher; + + bool rv = (*it)->GetSrtpCryptoSuite(&cipher); + if (negotiated_dtls_ && !expected_cipher.empty()) { + ASSERT_TRUE(rv); + + ASSERT_EQ(cipher, expected_cipher); + } else { + ASSERT_FALSE(rv); + } + } + } + + void CheckSsl(int expected_cipher) { + for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it = + channels_.begin(); it != channels_.end(); ++it) { + int cipher; + + bool rv = (*it)->GetSslCipherSuite(&cipher); + if (negotiated_dtls_ && expected_cipher) { + ASSERT_TRUE(rv); + + ASSERT_EQ(cipher, expected_cipher); + } else { + ASSERT_FALSE(rv); + } + } + } + + void SendPackets(size_t channel, size_t size, size_t count, bool srtp) { + ASSERT(channel < channels_.size()); + rtc::scoped_ptr<char[]> packet(new char[size]); + size_t sent = 0; + do { + // Fill the packet with a known value and a sequence number to check + // against, and make sure that it doesn't look like DTLS. + memset(packet.get(), sent & 0xff, size); + packet[0] = (srtp) ? 0x80 : 0x00; + rtc::SetBE32(packet.get() + kPacketNumOffset, + static_cast<uint32_t>(sent)); + + // Only set the bypass flag if we've activated DTLS. + int flags = (certificate_ && srtp) ? cricket::PF_SRTP_BYPASS : 0; + rtc::PacketOptions packet_options; + packet_options.packet_id = kFakePacketId; + int rv = channels_[channel]->SendPacket( + packet.get(), size, packet_options, flags); + ASSERT_GT(rv, 0); + ASSERT_EQ(size, static_cast<size_t>(rv)); + ++sent; + } while (sent < count); + } + + int SendInvalidSrtpPacket(size_t channel, size_t size) { + ASSERT(channel < channels_.size()); + rtc::scoped_ptr<char[]> packet(new char[size]); + // Fill the packet with 0 to form an invalid SRTP packet. + memset(packet.get(), 0, size); + + rtc::PacketOptions packet_options; + return channels_[channel]->SendPacket( + packet.get(), size, packet_options, cricket::PF_SRTP_BYPASS); + } + + void ExpectPackets(size_t channel, size_t size) { + packet_size_ = size; + received_.clear(); + } + + size_t NumPacketsReceived() { + return received_.size(); + } + + bool VerifyPacket(const char* data, size_t size, uint32_t* out_num) { + if (size != packet_size_ || + (data[0] != 0 && static_cast<uint8_t>(data[0]) != 0x80)) { + return false; + } + uint32_t packet_num = rtc::GetBE32(data + kPacketNumOffset); + for (size_t i = kPacketHeaderLen; i < size; ++i) { + if (static_cast<uint8_t>(data[i]) != (packet_num & 0xff)) { + return false; + } + } + if (out_num) { + *out_num = packet_num; + } + return true; + } + bool VerifyEncryptedPacket(const char* data, size_t size) { + // This is an encrypted data packet; let's make sure it's mostly random; + // less than 10% of the bytes should be equal to the cleartext packet. + if (size <= packet_size_) { + return false; + } + uint32_t packet_num = rtc::GetBE32(data + kPacketNumOffset); + int num_matches = 0; + for (size_t i = kPacketNumOffset; i < size; ++i) { + if (static_cast<uint8_t>(data[i]) == (packet_num & 0xff)) { + ++num_matches; + } + } + return (num_matches < ((static_cast<int>(size) - 5) / 10)); + } + + // Transport channel callbacks + void OnTransportChannelWritableState(cricket::TransportChannel* channel) { + LOG(LS_INFO) << name_ << ": Channel '" << channel->component() + << "' is writable"; + } + + void OnTransportChannelReadPacket(cricket::TransportChannel* channel, + const char* data, size_t size, + const rtc::PacketTime& packet_time, + int flags) { + uint32_t packet_num = 0; + ASSERT_TRUE(VerifyPacket(data, size, &packet_num)); + received_.insert(packet_num); + // Only DTLS-SRTP packets should have the bypass flag set. + int expected_flags = + (certificate_ && IsRtpLeadByte(data[0])) ? cricket::PF_SRTP_BYPASS : 0; + ASSERT_EQ(expected_flags, flags); + } + + void OnTransportChannelSentPacket(cricket::TransportChannel* channel, + const rtc::SentPacket& sent_packet) { + sent_packet_ = sent_packet; + } + + rtc::SentPacket sent_packet() const { return sent_packet_; } + + // Hook into the raw packet stream to make sure DTLS packets are encrypted. + void OnFakeTransportChannelReadPacket(cricket::TransportChannel* channel, + const char* data, size_t size, + const rtc::PacketTime& time, + int flags) { + // Flags shouldn't be set on the underlying TransportChannel packets. + ASSERT_EQ(0, flags); + + // Look at the handshake packets to see what role we played. + // Check that non-handshake packets are DTLS data or SRTP bypass. + if (negotiated_dtls_) { + if (data[0] == 22 && size > 17) { + if (data[13] == 1) { + received_dtls_client_hello_ = true; + } else if (data[13] == 2) { + received_dtls_server_hello_ = true; + } + } else if (!(data[0] >= 20 && data[0] <= 22)) { + ASSERT_TRUE(data[0] == 23 || IsRtpLeadByte(data[0])); + if (data[0] == 23) { + ASSERT_TRUE(VerifyEncryptedPacket(data, size)); + } else if (IsRtpLeadByte(data[0])) { + ASSERT_TRUE(VerifyPacket(data, size, NULL)); + } + } + } + } + + private: + std::string name_; + rtc::scoped_refptr<rtc::RTCCertificate> certificate_; + rtc::scoped_ptr<cricket::FakeTransport> transport_; + std::vector<cricket::DtlsTransportChannelWrapper*> channels_; + size_t packet_size_; + std::set<int> received_; + bool use_dtls_srtp_; + rtc::SSLProtocolVersion ssl_max_version_; + bool negotiated_dtls_; + bool received_dtls_client_hello_; + bool received_dtls_server_hello_; + rtc::SentPacket sent_packet_; +}; + + +class DtlsTransportChannelTest : public testing::Test { + public: + DtlsTransportChannelTest() + : client1_("P1"), + client2_("P2"), + channel_ct_(1), + use_dtls_(false), + use_dtls_srtp_(false), + ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {} + + void SetChannelCount(size_t channel_ct) { + channel_ct_ = static_cast<int>(channel_ct); + } + void SetMaxProtocolVersions(rtc::SSLProtocolVersion c1, + rtc::SSLProtocolVersion c2) { + client1_.SetupMaxProtocolVersion(c1); + client2_.SetupMaxProtocolVersion(c2); + ssl_expected_version_ = std::min(c1, c2); + } + void PrepareDtls(bool c1, bool c2, rtc::KeyType key_type) { + if (c1) { + client1_.CreateCertificate(key_type); + } + if (c2) { + client2_.CreateCertificate(key_type); + } + if (c1 && c2) + use_dtls_ = true; + } + void PrepareDtlsSrtp(bool c1, bool c2) { + if (!use_dtls_) + return; + + if (c1) + client1_.SetupSrtp(); + if (c2) + client2_.SetupSrtp(); + + if (c1 && c2) + use_dtls_srtp_ = true; + } + + bool Connect(ConnectionRole client1_role, ConnectionRole client2_role) { + Negotiate(client1_role, client2_role); + + bool rv = client1_.Connect(&client2_); + EXPECT_TRUE(rv); + if (!rv) + return false; + + EXPECT_TRUE_WAIT( + client1_.all_channels_writable() && client2_.all_channels_writable(), + 10000); + if (!client1_.all_channels_writable() || !client2_.all_channels_writable()) + return false; + + // Check that we used the right roles. + if (use_dtls_) { + rtc::SSLRole client1_ssl_role = + (client1_role == cricket::CONNECTIONROLE_ACTIVE || + (client2_role == cricket::CONNECTIONROLE_PASSIVE && + client1_role == cricket::CONNECTIONROLE_ACTPASS)) ? + rtc::SSL_CLIENT : rtc::SSL_SERVER; + + rtc::SSLRole client2_ssl_role = + (client2_role == cricket::CONNECTIONROLE_ACTIVE || + (client1_role == cricket::CONNECTIONROLE_PASSIVE && + client2_role == cricket::CONNECTIONROLE_ACTPASS)) ? + rtc::SSL_CLIENT : rtc::SSL_SERVER; + + client1_.CheckRole(client1_ssl_role); + client2_.CheckRole(client2_ssl_role); + } + + // Check that we negotiated the right ciphers. + if (use_dtls_srtp_) { + client1_.CheckSrtp(AES_CM_128_HMAC_SHA1_80); + client2_.CheckSrtp(AES_CM_128_HMAC_SHA1_80); + } else { + client1_.CheckSrtp(""); + client2_.CheckSrtp(""); + } + client1_.CheckSsl(rtc::SSLStreamAdapter::GetDefaultSslCipherForTest( + ssl_expected_version_, rtc::KT_DEFAULT)); + client2_.CheckSsl(rtc::SSLStreamAdapter::GetDefaultSslCipherForTest( + ssl_expected_version_, rtc::KT_DEFAULT)); + + return true; + } + + bool Connect() { + // By default, Client1 will be Server and Client2 will be Client. + return Connect(cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_ACTIVE); + } + + void Negotiate() { + Negotiate(cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE); + } + + void Negotiate(ConnectionRole client1_role, ConnectionRole client2_role) { + client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING); + client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED); + // Expect success from SLTD and SRTD. + client1_.Negotiate(&client2_, cricket::CA_OFFER, + client1_role, client2_role, 0); + client2_.Negotiate(&client1_, cricket::CA_ANSWER, + client2_role, client1_role, 0); + } + + // Negotiate with legacy client |client2|. Legacy client doesn't use setup + // attributes, except NONE. + void NegotiateWithLegacy() { + client1_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLING); + client2_.SetupChannels(channel_ct_, cricket::ICEROLE_CONTROLLED); + // Expect success from SLTD and SRTD. + client1_.Negotiate(&client2_, cricket::CA_OFFER, + cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_NONE, 0); + client2_.Negotiate(&client1_, cricket::CA_ANSWER, + cricket::CONNECTIONROLE_ACTIVE, + cricket::CONNECTIONROLE_NONE, 0); + } + + void Renegotiate(DtlsTestClient* reoffer_initiator, + ConnectionRole client1_role, ConnectionRole client2_role, + int flags) { + if (reoffer_initiator == &client1_) { + client1_.Negotiate(&client2_, cricket::CA_OFFER, + client1_role, client2_role, flags); + client2_.Negotiate(&client1_, cricket::CA_ANSWER, + client2_role, client1_role, flags); + } else { + client2_.Negotiate(&client1_, cricket::CA_OFFER, + client2_role, client1_role, flags); + client1_.Negotiate(&client2_, cricket::CA_ANSWER, + client1_role, client2_role, flags); + } + } + + void TestTransfer(size_t channel, size_t size, size_t count, bool srtp) { + LOG(LS_INFO) << "Expect packets, size=" << size; + client2_.ExpectPackets(channel, size); + client1_.SendPackets(channel, size, count, srtp); + EXPECT_EQ_WAIT(count, client2_.NumPacketsReceived(), 10000); + } + + protected: + DtlsTestClient client1_; + DtlsTestClient client2_; + int channel_ct_; + bool use_dtls_; + bool use_dtls_srtp_; + rtc::SSLProtocolVersion ssl_expected_version_; +}; + +// Test that transport negotiation of ICE, no DTLS works properly. +TEST_F(DtlsTransportChannelTest, TestChannelSetupIce) { + Negotiate(); + cricket::FakeTransportChannel* channel1 = client1_.GetFakeChannel(0); + cricket::FakeTransportChannel* channel2 = client2_.GetFakeChannel(0); + ASSERT_TRUE(channel1 != NULL); + ASSERT_TRUE(channel2 != NULL); + EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole()); + EXPECT_EQ(1U, channel1->IceTiebreaker()); + EXPECT_EQ(kIceUfrag1, channel1->ice_ufrag()); + EXPECT_EQ(kIcePwd1, channel1->ice_pwd()); + EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole()); + EXPECT_EQ(2U, channel2->IceTiebreaker()); +} + +// Connect without DTLS, and transfer some data. +TEST_F(DtlsTransportChannelTest, TestTransfer) { + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, false); +} + +// Connect without DTLS, and transfer some data. +TEST_F(DtlsTransportChannelTest, TestOnSentPacket) { + ASSERT_TRUE(Connect()); + EXPECT_EQ(client1_.sent_packet().send_time_ms, -1); + TestTransfer(0, 1000, 100, false); + EXPECT_EQ(kFakePacketId, client1_.sent_packet().packet_id); + EXPECT_GE(client1_.sent_packet().send_time_ms, 0); +} + +// Create two channels without DTLS, and transfer some data. +TEST_F(DtlsTransportChannelTest, TestTransferTwoChannels) { + SetChannelCount(2); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, false); + TestTransfer(1, 1000, 100, false); +} + +// Connect without DTLS, and transfer SRTP data. +TEST_F(DtlsTransportChannelTest, TestTransferSrtp) { + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, true); +} + +// Create two channels without DTLS, and transfer SRTP data. +TEST_F(DtlsTransportChannelTest, TestTransferSrtpTwoChannels) { + SetChannelCount(2); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + +// Connect with DTLS, and transfer some data. +TEST_F(DtlsTransportChannelTest, TestTransferDtls) { + MAYBE_SKIP_TEST(HaveDtls); + PrepareDtls(true, true, rtc::KT_DEFAULT); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, false); +} + +// Create two channels with DTLS, and transfer some data. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsTwoChannels) { + MAYBE_SKIP_TEST(HaveDtls); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, false); + TestTransfer(1, 1000, 100, false); +} + +// Connect with A doing DTLS and B not, and transfer some data. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsRejected) { + PrepareDtls(true, false, rtc::KT_DEFAULT); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, false); +} + +// Connect with B doing DTLS and A not, and transfer some data. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsNotOffered) { + PrepareDtls(false, true, rtc::KT_DEFAULT); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, false); +} + +// Create two channels with DTLS 1.0 and check ciphers. +TEST_F(DtlsTransportChannelTest, TestDtls12None) { + MAYBE_SKIP_TEST(HaveDtls); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + SetMaxProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10); + ASSERT_TRUE(Connect()); +} + +// Create two channels with DTLS 1.2 and check ciphers. +TEST_F(DtlsTransportChannelTest, TestDtls12Both) { + MAYBE_SKIP_TEST(HaveDtls); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + SetMaxProtocolVersions(rtc::SSL_PROTOCOL_DTLS_12, rtc::SSL_PROTOCOL_DTLS_12); + ASSERT_TRUE(Connect()); +} + +// Create two channels with DTLS 1.0 / DTLS 1.2 and check ciphers. +TEST_F(DtlsTransportChannelTest, TestDtls12Client1) { + MAYBE_SKIP_TEST(HaveDtls); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + SetMaxProtocolVersions(rtc::SSL_PROTOCOL_DTLS_12, rtc::SSL_PROTOCOL_DTLS_10); + ASSERT_TRUE(Connect()); +} + +// Create two channels with DTLS 1.2 / DTLS 1.0 and check ciphers. +TEST_F(DtlsTransportChannelTest, TestDtls12Client2) { + MAYBE_SKIP_TEST(HaveDtls); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + SetMaxProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_12); + ASSERT_TRUE(Connect()); +} + +// Connect with DTLS, negotiate DTLS-SRTP, and transfer SRTP using bypass. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtp) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, true); +} + +// Connect with DTLS-SRTP, transfer an invalid SRTP packet, and expects -1 +// returned. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsInvalidSrtpPacket) { + MAYBE_SKIP_TEST(HaveDtls); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect()); + int result = client1_.SendInvalidSrtpPacket(0, 100); + ASSERT_EQ(-1, result); +} + +// Connect with DTLS. A does DTLS-SRTP but B does not. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpRejected) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, false); + ASSERT_TRUE(Connect()); +} + +// Connect with DTLS. B does DTLS-SRTP but A does not. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpNotOffered) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(false, true); + ASSERT_TRUE(Connect()); +} + +// Create two channels with DTLS, negotiate DTLS-SRTP, and transfer bypass SRTP. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpTwoChannels) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + +// Create a single channel with DTLS, and send normal data and SRTP data on it. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpDemux) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect()); + TestTransfer(0, 1000, 100, false); + TestTransfer(0, 1000, 100, true); +} + +// Testing when the remote is passive. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsAnswererIsPassive) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_PASSIVE)); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + +// Testing with the legacy DTLS client which doesn't use setup attribute. +// In this case legacy is the answerer. +TEST_F(DtlsTransportChannelTest, TestDtlsSetupWithLegacyAsAnswerer) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + PrepareDtls(true, true, rtc::KT_DEFAULT); + NegotiateWithLegacy(); + rtc::SSLRole channel1_role; + rtc::SSLRole channel2_role; + EXPECT_TRUE(client1_.transport()->GetSslRole(&channel1_role)); + EXPECT_TRUE(client2_.transport()->GetSslRole(&channel2_role)); + EXPECT_EQ(rtc::SSL_SERVER, channel1_role); + EXPECT_EQ(rtc::SSL_CLIENT, channel2_role); +} + +// Testing re offer/answer after the session is estbalished. Roles will be +// kept same as of the previous negotiation. +TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromOfferer) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + // Initial role for client1 is ACTPASS and client2 is ACTIVE. + ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_ACTIVE)); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); + // Using input roles for the re-offer. + Renegotiate(&client1_, cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + +TEST_F(DtlsTransportChannelTest, TestDtlsReOfferFromAnswerer) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + // Initial role for client1 is ACTPASS and client2 is ACTIVE. + ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_ACTIVE)); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); + // Using input roles for the re-offer. + Renegotiate(&client2_, cricket::CONNECTIONROLE_PASSIVE, + cricket::CONNECTIONROLE_ACTPASS, NF_REOFFER); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + +// Test that any change in role after the intial setup will result in failure. +TEST_F(DtlsTransportChannelTest, TestDtlsRoleReversal) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_PASSIVE)); + + // Renegotiate from client2 with actpass and client1 as active. + Renegotiate(&client2_, cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_ACTIVE, + NF_REOFFER | NF_EXPECT_FAILURE); +} + +// Test that using different setup attributes which results in similar ssl +// role as the initial negotiation will result in success. +TEST_F(DtlsTransportChannelTest, TestDtlsReOfferWithDifferentSetupAttr) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect(cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_PASSIVE)); + // Renegotiate from client2 with actpass and client1 as active. + Renegotiate(&client2_, cricket::CONNECTIONROLE_ACTIVE, + cricket::CONNECTIONROLE_ACTPASS, NF_REOFFER); + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + +// Test that re-negotiation can be started before the clients become connected +// in the first negotiation. +TEST_F(DtlsTransportChannelTest, TestRenegotiateBeforeConnect) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true, rtc::KT_DEFAULT); + PrepareDtlsSrtp(true, true); + Negotiate(); + + Renegotiate(&client1_, cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER); + bool rv = client1_.Connect(&client2_); + EXPECT_TRUE(rv); + EXPECT_TRUE_WAIT( + client1_.all_channels_writable() && client2_.all_channels_writable(), + 10000); + + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + +// Test Certificates state after negotiation but before connection. +TEST_F(DtlsTransportChannelTest, TestCertificatesBeforeConnect) { + MAYBE_SKIP_TEST(HaveDtls); + PrepareDtls(true, true, rtc::KT_DEFAULT); + Negotiate(); + + rtc::scoped_refptr<rtc::RTCCertificate> certificate1; + rtc::scoped_refptr<rtc::RTCCertificate> certificate2; + rtc::scoped_ptr<rtc::SSLCertificate> remote_cert1; + rtc::scoped_ptr<rtc::SSLCertificate> remote_cert2; + + // After negotiation, each side has a distinct local certificate, but still no + // remote certificate, because connection has not yet occurred. + ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1)); + ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2)); + ASSERT_NE(certificate1->ssl_certificate().ToPEMString(), + certificate2->ssl_certificate().ToPEMString()); + ASSERT_FALSE( + client1_.transport()->GetRemoteSSLCertificate(remote_cert1.accept())); + ASSERT_FALSE(remote_cert1 != NULL); + ASSERT_FALSE( + client2_.transport()->GetRemoteSSLCertificate(remote_cert2.accept())); + ASSERT_FALSE(remote_cert2 != NULL); +} + +// Test Certificates state after connection. +TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) { + MAYBE_SKIP_TEST(HaveDtls); + PrepareDtls(true, true, rtc::KT_DEFAULT); + ASSERT_TRUE(Connect()); + + rtc::scoped_refptr<rtc::RTCCertificate> certificate1; + rtc::scoped_refptr<rtc::RTCCertificate> certificate2; + rtc::scoped_ptr<rtc::SSLCertificate> remote_cert1; + rtc::scoped_ptr<rtc::SSLCertificate> remote_cert2; + + // After connection, each side has a distinct local certificate. + ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1)); + ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2)); + ASSERT_NE(certificate1->ssl_certificate().ToPEMString(), + certificate2->ssl_certificate().ToPEMString()); + + // Each side's remote certificate is the other side's local certificate. + ASSERT_TRUE( + client1_.transport()->GetRemoteSSLCertificate(remote_cert1.accept())); + ASSERT_EQ(remote_cert1->ToPEMString(), + certificate2->ssl_certificate().ToPEMString()); + ASSERT_TRUE( + client2_.transport()->GetRemoteSSLCertificate(remote_cert2.accept())); + ASSERT_EQ(remote_cert2->ToPEMString(), + certificate1->ssl_certificate().ToPEMString()); +} |