| Index: webrtc/pc/channel_unittest.cc
|
| diff --git a/webrtc/pc/channel_unittest.cc b/webrtc/pc/channel_unittest.cc
|
| index cb7d318982c1c8a6ee1460176d9eb4b5a89316fd..7fe1472e25a33efff3bdee5326ce129e9f6af792 100644
|
| --- a/webrtc/pc/channel_unittest.cc
|
| +++ b/webrtc/pc/channel_unittest.cc
|
| @@ -100,6 +100,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
| // Use BaseChannel with PacketTransportInternal rather than
|
| // DtlsTransportInternal.
|
| RAW_PACKET_TRANSPORT = 0x20,
|
| + GCM_CIPHER = 0x40,
|
| + ENCRYPTED_HEADERS = 0x80,
|
| };
|
|
|
| ChannelTest(bool verify_playout,
|
| @@ -175,6 +177,22 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
| fake_rtcp_dtls_transport1_->SetLocalCertificate(cert1);
|
| }
|
| }
|
| + if (flags1 & ENCRYPTED_HEADERS) {
|
| + rtc::CryptoOptions crypto_options;
|
| + crypto_options.enable_encrypted_rtp_header_extensions = true;
|
| + fake_rtp_dtls_transport1_->SetCryptoOptions(crypto_options);
|
| + if (fake_rtcp_dtls_transport1_) {
|
| + fake_rtcp_dtls_transport1_->SetCryptoOptions(crypto_options);
|
| + }
|
| + }
|
| + if (flags1 & GCM_CIPHER) {
|
| + fake_rtp_dtls_transport1_->SetSrtpCryptoSuite(
|
| + rtc::SRTP_AEAD_AES_256_GCM);
|
| + if (fake_rtcp_dtls_transport1_) {
|
| + fake_rtcp_dtls_transport1_->SetSrtpCryptoSuite(
|
| + rtc::SRTP_AEAD_AES_256_GCM);
|
| + }
|
| + }
|
| }
|
| // Based on flags, create fake DTLS or raw packet transports.
|
| if (flags2 & RAW_PACKET_TRANSPORT) {
|
| @@ -205,6 +223,22 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
| fake_rtcp_dtls_transport2_->SetLocalCertificate(cert2);
|
| }
|
| }
|
| + if (flags2 & ENCRYPTED_HEADERS) {
|
| + rtc::CryptoOptions crypto_options;
|
| + crypto_options.enable_encrypted_rtp_header_extensions = true;
|
| + fake_rtp_dtls_transport2_->SetCryptoOptions(crypto_options);
|
| + if (fake_rtcp_dtls_transport2_) {
|
| + fake_rtcp_dtls_transport2_->SetCryptoOptions(crypto_options);
|
| + }
|
| + }
|
| + if (flags2 & GCM_CIPHER) {
|
| + fake_rtp_dtls_transport2_->SetSrtpCryptoSuite(
|
| + rtc::SRTP_AEAD_AES_256_GCM);
|
| + if (fake_rtcp_dtls_transport2_) {
|
| + fake_rtcp_dtls_transport2_->SetSrtpCryptoSuite(
|
| + rtc::SRTP_AEAD_AES_256_GCM);
|
| + }
|
| + }
|
| }
|
| channel1_.reset(
|
| CreateChannel(worker_thread, network_thread_, &media_engine_, ch1,
|
| @@ -869,6 +903,183 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
| EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
| }
|
|
|
| + enum EncryptedHeaderTestScenario {
|
| + // Offer/Answer are processed before DTLS completes.
|
| + DEFAULT,
|
| + // DTLS completes before any Offer/Answer have been sent.
|
| + DTLS_BEFORE_OFFER_ANSWER,
|
| + // DTLS completes after channel 2 has processed (remote) Offer and (local)
|
| + // Answer.
|
| + DTLS_AFTER_CHANNEL2_READY,
|
| + };
|
| +
|
| + // Test that encrypted header extensions are working and can be changed when
|
| + // sending a new OFFER/ANSWER.
|
| + void TestChangeEncryptedHeaderExtensions(int flags,
|
| + EncryptedHeaderTestScenario scenario = DEFAULT) {
|
| + RTC_CHECK(scenario == 0 || (flags & DTLS));
|
| + struct PacketListener : public sigslot::has_slots<> {
|
| + PacketListener() {}
|
| + void OnReadPacket(rtc::PacketTransportInternal* transport,
|
| + const char* data, size_t size, const rtc::PacketTime& time,
|
| + int flags) {
|
| + CompareHeaderExtensions(
|
| + reinterpret_cast<const char*>(kPcmuFrameWithExtensions),
|
| + sizeof(kPcmuFrameWithExtensions), data, size, encrypted_headers,
|
| + false);
|
| + }
|
| + std::vector<int> encrypted_headers;
|
| + } packet_listener1, packet_listener2;
|
| +
|
| + cricket::StreamParams stream1;
|
| + stream1.groupid = "group1";
|
| + stream1.id = "stream1";
|
| + stream1.ssrcs.push_back(kSsrc1);
|
| + stream1.cname = "stream1_cname";
|
| +
|
| + cricket::StreamParams stream2;
|
| + stream2.groupid = "group1";
|
| + stream2.id = "stream2";
|
| + stream2.ssrcs.push_back(kSsrc2);
|
| + stream2.cname = "stream2_cname";
|
| +
|
| + // Use SRTP when testing encrypted extensions.
|
| + int channel_flags = flags | SECURE | ENCRYPTED_HEADERS;
|
| + // Enable SDES if channel is not using DTLS.
|
| + int content_flags = (channel_flags & DTLS) == 0 ? SECURE : 0;
|
| +
|
| + // kPcmuFrameWithExtensions contains RTP extension headers with ids 1-4.
|
| + // Make sure to use URIs that are supported for encryption.
|
| + cricket::RtpHeaderExtensions extensions1;
|
| + extensions1.push_back(
|
| + RtpExtension(RtpExtension::kAudioLevelUri, 10));
|
| + extensions1.push_back(
|
| + RtpExtension(RtpExtension::kAudioLevelUri, 1, true));
|
| +
|
| + cricket::RtpHeaderExtensions extensions2;
|
| + extensions2.push_back(
|
| + RtpExtension(RtpExtension::kAudioLevelUri, 10));
|
| + extensions2.push_back(
|
| + RtpExtension(RtpExtension::kAudioLevelUri, 2, true));
|
| + extensions2.push_back(
|
| + RtpExtension(RtpExtension::kVideoRotationUri, 3));
|
| + extensions2.push_back(
|
| + RtpExtension(RtpExtension::kTimestampOffsetUri, 4, true));
|
| +
|
| + // Setup a call where channel 1 send |stream1| to channel 2.
|
| + CreateChannels(channel_flags, channel_flags);
|
| + fake_rtp_dtls_transport1_->fake_ice_transport()->SignalReadPacket.connect(
|
| + &packet_listener1, &PacketListener::OnReadPacket);
|
| + fake_rtp_dtls_transport2_->fake_ice_transport()->SignalReadPacket.connect(
|
| + &packet_listener2, &PacketListener::OnReadPacket);
|
| +
|
| + if (scenario == DTLS_BEFORE_OFFER_ANSWER) {
|
| + ConnectFakeTransports();
|
| + WaitForThreads();
|
| + }
|
| +
|
| + typename T::Content content1;
|
| + CreateContent(content_flags, kPcmuCodec, kH264Codec, &content1);
|
| + content1.AddStream(stream1);
|
| + content1.set_rtp_header_extensions(extensions1);
|
| + EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER, NULL));
|
| + EXPECT_TRUE(channel1_->Enable(true));
|
| + EXPECT_EQ(1u, media_channel1_->send_streams().size());
|
| + packet_listener1.encrypted_headers.push_back(1);
|
| +
|
| + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL));
|
| + EXPECT_EQ(1u, media_channel2_->recv_streams().size());
|
| +
|
| + // Channel 2 sends back |stream2|.
|
| + typename T::Content content2;
|
| + CreateContent(content_flags, kPcmuCodec, kH264Codec, &content2);
|
| + content2.AddStream(stream2);
|
| + content2.set_rtp_header_extensions(extensions1);
|
| + EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER, NULL));
|
| + EXPECT_TRUE(channel2_->Enable(true));
|
| + EXPECT_EQ(1u, media_channel2_->send_streams().size());
|
| + packet_listener2.encrypted_headers.push_back(1);
|
| +
|
| + if (scenario == DTLS_AFTER_CHANNEL2_READY) {
|
| + ConnectFakeTransports();
|
| + WaitForThreads();
|
| + }
|
| +
|
| + if (scenario == DTLS_BEFORE_OFFER_ANSWER ||
|
| + scenario == DTLS_AFTER_CHANNEL2_READY) {
|
| + // In both scenarios with partially completed Offer/Answer, sending
|
| + // packets from Channel 2 to Channel 1 should work.
|
| + SendCustomRtp2(kSsrc2, 0);
|
| + WaitForThreads();
|
| + EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
| + }
|
| +
|
| + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_ANSWER, NULL));
|
| + EXPECT_EQ(1u, media_channel1_->recv_streams().size());
|
| +
|
| + if (scenario == DEFAULT) {
|
| + ConnectFakeTransports();
|
| + WaitForThreads();
|
| + }
|
| +
|
| + SendCustomRtp1(kSsrc1, 0);
|
| + SendCustomRtp2(kSsrc2, 0);
|
| + WaitForThreads();
|
| + EXPECT_TRUE(CheckCustomRtp2(kSsrc1, 0));
|
| + EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
| +
|
| + // Let channel 2 update the encrypted header extensions.
|
| + typename T::Content content3;
|
| + CreateContent(content_flags, kPcmuCodec, kH264Codec, &content3);
|
| + content3.AddStream(stream2);
|
| + content3.set_rtp_header_extensions(extensions2);
|
| + EXPECT_TRUE(channel2_->SetLocalContent(&content3, CA_OFFER, NULL));
|
| + ASSERT_EQ(1u, media_channel2_->send_streams().size());
|
| + EXPECT_EQ(stream2, media_channel2_->send_streams()[0]);
|
| + packet_listener2.encrypted_headers.clear();
|
| + packet_listener2.encrypted_headers.push_back(2);
|
| + packet_listener2.encrypted_headers.push_back(4);
|
| +
|
| + EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_OFFER, NULL));
|
| + ASSERT_EQ(1u, media_channel1_->recv_streams().size());
|
| + EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]);
|
| +
|
| + // Channel 1 is already sending the new encrypted extensions. These
|
| + // can be decrypted by channel 2. Channel 2 is still sending the old
|
| + // encrypted extensions (which can be decrypted by channel 1).
|
| +
|
| + if (flags & DTLS) {
|
| + // DTLS supports updating the encrypted extensions with only the OFFER
|
| + // being processed. For SDES both the OFFER and ANSWER must have been
|
| + // processed to update encrypted extensions, so we can't check this case.
|
| + SendCustomRtp1(kSsrc1, 0);
|
| + SendCustomRtp2(kSsrc2, 0);
|
| + WaitForThreads();
|
| + EXPECT_TRUE(CheckCustomRtp2(kSsrc1, 0));
|
| + EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
| + }
|
| +
|
| + // Channel 1 replies with the same extensions.
|
| + typename T::Content content4;
|
| + CreateContent(content_flags, kPcmuCodec, kH264Codec, &content4);
|
| + content4.AddStream(stream1);
|
| + content4.set_rtp_header_extensions(extensions2);
|
| + EXPECT_TRUE(channel1_->SetLocalContent(&content4, CA_ANSWER, NULL));
|
| + EXPECT_EQ(1u, media_channel1_->send_streams().size());
|
| + packet_listener1.encrypted_headers.clear();
|
| + packet_listener1.encrypted_headers.push_back(2);
|
| + packet_listener1.encrypted_headers.push_back(4);
|
| +
|
| + EXPECT_TRUE(channel2_->SetRemoteContent(&content4, CA_ANSWER, NULL));
|
| + EXPECT_EQ(1u, media_channel2_->recv_streams().size());
|
| +
|
| + SendCustomRtp1(kSsrc1, 0);
|
| + SendCustomRtp2(kSsrc2, 0);
|
| + WaitForThreads();
|
| + EXPECT_TRUE(CheckCustomRtp2(kSsrc1, 0));
|
| + EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
| + }
|
| +
|
| // Test that we only start playout and sending at the right times.
|
| void TestPlayoutAndSendingStates() {
|
| CreateChannels(0, 0);
|
| @@ -1955,6 +2166,24 @@ class VoiceChannelDoubleThreadTest : public ChannelTest<VoiceTraits> {
|
| : Base(true, kPcmuFrame, kRtcpReport, NetworkIsWorker::No) {}
|
| };
|
|
|
| +class VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest
|
| + : public ChannelTest<VoiceTraits> {
|
| + public:
|
| + typedef ChannelTest<VoiceTraits> Base;
|
| + VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest()
|
| + : Base(true, kPcmuFrameWithExtensions, kRtcpReport,
|
| + NetworkIsWorker::Yes) {}
|
| +};
|
| +
|
| +class VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest
|
| + : public ChannelTest<VoiceTraits> {
|
| + public:
|
| + typedef ChannelTest<VoiceTraits> Base;
|
| + VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest()
|
| + : Base(true, kPcmuFrameWithExtensions, kRtcpReport,
|
| + NetworkIsWorker::No) {}
|
| +};
|
| +
|
| // override to add NULL parameter
|
| template <>
|
| cricket::VideoChannel* ChannelTest<VideoTraits>::CreateChannel(
|
| @@ -2091,6 +2320,48 @@ TEST_F(VoiceChannelSingleThreadTest, TestChangeStreamParamsInContent) {
|
| Base::TestChangeStreamParamsInContent();
|
| }
|
|
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtls) {
|
| + int flags = DTLS;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsScenario1) {
|
| + int flags = DTLS;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsScenario2) {
|
| + int flags = DTLS;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsGcm) {
|
| + int flags = DTLS | GCM_CIPHER;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsGcmScenario1) {
|
| + int flags = DTLS | GCM_CIPHER;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsGcmScenario2) {
|
| + int flags = DTLS | GCM_CIPHER;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsSDES) {
|
| + int flags = 0;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags);
|
| +}
|
| +
|
| TEST_F(VoiceChannelSingleThreadTest, TestPlayoutAndSendingStates) {
|
| Base::TestPlayoutAndSendingStates();
|
| }
|
| @@ -2408,6 +2679,48 @@ TEST_F(VoiceChannelDoubleThreadTest, TestChangeStreamParamsInContent) {
|
| Base::TestChangeStreamParamsInContent();
|
| }
|
|
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtls) {
|
| + int flags = DTLS;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsScenario1) {
|
| + int flags = DTLS;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsScenario2) {
|
| + int flags = DTLS;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsGcm) {
|
| + int flags = DTLS | GCM_CIPHER;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsGcmScenario1) {
|
| + int flags = DTLS | GCM_CIPHER;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsDtlsGcmScenario2) {
|
| + int flags = DTLS | GCM_CIPHER;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
| +}
|
| +
|
| +TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
| + TestChangeEncryptedHeaderExtensionsSDES) {
|
| + int flags = 0;
|
| + Base::TestChangeEncryptedHeaderExtensions(flags);
|
| +}
|
| +
|
| TEST_F(VoiceChannelDoubleThreadTest, TestPlayoutAndSendingStates) {
|
| Base::TestPlayoutAndSendingStates();
|
| }
|
|
|