Index: webrtc/pc/channel_unittest.cc |
diff --git a/webrtc/pc/channel_unittest.cc b/webrtc/pc/channel_unittest.cc |
index 99cca88637a877f018c6f7be8c22ee09a42c8023..c052f67cb4612ca4431f93383e9feca6d93f188e 100644 |
--- a/webrtc/pc/channel_unittest.cc |
+++ b/webrtc/pc/channel_unittest.cc |
@@ -101,6 +101,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { |
// Use BaseChannel with PacketTransportInternal rather than |
// DtlsTransportInternal. |
RAW_PACKET_TRANSPORT = 0x40, |
+ ENCRYPTED_HEADERS = 0x80, |
}; |
ChannelTest(bool verify_playout, |
@@ -265,6 +266,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { |
(flags & SECURE) != 0); |
rtc::CryptoOptions crypto_options; |
crypto_options.enable_gcm_crypto_suites = (flags & GCM_CIPHER) != 0; |
+ crypto_options.enable_encrypted_rtp_header_extensions = |
+ (flags & ENCRYPTED_HEADERS) != 0; |
channel->SetCryptoOptions(crypto_options); |
if (!channel->NeedsRtcpTransport()) { |
fake_rtcp_dtls_transport = nullptr; |
@@ -892,6 +895,179 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { |
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0)); |
} |
+ // Test that encrypted header extensions are working and can be changed when |
+ // sending a new OFFER/ANSWER. |
+ void TestChangeEncryptedHeaderExtensions(int flags) { |
+ 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), data, |
+ encrypted_headers, false); |
+ } |
+ // This expects both packets to be based on kPcmuFrameWithExtensions. |
+ // Header extensions with an id in "encrypted_headers" are expected to be |
+ // different in the packets unless "expect_equal" is set to "true". |
+ void CompareHeaderExtensions(const char* packet1, const char* packet2, |
+ const std::vector<int> encrypted_headers, |
+ bool expect_equal) { |
Taylor Brandstetter
2017/04/01 00:28:59
Since this is only called with "false" in this fil
joachim
2017/04/17 10:46:09
Moved to "webrtc/media/base/fakertp.cc" to avoid c
|
+ // RTP extension headers are the same. |
+ EXPECT_EQ(0, memcmp(packet1 + 12, packet2 + 12, 4)); |
+ // Check for one-byte header extensions. |
+ EXPECT_EQ('\xBE', packet1[12]); |
+ EXPECT_EQ('\xDE', packet1[13]); |
+ // Determine position and size of extension headers. |
+ size_t extension_words = packet1[14] << 8 | packet1[15]; |
+ const char* extension_data1 = packet1 + 12 + 4; |
+ const char* extension_end1 = extension_data1 + extension_words * 4; |
+ const char* extension_data2 = packet2 + 12 + 4; |
+ while (extension_data1 < extension_end1) { |
+ uint8_t id = (*extension_data1 & 0xf0) >> 4; |
+ uint8_t len = (*extension_data1 & 0x0f) +1; |
+ extension_data1++; |
+ extension_data2++; |
+ EXPECT_LE(extension_data1, extension_end1); |
+ if (id == 15) { |
+ // Finished parsing. |
+ break; |
+ } |
+ |
+ // The header extension doesn't get encrypted if the id not in the |
+ // list of header extensions to encrypt. |
+ if (expect_equal || |
+ std::find(encrypted_headers.begin(), encrypted_headers.end(), id) |
+ == encrypted_headers.end()) { |
+ EXPECT_EQ(0, memcmp(extension_data1, extension_data2, len)); |
+ } else { |
+ EXPECT_NE(0, memcmp(extension_data1, extension_data2, len)); |
+ } |
+ |
+ extension_data1 += len; |
+ extension_data2 += len; |
+ // Skip padding. |
+ while (extension_data1 < extension_end1 && *extension_data1 == 0) { |
+ extension_data1++; |
+ extension_data2++; |
+ } |
+ } |
+ } |
+ 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); |
+ |
+ 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()); |
+ |
+ EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL)); |
+ EXPECT_EQ(1u, media_channel2_->recv_streams().size()); |
+ ConnectFakeTransports(); |
+ |
+ // 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(channel1_->SetRemoteContent(&content2, CA_ANSWER, NULL)); |
+ EXPECT_EQ(1u, media_channel1_->recv_streams().size()); |
+ EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER, NULL)); |
+ EXPECT_TRUE(channel2_->Enable(true)); |
+ EXPECT_EQ(1u, media_channel2_->send_streams().size()); |
+ |
+ packet_listener1.encrypted_headers.push_back(1); |
+ packet_listener2.encrypted_headers.push_back(1); |
+ |
+ 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]); |
+ |
+ 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 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()); |
+ |
+ EXPECT_TRUE(channel2_->SetRemoteContent(&content4, CA_ANSWER, NULL)); |
+ EXPECT_EQ(1u, media_channel2_->recv_streams().size()); |
+ |
+ packet_listener1.encrypted_headers.clear(); |
+ packet_listener1.encrypted_headers.push_back(2); |
+ packet_listener1.encrypted_headers.push_back(4); |
+ packet_listener2.encrypted_headers.clear(); |
+ packet_listener2.encrypted_headers.push_back(2); |
+ packet_listener2.encrypted_headers.push_back(4); |
+ |
+ SendCustomRtp1(kSsrc1, 0); |
+ SendCustomRtp2(kSsrc2, 0); |
+ WaitForThreads(); |
+ EXPECT_TRUE(CheckCustomRtp2(kSsrc1, 0)); |
+ EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0)); |
+ } |
Taylor Brandstetter
2017/04/01 00:28:59
Once we decide how to resolve the other comments:
joachim
2017/04/17 10:46:09
Acknowledged - though I need to find out how to do
Taylor Brandstetter
2017/04/19 06:48:39
Well, ConnectFakeTransports is what simulates DTLS
joachim
2017/04/19 23:40:19
Done. Also added a check for correct encrypted ext
|
+ |
// Test that we only start playout and sending at the right times. |
void TestPlayoutAndSendingStates() { |
CreateChannels(0, 0); |
@@ -2108,6 +2284,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( |
@@ -2247,6 +2441,24 @@ TEST_F(VoiceChannelSingleThreadTest, TestChangeStreamParamsInContent) { |
Base::TestChangeStreamParamsInContent(); |
} |
+TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest, |
+ TestChangeEncryptedHeaderExtensionsDtls) { |
+ int flags = DTLS; |
+ Base::TestChangeEncryptedHeaderExtensions(flags); |
+} |
+ |
+TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest, |
+ TestChangeEncryptedHeaderExtensionsDtlsGcm) { |
+ int flags = DTLS | GCM_CIPHER; |
+ Base::TestChangeEncryptedHeaderExtensions(flags); |
+} |
+ |
+TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest, |
+ TestChangeEncryptedHeaderExtensionsSDES) { |
+ int flags = 0; |
+ Base::TestChangeEncryptedHeaderExtensions(flags); |
+} |
+ |
TEST_F(VoiceChannelSingleThreadTest, TestPlayoutAndSendingStates) { |
Base::TestPlayoutAndSendingStates(); |
} |
@@ -2580,6 +2792,24 @@ TEST_F(VoiceChannelDoubleThreadTest, TestChangeStreamParamsInContent) { |
Base::TestChangeStreamParamsInContent(); |
} |
+TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest, |
+ TestChangeEncryptedHeaderExtensionsDtls) { |
+ int flags = DTLS; |
+ Base::TestChangeEncryptedHeaderExtensions(flags); |
+} |
+ |
+TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest, |
+ TestChangeEncryptedHeaderExtensionsDtlsGcm) { |
+ int flags = DTLS | GCM_CIPHER; |
+ Base::TestChangeEncryptedHeaderExtensions(flags); |
+} |
+ |
+TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest, |
+ TestChangeEncryptedHeaderExtensionsSDES) { |
+ int flags = 0; |
+ Base::TestChangeEncryptedHeaderExtensions(flags); |
+} |
+ |
TEST_F(VoiceChannelDoubleThreadTest, TestPlayoutAndSendingStates) { |
Base::TestPlayoutAndSendingStates(); |
} |