Index: webrtc/pc/srtpfilter_unittest.cc |
diff --git a/webrtc/pc/srtpfilter_unittest.cc b/webrtc/pc/srtpfilter_unittest.cc |
index af8b69f73e334496ef4e2bc80224a66ecd85d6f8..33ff2e31bb9ea124ed278d5b9c52ef6990aca67b 100644 |
--- a/webrtc/pc/srtpfilter_unittest.cc |
+++ b/webrtc/pc/srtpfilter_unittest.cc |
@@ -8,6 +8,8 @@ |
* be found in the AUTHORS file in the root of the source tree. |
*/ |
+#include <algorithm> |
+ |
#include "webrtc/pc/srtpfilter.h" |
#include "third_party/libsrtp/include/srtp.h" |
@@ -67,6 +69,8 @@ static const cricket::CryptoParams kTestCryptoParamsGcm3( |
1, "AEAD_AES_128_GCM", kTestKeyParamsGcm3, ""); |
static const cricket::CryptoParams kTestCryptoParamsGcm4( |
1, "AEAD_AES_128_GCM", kTestKeyParamsGcm4, ""); |
+static const char kDummyExtensionUri1[] = "dummy-extension-uri-1"; |
+static const char kDummyExtensionUri4[] = "dummy-extension-uri-4"; |
static int rtp_auth_tag_len(const std::string& cs) { |
if (cs == CS_AES_CM_128_HMAC_SHA1_32) { |
@@ -193,6 +197,99 @@ class SrtpFilterTest : public testing::Test { |
EXPECT_EQ(rtcp_len, out_len); |
EXPECT_EQ(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len)); |
} |
+ // 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) { |
+ // RTP header + extension header are the same. |
+ EXPECT_EQ(0, memcmp(packet1, 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++; |
+ } |
+ } |
+ } |
+ void TestProtectUnprotectHeaderEncryption(const std::string& cs1, |
+ const std::string& cs2, |
+ const std::vector<webrtc::RtpExtension>& encrypted_headers) { |
+ rtc::Buffer rtp_buffer(sizeof(kPcmuFrameWithExtensions) + |
+ rtp_auth_tag_len(cs1)); |
+ char* rtp_packet = rtp_buffer.data<char>(); |
+ char original_rtp_packet[sizeof(kPcmuFrameWithExtensions)]; |
+ int rtp_len = sizeof(kPcmuFrameWithExtensions), out_len; |
+ memcpy(rtp_packet, kPcmuFrameWithExtensions, rtp_len); |
+ // In order to be able to run this test function multiple times we can not |
+ // use the same sequence number twice. Increase the sequence number by one. |
+ rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet) + 2, |
+ ++sequence_number_); |
+ memcpy(original_rtp_packet, rtp_packet, rtp_len); |
+ |
+ std::vector<int> encrypted_header_ids; |
+ for (const webrtc::RtpExtension& extension : encrypted_headers) { |
+ EXPECT_TRUE(extension.encrypt); |
+ encrypted_header_ids.push_back(extension.id); |
+ } |
+ |
+ EXPECT_TRUE(f1_.ProtectRtp(rtp_packet, rtp_len, |
+ static_cast<int>(rtp_buffer.size()), |
+ &out_len)); |
+ EXPECT_EQ(out_len, rtp_len + rtp_auth_tag_len(cs1)); |
+ EXPECT_NE(0, memcmp(rtp_packet, original_rtp_packet, rtp_len)); |
+ CompareHeaderExtensions(rtp_packet, original_rtp_packet, |
+ encrypted_header_ids, false); |
+ EXPECT_TRUE(f2_.UnprotectRtp(rtp_packet, out_len, &out_len)); |
+ EXPECT_EQ(rtp_len, out_len); |
+ EXPECT_EQ(0, memcmp(rtp_packet, original_rtp_packet, rtp_len)); |
+ CompareHeaderExtensions(rtp_packet, original_rtp_packet, |
+ encrypted_header_ids, true); |
+ |
+ EXPECT_TRUE(f2_.ProtectRtp(rtp_packet, rtp_len, |
+ static_cast<int>(rtp_buffer.size()), |
+ &out_len)); |
+ EXPECT_EQ(out_len, rtp_len + rtp_auth_tag_len(cs2)); |
+ EXPECT_NE(0, memcmp(rtp_packet, original_rtp_packet, rtp_len)); |
+ CompareHeaderExtensions(rtp_packet, original_rtp_packet, |
+ encrypted_header_ids, false); |
+ EXPECT_TRUE(f1_.UnprotectRtp(rtp_packet, out_len, &out_len)); |
+ EXPECT_EQ(rtp_len, out_len); |
+ EXPECT_EQ(0, memcmp(rtp_packet, original_rtp_packet, rtp_len)); |
+ CompareHeaderExtensions(rtp_packet, original_rtp_packet, |
+ encrypted_header_ids, true); |
+ } |
void TestProtectSetParamsDirect(bool enable_external_auth, int cs, |
const uint8_t* key1, int key1_len, const uint8_t* key2, int key2_len, |
const std::string& cs_name) { |
@@ -217,6 +314,29 @@ class SrtpFilterTest : public testing::Test { |
} |
TestProtectUnprotect(cs_name, cs_name); |
} |
+ void TestProtectSetParamsDirectHeaderEncryption(int cs, |
+ const uint8_t* key1, int key1_len, const uint8_t* key2, int key2_len, |
+ const std::string& cs_name) { |
+ std::vector<webrtc::RtpExtension> encrypted_headers; |
+ encrypted_headers.push_back( |
+ webrtc::RtpExtension(kDummyExtensionUri1, 1, true)); |
+ // Don't encrypt header ids 2 and 3. |
+ encrypted_headers.push_back( |
+ webrtc::RtpExtension(kDummyExtensionUri4, 4, true)); |
+ EXPECT_EQ(key1_len, key2_len); |
+ EXPECT_EQ(cs_name, rtc::SrtpCryptoSuiteToName(cs)); |
+ f1_.SetEncryptedHeaderExtensions(CS_LOCAL, encrypted_headers); |
+ f1_.SetEncryptedHeaderExtensions(CS_REMOTE, encrypted_headers); |
+ f2_.SetEncryptedHeaderExtensions(CS_LOCAL, encrypted_headers); |
+ f2_.SetEncryptedHeaderExtensions(CS_REMOTE, encrypted_headers); |
+ EXPECT_TRUE(f1_.SetRtpParams(cs, key1, key1_len, cs, key2, key2_len)); |
+ EXPECT_TRUE(f2_.SetRtpParams(cs, key2, key2_len, cs, key1, key1_len)); |
+ EXPECT_TRUE(f1_.IsActive()); |
+ EXPECT_TRUE(f2_.IsActive()); |
+ EXPECT_FALSE(f1_.IsExternalAuthActive()); |
+ EXPECT_FALSE(f2_.IsExternalAuthActive()); |
+ TestProtectUnprotectHeaderEncryption(cs_name, cs_name, encrypted_headers); |
+ } |
cricket::SrtpFilter f1_; |
cricket::SrtpFilter f2_; |
int sequence_number_; |
@@ -619,6 +739,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_AES_CM_128_HMAC_SHA1_80) { |
CS_AES_CM_128_HMAC_SHA1_80); |
} |
+TEST_F(SrtpFilterTest, |
+ TestProtectSetParamsDirectHeaderEncryption_AES_CM_128_HMAC_SHA1_80) { |
+ TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AES128_CM_SHA1_80, |
+ kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen, |
+ CS_AES_CM_128_HMAC_SHA1_80); |
+} |
+ |
// Test directly setting the params with AES_CM_128_HMAC_SHA1_32. |
TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_AES_CM_128_HMAC_SHA1_32) { |
bool enable_external_auth = GetParam(); |
@@ -627,6 +754,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_AES_CM_128_HMAC_SHA1_32) { |
CS_AES_CM_128_HMAC_SHA1_32); |
} |
+TEST_F(SrtpFilterTest, |
+ TestProtectSetParamsDirectHeaderEncryption_AES_CM_128_HMAC_SHA1_32) { |
+ TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AES128_CM_SHA1_32, |
+ kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen, |
+ CS_AES_CM_128_HMAC_SHA1_32); |
+} |
+ |
// Test directly setting the params with SRTP_AEAD_AES_128_GCM. |
TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_128_GCM) { |
bool enable_external_auth = GetParam(); |
@@ -635,6 +769,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_128_GCM) { |
CS_AEAD_AES_128_GCM); |
} |
+TEST_F(SrtpFilterTest, |
+ TestProtectSetParamsDirectHeaderEncryption_SRTP_AEAD_AES_128_GCM) { |
+ TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AEAD_AES_128_GCM, |
+ kTestKeyGcm128_1, kTestKeyGcm128Len, kTestKeyGcm128_2, kTestKeyGcm128Len, |
+ CS_AEAD_AES_128_GCM); |
+} |
+ |
// Test directly setting the params with SRTP_AEAD_AES_256_GCM. |
TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_256_GCM) { |
bool enable_external_auth = GetParam(); |
@@ -643,6 +784,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_256_GCM) { |
CS_AEAD_AES_256_GCM); |
} |
+TEST_F(SrtpFilterTest, |
+ TestProtectSetParamsDirectHeaderEncryption_SRTP_AEAD_AES_256_GCM) { |
+ TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AEAD_AES_256_GCM, |
+ kTestKeyGcm256_1, kTestKeyGcm256Len, kTestKeyGcm256_2, kTestKeyGcm256Len, |
+ CS_AEAD_AES_256_GCM); |
+} |
+ |
// Run all tests both with and without external auth enabled. |
INSTANTIATE_TEST_CASE_P(ExternalAuth, |
SrtpFilterProtectSetParamsDirectTest, |
@@ -704,30 +852,44 @@ class SrtpSessionTest : public testing::Test { |
// Test that we can set up the session and keys properly. |
TEST_F(SrtpSessionTest, TestGoodSetup) { |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
- EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
+ std::vector<int> no_encrypted_headers; |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
+ EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
} |
// Test that we can't change the keys once set. |
TEST_F(SrtpSessionTest, TestBadSetup) { |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
- EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
+ std::vector<int> no_encrypted_headers; |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
+ EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
EXPECT_FALSE( |
- s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey2, kTestKeyLen)); |
+ s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey2, kTestKeyLen, |
+ no_encrypted_headers)); |
EXPECT_FALSE( |
- s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey2, kTestKeyLen)); |
+ s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey2, kTestKeyLen, |
+ no_encrypted_headers)); |
} |
// Test that we fail keys of the wrong length. |
TEST_F(SrtpSessionTest, TestKeysTooShort) { |
- EXPECT_FALSE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, 1)); |
- EXPECT_FALSE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, 1)); |
+ std::vector<int> no_encrypted_headers; |
+ EXPECT_FALSE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, 1, |
+ no_encrypted_headers)); |
+ EXPECT_FALSE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, 1, |
+ no_encrypted_headers)); |
} |
// Test that we can encrypt and decrypt RTP/RTCP using AES_CM_128_HMAC_SHA1_80. |
TEST_F(SrtpSessionTest, TestProtect_AES_CM_128_HMAC_SHA1_80) { |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
- EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
+ std::vector<int> no_encrypted_headers; |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
+ EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
TestProtectRtp(CS_AES_CM_128_HMAC_SHA1_80); |
TestProtectRtcp(CS_AES_CM_128_HMAC_SHA1_80); |
TestUnprotectRtp(CS_AES_CM_128_HMAC_SHA1_80); |
@@ -736,8 +898,11 @@ TEST_F(SrtpSessionTest, TestProtect_AES_CM_128_HMAC_SHA1_80) { |
// Test that we can encrypt and decrypt RTP/RTCP using AES_CM_128_HMAC_SHA1_32. |
TEST_F(SrtpSessionTest, TestProtect_AES_CM_128_HMAC_SHA1_32) { |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1, kTestKeyLen)); |
- EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1, kTestKeyLen)); |
+ std::vector<int> no_encrypted_headers; |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
+ EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
TestProtectRtp(CS_AES_CM_128_HMAC_SHA1_32); |
TestProtectRtcp(CS_AES_CM_128_HMAC_SHA1_32); |
TestUnprotectRtp(CS_AES_CM_128_HMAC_SHA1_32); |
@@ -745,7 +910,9 @@ TEST_F(SrtpSessionTest, TestProtect_AES_CM_128_HMAC_SHA1_32) { |
} |
TEST_F(SrtpSessionTest, TestGetSendStreamPacketIndex) { |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1, kTestKeyLen)); |
+ std::vector<int> no_encrypted_headers; |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
int64_t index; |
int out_len = 0; |
EXPECT_TRUE(s1_.ProtectRtp(rtp_packet_, rtp_len_, |
@@ -757,9 +924,12 @@ TEST_F(SrtpSessionTest, TestGetSendStreamPacketIndex) { |
// Test that we fail to unprotect if someone tampers with the RTP/RTCP paylaods. |
TEST_F(SrtpSessionTest, TestTamperReject) { |
+ std::vector<int> no_encrypted_headers; |
int out_len; |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
- EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
+ EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
TestProtectRtp(CS_AES_CM_128_HMAC_SHA1_80); |
TestProtectRtcp(CS_AES_CM_128_HMAC_SHA1_80); |
rtp_packet_[0] = 0x12; |
@@ -770,17 +940,22 @@ TEST_F(SrtpSessionTest, TestTamperReject) { |
// Test that we fail to unprotect if the payloads are not authenticated. |
TEST_F(SrtpSessionTest, TestUnencryptReject) { |
+ std::vector<int> no_encrypted_headers; |
int out_len; |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
- EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
+ EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
EXPECT_FALSE(s2_.UnprotectRtp(rtp_packet_, rtp_len_, &out_len)); |
EXPECT_FALSE(s2_.UnprotectRtcp(rtcp_packet_, rtcp_len_, &out_len)); |
} |
// Test that we fail when using buffers that are too small. |
TEST_F(SrtpSessionTest, TestBuffersTooSmall) { |
+ std::vector<int> no_encrypted_headers; |
int out_len; |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
EXPECT_FALSE(s1_.ProtectRtp(rtp_packet_, rtp_len_, |
sizeof(rtp_packet_) - 10, &out_len)); |
EXPECT_FALSE(s1_.ProtectRtcp(rtcp_packet_, rtcp_len_, |
@@ -788,14 +963,17 @@ TEST_F(SrtpSessionTest, TestBuffersTooSmall) { |
} |
TEST_F(SrtpSessionTest, TestReplay) { |
+ std::vector<int> no_encrypted_headers; |
static const uint16_t kMaxSeqnum = static_cast<uint16_t>(-1); |
static const uint16_t seqnum_big = 62275; |
static const uint16_t seqnum_small = 10; |
static const uint16_t replay_window = 1024; |
int out_len; |
- EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
- EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen)); |
+ EXPECT_TRUE(s1_.SetSend(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
+ EXPECT_TRUE(s2_.SetRecv(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen, |
+ no_encrypted_headers)); |
// Initial sequence number. |
rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_) + 2, seqnum_big); |