Support GCM ciphers even if ENABLE_EXTERNAL_AUTH is defined.
With ENABLE_EXTERNAL_AUTH, external auth will only be used depending
on the selected cipher (allowed for non-GCM, not allowed for GCM).
BUG=webrtc:5222, chromium:628400
Review-Url: https://codereview.webrtc.org/2720663003
Cr-Commit-Position: refs/heads/master@{#16955}
diff --git a/webrtc/pc/BUILD.gn b/webrtc/pc/BUILD.gn
index 0d86bc3..eed2ee9 100644
--- a/webrtc/pc/BUILD.gn
+++ b/webrtc/pc/BUILD.gn
@@ -55,7 +55,7 @@
"../media",
]
- if (build_with_chromium) {
+ if (rtc_enable_external_auth) {
sources += [
"externalhmac.cc",
"externalhmac.h",
diff --git a/webrtc/pc/channel.cc b/webrtc/pc/channel.cc
index 9ac9d20..68fdabb 100644
--- a/webrtc/pc/channel.cc
+++ b/webrtc/pc/channel.cc
@@ -740,22 +740,27 @@
res = srtp_filter_.ProtectRtp(
data, len, static_cast<int>(packet->capacity()), &len);
#else
- updated_options.packet_time_params.rtp_sendtime_extension_id =
- rtp_abs_sendtime_extn_id_;
- res = srtp_filter_.ProtectRtp(
- data, len, static_cast<int>(packet->capacity()), &len,
- &updated_options.packet_time_params.srtp_packet_index);
- // If protection succeeds, let's get auth params from srtp.
- if (res) {
- uint8_t* auth_key = NULL;
- int key_len;
- res = srtp_filter_.GetRtpAuthParams(
- &auth_key, &key_len,
- &updated_options.packet_time_params.srtp_auth_tag_len);
+ if (!srtp_filter_.IsExternalAuthActive()) {
+ res = srtp_filter_.ProtectRtp(
+ data, len, static_cast<int>(packet->capacity()), &len);
+ } else {
+ updated_options.packet_time_params.rtp_sendtime_extension_id =
+ rtp_abs_sendtime_extn_id_;
+ res = srtp_filter_.ProtectRtp(
+ data, len, static_cast<int>(packet->capacity()), &len,
+ &updated_options.packet_time_params.srtp_packet_index);
+ // If protection succeeds, let's get auth params from srtp.
if (res) {
- updated_options.packet_time_params.srtp_auth_key.resize(key_len);
- updated_options.packet_time_params.srtp_auth_key.assign(
- auth_key, auth_key + key_len);
+ uint8_t* auth_key = NULL;
+ int key_len;
+ res = srtp_filter_.GetRtpAuthParams(
+ &auth_key, &key_len,
+ &updated_options.packet_time_params.srtp_auth_tag_len);
+ if (res) {
+ updated_options.packet_time_params.srtp_auth_key.resize(key_len);
+ updated_options.packet_time_params.srtp_auth_key.assign(
+ auth_key, auth_key + key_len);
+ }
}
}
#endif
diff --git a/webrtc/pc/channelmanager.cc b/webrtc/pc/channelmanager.cc
index 150dfd9..f2e0d71 100644
--- a/webrtc/pc/channelmanager.cc
+++ b/webrtc/pc/channelmanager.cc
@@ -101,14 +101,6 @@
LOG(LS_WARNING) << "Not changing crypto options in existing channels.";
}
crypto_options_ = crypto_options;
-#if defined(ENABLE_EXTERNAL_AUTH)
- if (crypto_options_.enable_gcm_crypto_suites) {
- // TODO(jbauch): Re-enable once https://crbug.com/628400 is resolved.
- crypto_options_.enable_gcm_crypto_suites = false;
- LOG(LS_WARNING) << "GCM ciphers are not supported with " <<
- "ENABLE_EXTERNAL_AUTH and will be disabled.";
- }
-#endif
return true;
}
diff --git a/webrtc/pc/srtpfilter.cc b/webrtc/pc/srtpfilter.cc
index e7622f7..5094987 100644
--- a/webrtc/pc/srtpfilter.cc
+++ b/webrtc/pc/srtpfilter.cc
@@ -21,6 +21,7 @@
#include "webrtc/base/byteorder.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
+#include "webrtc/base/sslstreamadapter.h"
#include "webrtc/base/stringencode.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/media/base/rtputils.h"
@@ -225,6 +226,18 @@
return true;
}
+#if defined(ENABLE_EXTERNAL_AUTH)
+bool SrtpFilter::IsExternalAuthActive() const {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to check IsExternalAuthActive: SRTP not active";
+ return false;
+ }
+
+ RTC_CHECK(send_session_);
+ return send_session_->IsExternalAuthActive();
+}
+#endif
+
void SrtpFilter::set_signal_silent_time(int signal_silent_time_in_ms) {
signal_silent_time_in_ms_ = signal_silent_time_in_ms;
if (IsActive()) {
@@ -462,12 +475,7 @@
// This lock protects SrtpSession::inited_.
rtc::GlobalLockPod SrtpSession::lock_;
-SrtpSession::SrtpSession()
- : session_(nullptr),
- rtp_auth_tag_len_(0),
- rtcp_auth_tag_len_(0),
- srtp_stat_(new SrtpStat()),
- last_send_seq_num_(-1) {
+SrtpSession::SrtpSession() : srtp_stat_(new SrtpStat()) {
SignalSrtpError.repeat(srtp_stat_->SignalSrtpError);
}
@@ -593,6 +601,11 @@
bool SrtpSession::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) {
#if defined(ENABLE_EXTERNAL_AUTH)
RTC_DCHECK(thread_checker_.CalledOnValidThread());
+ RTC_DCHECK(IsExternalAuthActive());
+ if (!IsExternalAuthActive()) {
+ return false;
+ }
+
ExternalHmacContext* external_hmac = nullptr;
// stream_template will be the reference context for other streams.
// Let's use it for getting the keys.
@@ -620,6 +633,12 @@
return rtp_auth_tag_len_;
}
+#if defined(ENABLE_EXTERNAL_AUTH)
+bool SrtpSession::IsExternalAuthActive() const {
+ return external_auth_active_;
+}
+#endif
+
bool SrtpSession::GetSendStreamPacketIndex(void* p,
int in_len,
int64_t* index) {
@@ -662,15 +681,12 @@
// RTP HMAC is shortened to 32 bits, but RTCP remains 80 bits.
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
-#if !defined(ENABLE_EXTERNAL_AUTH)
- // TODO(jbauch): Re-enable once https://crbug.com/628400 is resolved.
} else if (cs == rtc::SRTP_AEAD_AES_128_GCM) {
srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
} else if (cs == rtc::SRTP_AEAD_AES_256_GCM) {
srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
-#endif // ENABLE_EXTERNAL_AUTH
} else {
LOG(LS_WARNING) << "Failed to create SRTP session: unsupported"
<< " cipher_suite " << cs;
@@ -704,8 +720,9 @@
// We want to set this option only for rtp packets.
// By default policy structure is initialized to HMAC_SHA1.
#if defined(ENABLE_EXTERNAL_AUTH)
- // Enable external HMAC authentication only for outgoing streams.
- if (type == ssrc_any_outbound) {
+ // Enable external HMAC authentication only for outgoing streams and only
+ // for cipher suites that support it (i.e. only non-GCM cipher suites).
+ if (type == ssrc_any_outbound && !rtc::IsGcmCryptoSuite(cs)) {
policy.rtp.auth_type = EXTERNAL_HMAC_SHA1;
}
#endif
@@ -721,6 +738,9 @@
srtp_set_user_data(session_, this);
rtp_auth_tag_len_ = policy.rtp.auth_tag_len;
rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len;
+#if defined(ENABLE_EXTERNAL_AUTH)
+ external_auth_active_ = (policy.rtp.auth_type == EXTERNAL_HMAC_SHA1);
+#endif
return true;
}
diff --git a/webrtc/pc/srtpfilter.h b/webrtc/pc/srtpfilter.h
index 06edddf..46b9dea 100644
--- a/webrtc/pc/srtpfilter.h
+++ b/webrtc/pc/srtpfilter.h
@@ -114,6 +114,13 @@
// Returns srtp overhead for rtp packets.
bool GetSrtpOverhead(int* srtp_overhead) const;
+#if defined(ENABLE_EXTERNAL_AUTH)
+ // A SRTP filter supports external creation of the auth tag if a non-GCM
+ // cipher is used. This method is only valid after the RTP params have
+ // been set.
+ bool IsExternalAuthActive() const;
+#endif
+
// Update the silent threshold (in ms) for signaling errors.
void set_signal_silent_time(int signal_silent_time_in_ms);
@@ -206,6 +213,13 @@
int GetSrtpOverhead() const;
+#if defined(ENABLE_EXTERNAL_AUTH)
+ // A SRTP session supports external creation of the auth tag if a non-GCM
+ // cipher is used. This method is only valid after the RTP params have
+ // been set.
+ bool IsExternalAuthActive() const;
+#endif
+
// Update the silent threshold (in ms) for signaling errors.
void set_signal_silent_time(int signal_silent_time_in_ms);
@@ -225,13 +239,16 @@
static void HandleEventThunk(srtp_event_data_t* ev);
rtc::ThreadChecker thread_checker_;
- srtp_ctx_t_* session_;
- int rtp_auth_tag_len_;
- int rtcp_auth_tag_len_;
+ srtp_ctx_t_* session_ = nullptr;
+ int rtp_auth_tag_len_ = 0;
+ int rtcp_auth_tag_len_ = 0;
std::unique_ptr<SrtpStat> srtp_stat_;
static bool inited_;
static rtc::GlobalLockPod lock_;
- int last_send_seq_num_;
+ int last_send_seq_num_ = -1;
+#if defined(ENABLE_EXTERNAL_AUTH)
+ bool external_auth_active_ = false;
+#endif
RTC_DISALLOW_COPY_AND_ASSIGN(SrtpSession);
};
diff --git a/webrtc/pc/srtpfilter_unittest.cc b/webrtc/pc/srtpfilter_unittest.cc
index 32cd20e..9486dd6 100644
--- a/webrtc/pc/srtpfilter_unittest.cc
+++ b/webrtc/pc/srtpfilter_unittest.cc
@@ -11,6 +11,7 @@
#include "webrtc/pc/srtpfilter.h"
#include "third_party/libsrtp/include/srtp.h"
+#include "webrtc/base/buffer.h"
#include "webrtc/base/byteorder.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/gunit.h"
@@ -30,6 +31,14 @@
static const uint8_t kTestKey1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234";
static const uint8_t kTestKey2[] = "4321ZYXWVUTSRQPONMLKJIHGFEDCBA";
static const int kTestKeyLen = 30;
+static const uint8_t kTestKeyGcm128_1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ12";
+static const uint8_t kTestKeyGcm128_2[] = "21ZYXWVUTSRQPONMLKJIHGFEDCBA";
+static const int kTestKeyGcm128Len = 28; // 128 bits key + 96 bits salt.
+static const uint8_t kTestKeyGcm256_1[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr";
+static const uint8_t kTestKeyGcm256_2[] =
+ "rqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";
+static const int kTestKeyGcm256Len = 44; // 256 bits key + 96 bits salt.
static const std::string kTestKeyParams1 =
"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz";
static const std::string kTestKeyParams2 =
@@ -60,10 +69,20 @@
1, "AEAD_AES_128_GCM", kTestKeyParamsGcm4, "");
static int rtp_auth_tag_len(const std::string& cs) {
- return (cs == CS_AES_CM_128_HMAC_SHA1_32) ? 4 : 10;
+ if (cs == CS_AES_CM_128_HMAC_SHA1_32) {
+ return 4;
+ } else if (cs == CS_AEAD_AES_128_GCM || cs == CS_AEAD_AES_256_GCM) {
+ return 16;
+ } else {
+ return 10;
+ }
}
static int rtcp_auth_tag_len(const std::string& cs) {
- return 10;
+ if (cs == CS_AEAD_AES_128_GCM || cs == CS_AEAD_AES_256_GCM) {
+ return 16;
+ } else {
+ return 10;
+ }
}
class SrtpFilterTest : public testing::Test {
@@ -89,9 +108,11 @@
EXPECT_TRUE(f2_.IsActive());
}
void TestProtectUnprotect(const std::string& cs1, const std::string& cs2) {
- char rtp_packet[sizeof(kPcmuFrame) + 10];
+ rtc::Buffer rtp_buffer(sizeof(kPcmuFrame) + rtp_auth_tag_len(cs1));
+ char* rtp_packet = rtp_buffer.data<char>();
char original_rtp_packet[sizeof(kPcmuFrame)];
- char rtcp_packet[sizeof(kRtcpReport) + 4 + 10];
+ rtc::Buffer rtcp_buffer(sizeof(kRtcpReport) + 4 + rtcp_auth_tag_len(cs2));
+ char* rtcp_packet = rtcp_buffer.data<char>();
int rtp_len = sizeof(kPcmuFrame), rtcp_len = sizeof(kRtcpReport), out_len;
memcpy(rtp_packet, kPcmuFrame, rtp_len);
// In order to be able to run this test function multiple times we can not
@@ -102,7 +123,8 @@
memcpy(rtcp_packet, kRtcpReport, rtcp_len);
EXPECT_TRUE(f1_.ProtectRtp(rtp_packet, rtp_len,
- sizeof(rtp_packet), &out_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));
EXPECT_TRUE(f2_.UnprotectRtp(rtp_packet, out_len, &out_len));
@@ -110,7 +132,8 @@
EXPECT_EQ(0, memcmp(rtp_packet, original_rtp_packet, rtp_len));
EXPECT_TRUE(f2_.ProtectRtp(rtp_packet, rtp_len,
- sizeof(rtp_packet), &out_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));
EXPECT_TRUE(f1_.UnprotectRtp(rtp_packet, out_len, &out_len));
@@ -118,7 +141,8 @@
EXPECT_EQ(0, memcmp(rtp_packet, original_rtp_packet, rtp_len));
EXPECT_TRUE(f1_.ProtectRtcp(rtcp_packet, rtcp_len,
- sizeof(rtcp_packet), &out_len));
+ static_cast<int>(rtcp_buffer.size()),
+ &out_len));
EXPECT_EQ(out_len, rtcp_len + 4 + rtcp_auth_tag_len(cs1)); // NOLINT
EXPECT_NE(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len));
EXPECT_TRUE(f2_.UnprotectRtcp(rtcp_packet, out_len, &out_len));
@@ -126,7 +150,8 @@
EXPECT_EQ(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len));
EXPECT_TRUE(f2_.ProtectRtcp(rtcp_packet, rtcp_len,
- sizeof(rtcp_packet), &out_len));
+ static_cast<int>(rtcp_buffer.size()),
+ &out_len));
EXPECT_EQ(out_len, rtcp_len + 4 + rtcp_auth_tag_len(cs2)); // NOLINT
EXPECT_NE(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len));
EXPECT_TRUE(f1_.UnprotectRtcp(rtcp_packet, out_len, &out_len));
@@ -522,7 +547,7 @@
EXPECT_FALSE(f2_.IsActive());
}
-// Test directly setting the params with AES_CM_128_HMAC_SHA1_80
+// Test directly setting the params with AES_CM_128_HMAC_SHA1_80.
TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_AES_CM_128_HMAC_SHA1_80) {
EXPECT_TRUE(f1_.SetRtpParams(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1,
kTestKeyLen, rtc::SRTP_AES128_CM_SHA1_80,
@@ -538,10 +563,14 @@
kTestKey1, kTestKeyLen));
EXPECT_TRUE(f1_.IsActive());
EXPECT_TRUE(f2_.IsActive());
+#if defined(ENABLE_EXTERNAL_AUTH)
+ EXPECT_TRUE(f1_.IsExternalAuthActive());
+ EXPECT_TRUE(f2_.IsExternalAuthActive());
+#endif
TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
}
-// Test directly setting the params with AES_CM_128_HMAC_SHA1_32
+// Test directly setting the params with AES_CM_128_HMAC_SHA1_32.
TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_AES_CM_128_HMAC_SHA1_32) {
EXPECT_TRUE(f1_.SetRtpParams(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1,
kTestKeyLen, rtc::SRTP_AES128_CM_SHA1_32,
@@ -557,10 +586,60 @@
kTestKey1, kTestKeyLen));
EXPECT_TRUE(f1_.IsActive());
EXPECT_TRUE(f2_.IsActive());
+#if defined(ENABLE_EXTERNAL_AUTH)
+ EXPECT_TRUE(f1_.IsExternalAuthActive());
+ EXPECT_TRUE(f2_.IsExternalAuthActive());
+#endif
TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_32, CS_AES_CM_128_HMAC_SHA1_32);
}
-// Test directly setting the params with bogus keys
+// Test directly setting the params with SRTP_AEAD_AES_128_GCM.
+TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_SRTP_AEAD_AES_128_GCM) {
+ EXPECT_TRUE(f1_.SetRtpParams(rtc::SRTP_AEAD_AES_128_GCM, kTestKeyGcm128_1,
+ kTestKeyGcm128Len, rtc::SRTP_AEAD_AES_128_GCM,
+ kTestKeyGcm128_2, kTestKeyGcm128Len));
+ EXPECT_TRUE(f2_.SetRtpParams(rtc::SRTP_AEAD_AES_128_GCM, kTestKeyGcm128_2,
+ kTestKeyGcm128Len, rtc::SRTP_AEAD_AES_128_GCM,
+ kTestKeyGcm128_1, kTestKeyGcm128Len));
+ EXPECT_TRUE(f1_.SetRtcpParams(rtc::SRTP_AEAD_AES_128_GCM, kTestKeyGcm128_1,
+ kTestKeyGcm128Len, rtc::SRTP_AEAD_AES_128_GCM,
+ kTestKeyGcm128_2, kTestKeyGcm128Len));
+ EXPECT_TRUE(f2_.SetRtcpParams(rtc::SRTP_AEAD_AES_128_GCM, kTestKeyGcm128_2,
+ kTestKeyGcm128Len, rtc::SRTP_AEAD_AES_128_GCM,
+ kTestKeyGcm128_1, kTestKeyGcm128Len));
+ EXPECT_TRUE(f1_.IsActive());
+ EXPECT_TRUE(f2_.IsActive());
+#if defined(ENABLE_EXTERNAL_AUTH)
+ EXPECT_FALSE(f1_.IsExternalAuthActive());
+ EXPECT_FALSE(f2_.IsExternalAuthActive());
+#endif
+ TestProtectUnprotect(CS_AEAD_AES_128_GCM, CS_AEAD_AES_128_GCM);
+}
+
+// Test directly setting the params with SRTP_AEAD_AES_256_GCM.
+TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_SRTP_AEAD_AES_256_GCM) {
+ EXPECT_TRUE(f1_.SetRtpParams(rtc::SRTP_AEAD_AES_256_GCM, kTestKeyGcm256_1,
+ kTestKeyGcm256Len, rtc::SRTP_AEAD_AES_256_GCM,
+ kTestKeyGcm256_2, kTestKeyGcm256Len));
+ EXPECT_TRUE(f2_.SetRtpParams(rtc::SRTP_AEAD_AES_256_GCM, kTestKeyGcm256_2,
+ kTestKeyGcm256Len, rtc::SRTP_AEAD_AES_256_GCM,
+ kTestKeyGcm256_1, kTestKeyGcm256Len));
+ EXPECT_TRUE(f1_.SetRtcpParams(rtc::SRTP_AEAD_AES_256_GCM, kTestKeyGcm256_1,
+ kTestKeyGcm256Len, rtc::SRTP_AEAD_AES_256_GCM,
+ kTestKeyGcm256_2, kTestKeyGcm256Len));
+ EXPECT_TRUE(f2_.SetRtcpParams(rtc::SRTP_AEAD_AES_256_GCM, kTestKeyGcm256_2,
+ kTestKeyGcm256Len, rtc::SRTP_AEAD_AES_256_GCM,
+ kTestKeyGcm256_1, kTestKeyGcm256Len));
+ EXPECT_TRUE(f1_.IsActive());
+ EXPECT_TRUE(f2_.IsActive());
+#if defined(ENABLE_EXTERNAL_AUTH)
+ EXPECT_FALSE(f1_.IsExternalAuthActive());
+ EXPECT_FALSE(f2_.IsExternalAuthActive());
+#endif
+ TestProtectUnprotect(CS_AEAD_AES_256_GCM, CS_AEAD_AES_256_GCM);
+}
+
+// Test directly setting the params with bogus keys.
TEST_F(SrtpFilterTest, TestSetParamsKeyTooShort) {
EXPECT_FALSE(f1_.SetRtpParams(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1,
kTestKeyLen - 1, rtc::SRTP_AES128_CM_SHA1_80,
@@ -578,6 +657,8 @@
EXPECT_TRUE(f1_.SetRtcpParams(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1,
kTestKeyLen, rtc::SRTP_AES128_CM_SHA1_32,
kTestKey2, kTestKeyLen));
+ // Non-GCM ciphers support external auth.
+ EXPECT_TRUE(f1_.IsExternalAuthActive());
uint8_t* auth_key = NULL;
int auth_key_len = 0, auth_tag_len = 0;
EXPECT_TRUE(f1_.GetRtpAuthParams(&auth_key, &auth_key_len, &auth_tag_len));