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));