diff --git a/webrtc/pc/BUILD.gn b/webrtc/pc/BUILD.gn index 0d86bc3f01..eed2ee9049 100644 --- a/webrtc/pc/BUILD.gn +++ b/webrtc/pc/BUILD.gn @@ -55,7 +55,7 @@ rtc_static_library("rtc_pc") { "../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 9ac9d201ab..68fdabb390 100644 --- a/webrtc/pc/channel.cc +++ b/webrtc/pc/channel.cc @@ -740,22 +740,27 @@ bool BaseChannel::SendPacket(bool rtcp, res = srtp_filter_.ProtectRtp( data, len, static_cast(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(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(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(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 150dfd9a9d..f2e0d719ff 100644 --- a/webrtc/pc/channelmanager.cc +++ b/webrtc/pc/channelmanager.cc @@ -101,14 +101,6 @@ bool ChannelManager::SetCryptoOptions_w( 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 e7622f7bc5..5094987a76 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 @@ bool SrtpFilter::GetSrtpOverhead(int* srtp_overhead) const { 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 @@ bool SrtpSession::inited_ = false; // 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::UnprotectRtcp(void* p, int in_len, int* out_len) { 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 @@ int SrtpSession::GetSrtpOverhead() const { 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 @@ bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t len) { // 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 @@ bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t len) { // 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 @@ bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t len) { 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 06edddf80d..46b9dea19f 100644 --- a/webrtc/pc/srtpfilter.h +++ b/webrtc/pc/srtpfilter.h @@ -114,6 +114,13 @@ class SrtpFilter { // 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 @@ class SrtpSession { 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 @@ class SrtpSession { 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 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 32cd20ed6a..9486dd6ac9 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 @@ using cricket::CS_REMOTE; 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 @@ static const cricket::CryptoParams kTestCryptoParamsGcm4( 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 @@ class SrtpFilterTest : public testing::Test { 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 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(); 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 @@ class SrtpFilterTest : public testing::Test { memcpy(rtcp_packet, kRtcpReport, rtcp_len); EXPECT_TRUE(f1_.ProtectRtp(rtp_packet, rtp_len, - sizeof(rtp_packet), &out_len)); + static_cast(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 @@ class SrtpFilterTest : public testing::Test { 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(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 @@ class SrtpFilterTest : public testing::Test { 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(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 @@ class SrtpFilterTest : public testing::Test { EXPECT_EQ(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len)); EXPECT_TRUE(f2_.ProtectRtcp(rtcp_packet, rtcp_len, - sizeof(rtcp_packet), &out_len)); + static_cast(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 @@ TEST_F(SrtpFilterTest, TestDisableEncryption) { 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 @@ TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_AES_CM_128_HMAC_SHA1_80) { 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 @@ TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_AES_CM_128_HMAC_SHA1_32) { 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 @@ TEST_F(SrtpFilterTest, TestGetSendAuthParams) { 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));