Add support for GCM cipher suites from RFC 7714.

GCM cipher suites are optional (disabled by default) and can be enabled
through "PeerConnectionFactoryInterface::Options".

If compiled with Chromium (i.e. "ENABLE_EXTERNAL_AUTH" is defined), no
GCM ciphers can be used yet (see https://crbug.com/628400).

BUG=webrtc:5222, 628400

Review-Url: https://codereview.webrtc.org/1528843005
Cr-Commit-Position: refs/heads/master@{#13635}
diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc
index e217f80..4ccd6e8 100644
--- a/webrtc/api/peerconnection.cc
+++ b/webrtc/api/peerconnection.cc
@@ -1623,6 +1623,7 @@
   }
 
   session_options->rtcp_cname = rtcp_cname_;
+  session_options->crypto_options = factory_->options().crypto_options;
   return true;
 }
 
@@ -1650,6 +1651,7 @@
   if (session_->data_channel_type() == cricket::DCT_SCTP) {
     session_options->data_channel_type = cricket::DCT_SCTP;
   }
+  session_options->crypto_options = factory_->options().crypto_options;
 }
 
 bool PeerConnection::GetOptionsForAnswer(
diff --git a/webrtc/api/peerconnection_unittest.cc b/webrtc/api/peerconnection_unittest.cc
index 4b06b90..18d3606 100644
--- a/webrtc/api/peerconnection_unittest.cc
+++ b/webrtc/api/peerconnection_unittest.cc
@@ -100,6 +100,7 @@
 // SRTP cipher name negotiated by the tests. This must be updated if the
 // default changes.
 static const int kDefaultSrtpCryptoSuite = rtc::SRTP_AES128_CM_SHA1_32;
+static const int kDefaultSrtpCryptoSuiteGcm = rtc::SRTP_AEAD_AES_256_GCM;
 #endif
 
 static void RemoveLinesFromSdp(const std::string& line_start,
@@ -1364,6 +1365,28 @@
     return true;
   }
 
+  void TestGcmNegotiation(bool local_gcm_enabled, bool remote_gcm_enabled,
+      int expected_cipher_suite) {
+    PeerConnectionFactory::Options init_options;
+    init_options.crypto_options.enable_gcm_crypto_suites = local_gcm_enabled;
+    PeerConnectionFactory::Options recv_options;
+    recv_options.crypto_options.enable_gcm_crypto_suites = remote_gcm_enabled;
+    ASSERT_TRUE(
+        CreateTestClients(nullptr, &init_options, nullptr, &recv_options));
+    rtc::scoped_refptr<webrtc::FakeMetricsObserver>
+        init_observer =
+            new rtc::RefCountedObject<webrtc::FakeMetricsObserver>();
+    initializing_client()->pc()->RegisterUMAObserver(init_observer);
+    LocalP2PTest();
+
+    EXPECT_EQ_WAIT(rtc::SrtpCryptoSuiteToName(expected_cipher_suite),
+                   initializing_client()->GetSrtpCipherStats(),
+                   kMaxWaitMs);
+    EXPECT_EQ(1,
+              init_observer->GetEnumCounter(webrtc::kEnumCounterAudioSrtpCipher,
+                                            expected_cipher_suite));
+  }
+
  private:
   // |ss_| is used by |network_thread_| so it must be destroyed later.
   std::unique_ptr<rtc::PhysicalSocketServer> pss_;
@@ -1814,6 +1837,26 @@
                                           kDefaultSrtpCryptoSuite));
 }
 
+// Test that a non-GCM cipher is used if both sides only support non-GCM.
+TEST_F(P2PTestConductor, GetGcmNone) {
+  TestGcmNegotiation(false, false, kDefaultSrtpCryptoSuite);
+}
+
+// Test that a GCM cipher is used if both ends support it.
+TEST_F(P2PTestConductor, GetGcmBoth) {
+  TestGcmNegotiation(true, true, kDefaultSrtpCryptoSuiteGcm);
+}
+
+// Test that GCM isn't used if only the initiator supports it.
+TEST_F(P2PTestConductor, GetGcmInit) {
+  TestGcmNegotiation(true, false, kDefaultSrtpCryptoSuite);
+}
+
+// Test that GCM isn't used if only the receiver supports it.
+TEST_F(P2PTestConductor, GetGcmRecv) {
+  TestGcmNegotiation(false, true, kDefaultSrtpCryptoSuite);
+}
+
 // This test sets up a call between two parties with audio, video and an RTP
 // data channel.
 TEST_F(P2PTestConductor, LocalP2PTestRtpDataChannel) {
diff --git a/webrtc/api/peerconnectionfactory.cc b/webrtc/api/peerconnectionfactory.cc
index 26ca666..82cd5d4 100644
--- a/webrtc/api/peerconnectionfactory.cc
+++ b/webrtc/api/peerconnectionfactory.cc
@@ -164,6 +164,7 @@
       media_engine, worker_thread_, network_thread_));
 
   channel_manager_->SetVideoRtxEnabled(true);
+  channel_manager_->SetCryptoOptions(options_.crypto_options);
   if (!channel_manager_->Init()) {
     return false;
   }
@@ -171,6 +172,13 @@
   return true;
 }
 
+void PeerConnectionFactory::SetOptions(const Options& options) {
+  options_ = options;
+  if (channel_manager_) {
+    channel_manager_->SetCryptoOptions(options.crypto_options);
+  }
+}
+
 rtc::scoped_refptr<AudioSourceInterface>
 PeerConnectionFactory::CreateAudioSource(
     const MediaConstraintsInterface* constraints) {
diff --git a/webrtc/api/peerconnectionfactory.h b/webrtc/api/peerconnectionfactory.h
index c209fdb..377ad73 100644
--- a/webrtc/api/peerconnectionfactory.h
+++ b/webrtc/api/peerconnectionfactory.h
@@ -31,9 +31,7 @@
 
 class PeerConnectionFactory : public PeerConnectionFactoryInterface {
  public:
-  void SetOptions(const Options& options) override {
-    options_ = options;
-  }
+  void SetOptions(const Options& options) override;
 
   // Deprecated, use version without constraints.
   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h
index 39c4856..e0eb1a4 100644
--- a/webrtc/api/peerconnectioninterface.h
+++ b/webrtc/api/peerconnectioninterface.h
@@ -597,7 +597,8 @@
           disable_sctp_data_channels(false),
           disable_network_monitor(false),
           network_ignore_mask(rtc::kDefaultNetworkIgnoreMask),
-          ssl_max_version(rtc::SSL_PROTOCOL_DTLS_12) {}
+          ssl_max_version(rtc::SSL_PROTOCOL_DTLS_12),
+          crypto_options(rtc::CryptoOptions::NoGcm()) {}
     bool disable_encryption;
     bool disable_sctp_data_channels;
     bool disable_network_monitor;
@@ -611,6 +612,9 @@
     // supported by both ends will be used for the connection, i.e. if one
     // party supports DTLS 1.0 and the other DTLS 1.2, DTLS 1.0 will be used.
     rtc::SSLProtocolVersion ssl_max_version;
+
+    // Sets crypto related options, e.g. enabled cipher suites.
+    rtc::CryptoOptions crypto_options;
   };
 
   virtual void SetOptions(const Options& options) = 0;
diff --git a/webrtc/api/webrtcsession_unittest.cc b/webrtc/api/webrtcsession_unittest.cc
index b96236d..1aff50f 100644
--- a/webrtc/api/webrtcsession_unittest.cc
+++ b/webrtc/api/webrtcsession_unittest.cc
@@ -461,6 +461,14 @@
     Init();
   }
 
+  void InitWithGcm() {
+    rtc::CryptoOptions crypto_options;
+    crypto_options.enable_gcm_crypto_suites = true;
+    channel_manager_->SetCryptoOptions(crypto_options);
+    with_gcm_ = true;
+    Init();
+  }
+
   void SendAudioVideoStream1() {
     send_stream_1_ = true;
     send_stream_2_ = false;
@@ -551,6 +559,10 @@
     if (session_->data_channel_type() == cricket::DCT_SCTP && data_channel_) {
       session_options->data_channel_type = cricket::DCT_SCTP;
     }
+
+    if (with_gcm_) {
+      session_options->crypto_options.enable_gcm_crypto_suites = true;
+    }
   }
 
   void GetOptionsForAnswer(cricket::MediaSessionOptions* session_options) {
@@ -566,6 +578,10 @@
     if (session_->data_channel_type() == cricket::DCT_SCTP) {
       session_options->data_channel_type = cricket::DCT_SCTP;
     }
+
+    if (with_gcm_) {
+      session_options->crypto_options.enable_gcm_crypto_suites = true;
+    }
   }
 
   // Creates a local offer and applies it. Starts ICE.
@@ -628,7 +644,8 @@
             session_->video_channel() != NULL);
   }
 
-  void VerifyCryptoParams(const cricket::SessionDescription* sdp) {
+  void VerifyCryptoParams(const cricket::SessionDescription* sdp,
+      bool gcm_enabled = false) {
     ASSERT_TRUE(session_.get() != NULL);
     const cricket::ContentInfo* content = cricket::GetFirstAudioContent(sdp);
     ASSERT_TRUE(content != NULL);
@@ -636,12 +653,24 @@
         static_cast<const cricket::AudioContentDescription*>(
             content->description);
     ASSERT_TRUE(audio_content != NULL);
-    ASSERT_EQ(1U, audio_content->cryptos().size());
-    ASSERT_EQ(47U, audio_content->cryptos()[0].key_params.size());
-    ASSERT_EQ("AES_CM_128_HMAC_SHA1_80",
-              audio_content->cryptos()[0].cipher_suite);
-    EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf),
-              audio_content->protocol());
+    if (!gcm_enabled) {
+      ASSERT_EQ(1U, audio_content->cryptos().size());
+      ASSERT_EQ(47U, audio_content->cryptos()[0].key_params.size());
+      ASSERT_EQ("AES_CM_128_HMAC_SHA1_80",
+                audio_content->cryptos()[0].cipher_suite);
+      EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf),
+                audio_content->protocol());
+    } else {
+      // The offer contains 3 possible crypto suites, the answer 1.
+      EXPECT_LE(1U, audio_content->cryptos().size());
+      EXPECT_NE(2U, audio_content->cryptos().size());
+      EXPECT_GE(3U, audio_content->cryptos().size());
+      ASSERT_EQ(67U, audio_content->cryptos()[0].key_params.size());
+      ASSERT_EQ("AEAD_AES_256_GCM",
+                audio_content->cryptos()[0].cipher_suite);
+      EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf),
+                audio_content->protocol());
+    }
 
     content = cricket::GetFirstVideoContent(sdp);
     ASSERT_TRUE(content != NULL);
@@ -649,12 +678,24 @@
         static_cast<const cricket::VideoContentDescription*>(
             content->description);
     ASSERT_TRUE(video_content != NULL);
-    ASSERT_EQ(1U, video_content->cryptos().size());
-    ASSERT_EQ("AES_CM_128_HMAC_SHA1_80",
-              video_content->cryptos()[0].cipher_suite);
-    ASSERT_EQ(47U, video_content->cryptos()[0].key_params.size());
-    EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf),
-              video_content->protocol());
+    if (!gcm_enabled) {
+      ASSERT_EQ(1U, video_content->cryptos().size());
+      ASSERT_EQ("AES_CM_128_HMAC_SHA1_80",
+                video_content->cryptos()[0].cipher_suite);
+      ASSERT_EQ(47U, video_content->cryptos()[0].key_params.size());
+      EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf),
+                video_content->protocol());
+    } else {
+      // The offer contains 3 possible crypto suites, the answer 1.
+      EXPECT_LE(1U, video_content->cryptos().size());
+      EXPECT_NE(2U, video_content->cryptos().size());
+      EXPECT_GE(3U, video_content->cryptos().size());
+      ASSERT_EQ("AEAD_AES_256_GCM",
+                video_content->cryptos()[0].cipher_suite);
+      ASSERT_EQ(67U, video_content->cryptos()[0].key_params.size());
+      EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf),
+                video_content->protocol());
+    }
   }
 
   void VerifyNoCryptoParams(const cricket::SessionDescription* sdp, bool dtls) {
@@ -1470,6 +1511,7 @@
   std::string last_data_channel_label_;
   InternalDataChannelInit last_data_channel_config_;
   bool session_destroyed_ = false;
+  bool with_gcm_ = false;
 };
 
 TEST_P(WebRtcSessionTest, TestInitializeWithDtls) {
@@ -2770,6 +2812,16 @@
   VerifyCryptoParams(answer->description());
 }
 
+TEST_F(WebRtcSessionTest, VerifyCryptoParamsInSDPGcm) {
+  InitWithGcm();
+  SendAudioVideoStream1();
+  std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
+  VerifyCryptoParams(offer->description(), true);
+  SetRemoteDescriptionWithoutError(offer.release());
+  std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
+  VerifyCryptoParams(answer->description(), true);
+}
+
 TEST_F(WebRtcSessionTest, VerifyNoCryptoParamsInSDP) {
   options_.disable_encryption = true;
   Init();
@@ -3395,6 +3447,12 @@
   SetLocalDescriptionWithoutError(offer);
 }
 
+TEST_F(WebRtcSessionTest, SetSetupGcm) {
+  InitWithGcm();
+  SendAudioVideoStream1();
+  CreateAndSetRemoteOfferAndLocalAnswer();
+}
+
 TEST_F(WebRtcSessionTest, CanNotInsertDtmf) {
   TestCanInsertDtmf(false);
 }
diff --git a/webrtc/base/helpers.cc b/webrtc/base/helpers.cc
index 0a39ee9..b284cd7 100644
--- a/webrtc/base/helpers.cc
+++ b/webrtc/base/helpers.cc
@@ -245,6 +245,13 @@
                             static_cast<int>(table.size()), str);
 }
 
+bool CreateRandomData(size_t length, std::string* data) {
+  data->resize(length);
+  // std::string is guaranteed to use contiguous memory in c++11 so we can
+  // safely write directly to it.
+  return Rng().Generate(&data->at(0), length);
+}
+
 // Version 4 UUID is of the form:
 // xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
 // Where 'x' is a hex digit, and 'y' is 8, 9, a or b.
diff --git a/webrtc/base/helpers.h b/webrtc/base/helpers.h
index 0e79373..c75dba5 100644
--- a/webrtc/base/helpers.h
+++ b/webrtc/base/helpers.h
@@ -39,6 +39,10 @@
 bool CreateRandomString(size_t length, const std::string& table,
                         std::string* str);
 
+// Generates (cryptographically) random data of the given length.
+// Return false if the random number generator failed.
+bool CreateRandomData(size_t length, std::string* data);
+
 // Generates a (cryptographically) random UUID version 4 string.
 std::string CreateRandomUuid();
 
diff --git a/webrtc/base/helpers_unittest.cc b/webrtc/base/helpers_unittest.cc
index 83cc685..e4903f5 100644
--- a/webrtc/base/helpers_unittest.cc
+++ b/webrtc/base/helpers_unittest.cc
@@ -10,6 +10,7 @@
 
 #include <string>
 
+#include "webrtc/base/buffer.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/helpers.h"
 #include "webrtc/base/ssladapter.h"
@@ -43,6 +44,17 @@
   EXPECT_EQ(256U, random2.size());
 }
 
+TEST_F(RandomTest, TestCreateRandomData) {
+  static size_t kRandomDataLength = 32;
+  std::string random1;
+  std::string random2;
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random1));
+  EXPECT_EQ(kRandomDataLength, random1.size());
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random2));
+  EXPECT_EQ(kRandomDataLength, random2.size());
+  EXPECT_NE(0, memcmp(random1.data(), random2.data(), kRandomDataLength));
+}
+
 TEST_F(RandomTest, TestCreateRandomUuid) {
   std::string random = CreateRandomUuid();
   EXPECT_EQ(36U, random.size());
@@ -54,12 +66,24 @@
   EXPECT_EQ(2154761789U, CreateRandomId());
   EXPECT_EQ("h0ISP4S5SJKH/9EY", CreateRandomString(16));
   EXPECT_EQ("41706e92-cdd3-46d9-a22d-8ff1737ffb11", CreateRandomUuid());
+  static size_t kRandomDataLength = 32;
+  std::string random;
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random));
+  EXPECT_EQ(kRandomDataLength, random.size());
+  Buffer expected("\xbd\x52\x2a\x4b\x97\x93\x2f\x1c"
+      "\xc4\x72\xab\xa2\x88\x68\x3e\xcc"
+      "\xa3\x8d\xaf\x13\x3b\xbc\x83\xbb"
+      "\x16\xf1\xcf\x56\x0c\xf5\x4a\x8b", kRandomDataLength);
+  EXPECT_EQ(0, memcmp(expected.data(), random.data(), kRandomDataLength));
 
   // Reset and make sure we get the same output.
   SetRandomTestMode(true);
   EXPECT_EQ(2154761789U, CreateRandomId());
   EXPECT_EQ("h0ISP4S5SJKH/9EY", CreateRandomString(16));
   EXPECT_EQ("41706e92-cdd3-46d9-a22d-8ff1737ffb11", CreateRandomUuid());
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random));
+  EXPECT_EQ(kRandomDataLength, random.size());
+  EXPECT_EQ(0, memcmp(expected.data(), random.data(), kRandomDataLength));
 
   // Test different character sets.
   SetRandomTestMode(true);
diff --git a/webrtc/base/opensslstreamadapter.cc b/webrtc/base/opensslstreamadapter.cc
index e04eb04..89f628d 100644
--- a/webrtc/base/opensslstreamadapter.cc
+++ b/webrtc/base/opensslstreamadapter.cc
@@ -56,6 +56,8 @@
 static SrtpCipherMapEntry SrtpCipherMap[] = {
     {"SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80},
     {"SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32},
+    {"SRTP_AEAD_AES_128_GCM", SRTP_AEAD_AES_128_GCM},
+    {"SRTP_AEAD_AES_256_GCM", SRTP_AEAD_AES_256_GCM},
     {nullptr, 0}};
 #endif
 
diff --git a/webrtc/base/sslstreamadapter.cc b/webrtc/base/sslstreamadapter.cc
index 44158d4..c34fc90 100644
--- a/webrtc/base/sslstreamadapter.cc
+++ b/webrtc/base/sslstreamadapter.cc
@@ -25,13 +25,22 @@
 // webrtc:5043.
 const char CS_AES_CM_128_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80";
 const char CS_AES_CM_128_HMAC_SHA1_32[] = "AES_CM_128_HMAC_SHA1_32";
+const char CS_AEAD_AES_128_GCM[] = "AEAD_AES_128_GCM";
+const char CS_AEAD_AES_256_GCM[] = "AEAD_AES_256_GCM";
 
 std::string SrtpCryptoSuiteToName(int crypto_suite) {
-  if (crypto_suite == SRTP_AES128_CM_SHA1_32)
+  switch (crypto_suite) {
+  case SRTP_AES128_CM_SHA1_32:
     return CS_AES_CM_128_HMAC_SHA1_32;
-  if (crypto_suite == SRTP_AES128_CM_SHA1_80)
+  case SRTP_AES128_CM_SHA1_80:
     return CS_AES_CM_128_HMAC_SHA1_80;
-  return std::string();
+  case SRTP_AEAD_AES_128_GCM:
+    return CS_AEAD_AES_128_GCM;
+  case SRTP_AEAD_AES_256_GCM:
+    return CS_AEAD_AES_256_GCM;
+  default:
+    return std::string();
+  }
 }
 
 int SrtpCryptoSuiteFromName(const std::string& crypto_suite) {
@@ -39,9 +48,58 @@
     return SRTP_AES128_CM_SHA1_32;
   if (crypto_suite == CS_AES_CM_128_HMAC_SHA1_80)
     return SRTP_AES128_CM_SHA1_80;
+  if (crypto_suite == CS_AEAD_AES_128_GCM)
+    return SRTP_AEAD_AES_128_GCM;
+  if (crypto_suite == CS_AEAD_AES_256_GCM)
+    return SRTP_AEAD_AES_256_GCM;
   return SRTP_INVALID_CRYPTO_SUITE;
 }
 
+bool GetSrtpKeyAndSaltLengths(int crypto_suite, int *key_length,
+    int *salt_length) {
+  switch (crypto_suite) {
+  case SRTP_AES128_CM_SHA1_32:
+  case SRTP_AES128_CM_SHA1_80:
+    // SRTP_AES128_CM_HMAC_SHA1_32 and SRTP_AES128_CM_HMAC_SHA1_80 are defined
+    // in RFC 5764 to use a 128 bits key and 112 bits salt for the cipher.
+    *key_length = 16;
+    *salt_length = 14;
+    break;
+  case SRTP_AEAD_AES_128_GCM:
+    // SRTP_AEAD_AES_128_GCM is defined in RFC 7714 to use a 128 bits key and
+    // a 96 bits salt for the cipher.
+    *key_length = 16;
+    *salt_length = 12;
+    break;
+  case SRTP_AEAD_AES_256_GCM:
+    // SRTP_AEAD_AES_256_GCM is defined in RFC 7714 to use a 256 bits key and
+    // a 96 bits salt for the cipher.
+    *key_length = 32;
+    *salt_length = 12;
+    break;
+  default:
+    return false;
+  }
+  return true;
+}
+
+bool IsGcmCryptoSuite(int crypto_suite) {
+  return (crypto_suite == SRTP_AEAD_AES_256_GCM ||
+          crypto_suite == SRTP_AEAD_AES_128_GCM);
+}
+
+bool IsGcmCryptoSuiteName(const std::string& crypto_suite) {
+  return (crypto_suite == CS_AEAD_AES_256_GCM ||
+          crypto_suite == CS_AEAD_AES_128_GCM);
+}
+
+// static
+CryptoOptions CryptoOptions::NoGcm() {
+  CryptoOptions options;
+  options.enable_gcm_crypto_suites = false;
+  return options;
+}
+
 SSLStreamAdapter* SSLStreamAdapter::Create(StreamInterface* stream) {
 #if SSL_USE_OPENSSL
   return new OpenSSLStreamAdapter(stream);
diff --git a/webrtc/base/sslstreamadapter.h b/webrtc/base/sslstreamadapter.h
index ba60ce3..4dbe457 100644
--- a/webrtc/base/sslstreamadapter.h
+++ b/webrtc/base/sslstreamadapter.h
@@ -31,6 +31,12 @@
 #ifndef SRTP_AES128_CM_SHA1_32
 const int SRTP_AES128_CM_SHA1_32 = 0x0002;
 #endif
+#ifndef SRTP_AEAD_AES_128_GCM
+const int SRTP_AEAD_AES_128_GCM = 0x0007;
+#endif
+#ifndef SRTP_AEAD_AES_256_GCM
+const int SRTP_AEAD_AES_256_GCM = 0x0008;
+#endif
 
 // Cipher suite to use for SRTP. Typically a 80-bit HMAC will be used, except
 // in applications (voice) where the additional bandwidth may be significant.
@@ -39,6 +45,10 @@
 extern const char CS_AES_CM_128_HMAC_SHA1_80[];
 // 128-bit AES with 32-bit SHA-1 HMAC.
 extern const char CS_AES_CM_128_HMAC_SHA1_32[];
+// 128-bit AES GCM with 16 byte AEAD auth tag.
+extern const char CS_AEAD_AES_128_GCM[];
+// 256-bit AES GCM with 16 byte AEAD auth tag.
+extern const char CS_AEAD_AES_256_GCM[];
 
 // Given the DTLS-SRTP protection profile ID, as defined in
 // https://tools.ietf.org/html/rfc4568#section-6.2 , return the SRTP profile
@@ -48,6 +58,30 @@
 // The reverse of above conversion.
 int SrtpCryptoSuiteFromName(const std::string& crypto_suite);
 
+// Get key length and salt length for given crypto suite. Returns true for
+// valid suites, otherwise false.
+bool GetSrtpKeyAndSaltLengths(int crypto_suite, int *key_length,
+    int *salt_length);
+
+// Returns true if the given crypto suite id uses a GCM cipher.
+bool IsGcmCryptoSuite(int crypto_suite);
+
+// Returns true if the given crypto suite name uses a GCM cipher.
+bool IsGcmCryptoSuiteName(const std::string& crypto_suite);
+
+struct CryptoOptions {
+  CryptoOptions() {}
+
+  // Helper method to return an instance of the CryptoOptions with GCM crypto
+  // suites disabled. This method should be used instead of depending on current
+  // default values set by the constructor.
+  static CryptoOptions NoGcm();
+
+  // Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used
+  // if both sides enable it.
+  bool enable_gcm_crypto_suites = false;
+};
+
 // SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS.
 // After SSL has been started, the stream will only open on successful
 // SSL verification of certificates, and the communication is
diff --git a/webrtc/base/sslstreamadapter_unittest.cc b/webrtc/base/sslstreamadapter_unittest.cc
index dc62ac0..07ee855 100644
--- a/webrtc/base/sslstreamadapter_unittest.cc
+++ b/webrtc/base/sslstreamadapter_unittest.cc
@@ -947,7 +947,6 @@
   ASSERT_EQ(client_cipher, rtc::SRTP_AES128_CM_SHA1_32);
 };
 
-
 // Test DTLS-SRTP with a mismatch -- should not converge
 TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpHighLow) {
   MAYBE_SKIP_TEST(HaveDtlsSrtp);
@@ -984,6 +983,107 @@
   ASSERT_EQ(client_cipher, rtc::SRTP_AES128_CM_SHA1_80);
 };
 
+// Test DTLS-SRTP with all GCM-128 ciphers.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCM128) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  std::vector<int> gcm128;
+  gcm128.push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  SetDtlsSrtpCryptoSuites(gcm128, true);
+  SetDtlsSrtpCryptoSuites(gcm128, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AEAD_AES_128_GCM);
+};
+
+// Test DTLS-SRTP with all GCM-256 ciphers.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCM256) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  std::vector<int> gcm256;
+  gcm256.push_back(rtc::SRTP_AEAD_AES_256_GCM);
+  SetDtlsSrtpCryptoSuites(gcm256, true);
+  SetDtlsSrtpCryptoSuites(gcm256, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AEAD_AES_256_GCM);
+};
+
+// Test DTLS-SRTP with mixed GCM-128/-256 ciphers -- should not converge.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCMMismatch) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  std::vector<int> gcm128;
+  gcm128.push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  std::vector<int> gcm256;
+  gcm256.push_back(rtc::SRTP_AEAD_AES_256_GCM);
+  SetDtlsSrtpCryptoSuites(gcm128, true);
+  SetDtlsSrtpCryptoSuites(gcm256, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_FALSE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_FALSE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+};
+
+// Test DTLS-SRTP with both GCM-128/-256 ciphers -- should select GCM-256.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCMMixed) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  std::vector<int> gcmBoth;
+  gcmBoth.push_back(rtc::SRTP_AEAD_AES_256_GCM);
+  gcmBoth.push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  SetDtlsSrtpCryptoSuites(gcmBoth, true);
+  SetDtlsSrtpCryptoSuites(gcmBoth, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AEAD_AES_256_GCM);
+};
+
+// Test SRTP cipher suite lengths.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpKeyAndSaltLengths) {
+  int key_len;
+  int salt_len;
+
+  ASSERT_FALSE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_INVALID_CRYPTO_SUITE, &key_len, &salt_len));
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AES128_CM_SHA1_32, &key_len, &salt_len));
+  ASSERT_EQ(128/8, key_len);
+  ASSERT_EQ(112/8, salt_len);
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AES128_CM_SHA1_80, &key_len, &salt_len));
+  ASSERT_EQ(128/8, key_len);
+  ASSERT_EQ(112/8, salt_len);
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AEAD_AES_128_GCM, &key_len, &salt_len));
+  ASSERT_EQ(128/8, key_len);
+  ASSERT_EQ(96/8, salt_len);
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AEAD_AES_256_GCM, &key_len, &salt_len));
+  ASSERT_EQ(256/8, key_len);
+  ASSERT_EQ(96/8, salt_len);
+};
+
 // Test an exporter
 TEST_P(SSLStreamAdapterTestDTLS, TestDTLSExporter) {
   MAYBE_SKIP_TEST(HaveExporter);
diff --git a/webrtc/pc/channel.cc b/webrtc/pc/channel.cc
index cde1355..e464124 100644
--- a/webrtc/pc/channel.cc
+++ b/webrtc/pc/channel.cc
@@ -555,6 +555,11 @@
   return channel ? channel->SetOption(opt, value) : -1;
 }
 
+bool BaseChannel::SetCryptoOptions(const rtc::CryptoOptions& crypto_options) {
+  crypto_options_ = crypto_options;
+  return true;
+}
+
 void BaseChannel::OnWritableState(TransportChannel* channel) {
   RTC_DCHECK(channel == transport_channel_ ||
              channel == rtcp_transport_channel_);
@@ -964,7 +969,7 @@
   if (!rtcp) {
     GetSrtpCryptoSuites_n(&crypto_suites);
   } else {
-    GetDefaultSrtpCryptoSuites(&crypto_suites);
+    GetDefaultSrtpCryptoSuites(crypto_options(), &crypto_suites);
   }
   return tc->SetSrtpCryptoSuites(crypto_suites);
 }
@@ -996,9 +1001,16 @@
                << content_name() << " "
                << PacketType(rtcp_channel);
 
+  int key_len;
+  int salt_len;
+  if (!rtc::GetSrtpKeyAndSaltLengths(selected_crypto_suite, &key_len,
+      &salt_len)) {
+    LOG(LS_ERROR) << "Unknown DTLS-SRTP crypto suite" << selected_crypto_suite;
+    return false;
+  }
+
   // OK, we're now doing DTLS (RFC 5764)
-  std::vector<unsigned char> dtls_buffer(SRTP_MASTER_KEY_KEY_LEN * 2 +
-                                         SRTP_MASTER_KEY_SALT_LEN * 2);
+  std::vector<unsigned char> dtls_buffer(key_len * 2 + salt_len * 2);
 
   // RFC 5705 exporter using the RFC 5764 parameters
   if (!channel->ExportKeyingMaterial(
@@ -1011,22 +1023,16 @@
   }
 
   // Sync up the keys with the DTLS-SRTP interface
-  std::vector<unsigned char> client_write_key(SRTP_MASTER_KEY_KEY_LEN +
-    SRTP_MASTER_KEY_SALT_LEN);
-  std::vector<unsigned char> server_write_key(SRTP_MASTER_KEY_KEY_LEN +
-    SRTP_MASTER_KEY_SALT_LEN);
+  std::vector<unsigned char> client_write_key(key_len + salt_len);
+  std::vector<unsigned char> server_write_key(key_len + salt_len);
   size_t offset = 0;
-  memcpy(&client_write_key[0], &dtls_buffer[offset],
-    SRTP_MASTER_KEY_KEY_LEN);
-  offset += SRTP_MASTER_KEY_KEY_LEN;
-  memcpy(&server_write_key[0], &dtls_buffer[offset],
-    SRTP_MASTER_KEY_KEY_LEN);
-  offset += SRTP_MASTER_KEY_KEY_LEN;
-  memcpy(&client_write_key[SRTP_MASTER_KEY_KEY_LEN],
-    &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN);
-  offset += SRTP_MASTER_KEY_SALT_LEN;
-  memcpy(&server_write_key[SRTP_MASTER_KEY_KEY_LEN],
-    &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN);
+  memcpy(&client_write_key[0], &dtls_buffer[offset], key_len);
+  offset += key_len;
+  memcpy(&server_write_key[0], &dtls_buffer[offset], key_len);
+  offset += key_len;
+  memcpy(&client_write_key[key_len], &dtls_buffer[offset], salt_len);
+  offset += salt_len;
+  memcpy(&server_write_key[key_len], &dtls_buffer[offset], salt_len);
 
   std::vector<unsigned char> *send_key, *recv_key;
   rtc::SSLRole role;
@@ -1846,7 +1852,7 @@
 
 void VoiceChannel::GetSrtpCryptoSuites_n(
     std::vector<int>* crypto_suites) const {
-  GetSupportedAudioCryptoSuites(crypto_suites);
+  GetSupportedAudioCryptoSuites(crypto_options(), crypto_suites);
 }
 
 VideoChannel::VideoChannel(rtc::Thread* worker_thread,
@@ -2107,7 +2113,7 @@
 
 void VideoChannel::GetSrtpCryptoSuites_n(
     std::vector<int>* crypto_suites) const {
-  GetSupportedVideoCryptoSuites(crypto_suites);
+  GetSupportedVideoCryptoSuites(crypto_options(), crypto_suites);
 }
 
 DataChannel::DataChannel(rtc::Thread* worker_thread,
@@ -2420,7 +2426,7 @@
 }
 
 void DataChannel::GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const {
-  GetSupportedDataCryptoSuites(crypto_suites);
+  GetSupportedDataCryptoSuites(crypto_options(), crypto_suites);
 }
 
 bool DataChannel::ShouldSetupDtlsSrtp_n() const {
diff --git a/webrtc/pc/channel.h b/webrtc/pc/channel.h
index 37eee47..3c00ee3 100644
--- a/webrtc/pc/channel.h
+++ b/webrtc/pc/channel.h
@@ -170,6 +170,8 @@
 
   virtual cricket::MediaType media_type() = 0;
 
+  bool SetCryptoOptions(const rtc::CryptoOptions& crypto_options);
+
  protected:
   virtual MediaChannel* media_channel() const { return media_channel_; }
   // Sets the |transport_channel_| (and |rtcp_transport_channel_|, if |rtcp_| is
@@ -303,6 +305,10 @@
   // From MessageHandler
   void OnMessage(rtc::Message* pmsg) override;
 
+  const rtc::CryptoOptions& crypto_options() const {
+    return crypto_options_;
+  }
+
   // Handled in derived classes
   // Get the SRTP crypto suites to use for RTP media
   virtual void GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const = 0;
@@ -351,6 +357,7 @@
   bool has_received_packet_;
   bool dtls_keyed_;
   bool secure_required_;
+  rtc::CryptoOptions crypto_options_;
   int rtp_abs_sendtime_extn_id_;
 
   // MediaChannel related members that should be access from worker thread.
diff --git a/webrtc/pc/channel_unittest.cc b/webrtc/pc/channel_unittest.cc
index b36dcd1..7b30547 100644
--- a/webrtc/pc/channel_unittest.cc
+++ b/webrtc/pc/channel_unittest.cc
@@ -15,6 +15,7 @@
 #include "webrtc/base/fakeclock.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/logging.h"
+#include "webrtc/base/sslstreamadapter.h"
 #include "webrtc/media/base/fakemediaengine.h"
 #include "webrtc/media/base/fakertp.h"
 #include "webrtc/media/base/mediachannel.h"
@@ -94,7 +95,7 @@
 class ChannelTest : public testing::Test, public sigslot::has_slots<> {
  public:
   enum Flags { RTCP = 0x1, RTCP_MUX = 0x2, SECURE = 0x4, SSRC_MUX = 0x8,
-               DTLS = 0x10 };
+               DTLS = 0x10, GCM_CIPHER = 0x20 };
 
   ChannelTest(bool verify_playout,
               rtc::ArrayView<const uint8_t> rtp_data,
@@ -135,10 +136,10 @@
     media_channel2_ = ch2;
     channel1_.reset(
         CreateChannel(worker_thread, network_thread_, &media_engine_, ch1,
-                      transport_controller1_.get(), (flags1 & RTCP) != 0));
+                      transport_controller1_.get(), flags1));
     channel2_.reset(
         CreateChannel(worker_thread, network_thread_, &media_engine_, ch2,
-                      transport_controller2_.get(), (flags2 & RTCP) != 0));
+                      transport_controller2_.get(), flags2));
     channel1_->SignalMediaMonitor.connect(this,
                                           &ChannelTest<T>::OnMediaMonitor1);
     channel2_->SignalMediaMonitor.connect(this,
@@ -187,10 +188,14 @@
       cricket::MediaEngineInterface* engine,
       typename T::MediaChannel* ch,
       cricket::TransportController* transport_controller,
-      bool rtcp) {
+      int flags) {
     typename T::Channel* channel =
         new typename T::Channel(worker_thread, network_thread, engine, ch,
-                                transport_controller, cricket::CN_AUDIO, rtcp);
+                                transport_controller, cricket::CN_AUDIO,
+                                (flags & RTCP) != 0);
+    rtc::CryptoOptions crypto_options;
+    crypto_options.enable_gcm_crypto_suites = (flags & GCM_CIPHER) != 0;
+    channel->SetCryptoOptions(crypto_options);
     if (!channel->Init_w(nullptr)) {
       delete channel;
       channel = NULL;
@@ -369,6 +374,21 @@
   bool CheckNoRtcp2() {
     return media_channel2_->CheckNoRtcp();
   }
+  // Checks that the channel is using GCM iff GCM_CIPHER is set in flags.
+  // Returns true if so.
+  bool CheckGcmCipher(typename T::Channel* channel, int flags) {
+    int suite;
+    if (!channel->transport_channel()->GetSrtpCryptoSuite(&suite)) {
+      return false;
+    }
+
+    if (flags & GCM_CIPHER) {
+      return rtc::IsGcmCryptoSuite(suite);
+    } else {
+      return (suite != rtc::SRTP_INVALID_CRYPTO_SUITE &&
+          !rtc::IsGcmCryptoSuite(suite));
+    }
+  }
 
   void CreateContent(int flags,
                      const cricket::AudioCodec& audio_codec,
@@ -1289,8 +1309,8 @@
   // Test that we properly send SRTP with RTCP in both directions.
   // You can pass in DTLS and/or RTCP_MUX as flags.
   void SendSrtpToSrtp(int flags1_in = 0, int flags2_in = 0) {
-    ASSERT((flags1_in & ~(RTCP_MUX | DTLS)) == 0);
-    ASSERT((flags2_in & ~(RTCP_MUX | DTLS)) == 0);
+    ASSERT((flags1_in & ~(RTCP_MUX | DTLS | GCM_CIPHER)) == 0);
+    ASSERT((flags2_in & ~(RTCP_MUX | DTLS | GCM_CIPHER)) == 0);
 
     int flags1 = RTCP | SECURE | flags1_in;
     int flags2 = RTCP | SECURE | flags2_in;
@@ -1308,6 +1328,14 @@
     EXPECT_TRUE(channel2_->secure());
     EXPECT_EQ(dtls1 && dtls2, channel1_->secure_dtls());
     EXPECT_EQ(dtls1 && dtls2, channel2_->secure_dtls());
+    // We can only query the negotiated cipher suite for DTLS-SRTP transport
+    // channels.
+    if (dtls1 && dtls2) {
+      // A GCM cipher is only used if both channels support GCM ciphers.
+      int common_gcm_flags = flags1 & flags2 & GCM_CIPHER;
+      EXPECT_TRUE(CheckGcmCipher(channel1_.get(), common_gcm_flags));
+      EXPECT_TRUE(CheckGcmCipher(channel2_.get(), common_gcm_flags));
+    }
     SendRtp1();
     SendRtp2();
     SendRtcp1();
@@ -2034,10 +2062,14 @@
     cricket::MediaEngineInterface* engine,
     cricket::FakeVideoMediaChannel* ch,
     cricket::TransportController* transport_controller,
-    bool rtcp) {
+    int flags) {
   cricket::VideoChannel* channel =
       new cricket::VideoChannel(worker_thread, network_thread, ch,
-                                transport_controller, cricket::CN_VIDEO, rtcp);
+                                transport_controller, cricket::CN_VIDEO,
+                                (flags & RTCP) != 0);
+  rtc::CryptoOptions crypto_options;
+  crypto_options.enable_gcm_crypto_suites = (flags & GCM_CIPHER) != 0;
+  channel->SetCryptoOptions(crypto_options);
   if (!channel->Init_w(nullptr)) {
     delete channel;
     channel = NULL;
@@ -2265,6 +2297,21 @@
   Base::SendSrtpToSrtp(DTLS, DTLS);
 }
 
+TEST_F(VoiceChannelSingleThreadTest, SendDtlsSrtpToDtlsSrtpGcmBoth) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  Base::SendSrtpToSrtp(DTLS | GCM_CIPHER, DTLS | GCM_CIPHER);
+}
+
+TEST_F(VoiceChannelSingleThreadTest, SendDtlsSrtpToDtlsSrtpGcmOne) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  Base::SendSrtpToSrtp(DTLS | GCM_CIPHER, DTLS);
+}
+
+TEST_F(VoiceChannelSingleThreadTest, SendDtlsSrtpToDtlsSrtpGcmTwo) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  Base::SendSrtpToSrtp(DTLS, DTLS | GCM_CIPHER);
+}
+
 TEST_F(VoiceChannelSingleThreadTest, SendDtlsSrtpToDtlsSrtpRtcpMux) {
   MAYBE_SKIP_TEST(HaveDtlsSrtp);
   Base::SendSrtpToSrtp(DTLS | RTCP_MUX, DTLS | RTCP_MUX);
@@ -2595,6 +2642,21 @@
   Base::SendSrtpToSrtp(DTLS, DTLS);
 }
 
+TEST_F(VoiceChannelDoubleThreadTest, SendDtlsSrtpToDtlsSrtpGcmBoth) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  Base::SendSrtpToSrtp(DTLS | GCM_CIPHER, DTLS | GCM_CIPHER);
+}
+
+TEST_F(VoiceChannelDoubleThreadTest, SendDtlsSrtpToDtlsSrtpGcmOne) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  Base::SendSrtpToSrtp(DTLS | GCM_CIPHER, DTLS);
+}
+
+TEST_F(VoiceChannelDoubleThreadTest, SendDtlsSrtpToDtlsSrtpGcmTwo) {
+  MAYBE_SKIP_TEST(HaveDtlsSrtp);
+  Base::SendSrtpToSrtp(DTLS, DTLS | GCM_CIPHER);
+}
+
 TEST_F(VoiceChannelDoubleThreadTest, SendDtlsSrtpToDtlsSrtpRtcpMux) {
   MAYBE_SKIP_TEST(HaveDtlsSrtp);
   Base::SendSrtpToSrtp(DTLS | RTCP_MUX, DTLS | RTCP_MUX);
@@ -3274,10 +3336,14 @@
     cricket::MediaEngineInterface* engine,
     cricket::FakeDataMediaChannel* ch,
     cricket::TransportController* transport_controller,
-    bool rtcp) {
+    int flags) {
   cricket::DataChannel* channel =
       new cricket::DataChannel(worker_thread, network_thread, ch,
-                               transport_controller, cricket::CN_DATA, rtcp);
+                               transport_controller, cricket::CN_DATA,
+                               (flags & RTCP) != 0);
+  rtc::CryptoOptions crypto_options;
+  crypto_options.enable_gcm_crypto_suites = (flags & GCM_CIPHER) != 0;
+  channel->SetCryptoOptions(crypto_options);
   if (!channel->Init_w(nullptr)) {
     delete channel;
     channel = NULL;
diff --git a/webrtc/pc/channelmanager.cc b/webrtc/pc/channelmanager.cc
index c2ce1cc..06475e9 100644
--- a/webrtc/pc/channelmanager.cc
+++ b/webrtc/pc/channelmanager.cc
@@ -64,6 +64,7 @@
   network_thread_ = network_thread;
   capturing_ = false;
   enable_rtx_ = false;
+  crypto_options_ = rtc::CryptoOptions::NoGcm();
 }
 
 ChannelManager::~ChannelManager() {
@@ -97,6 +98,30 @@
   }
 }
 
+bool ChannelManager::SetCryptoOptions(
+    const rtc::CryptoOptions& crypto_options) {
+  return worker_thread_->Invoke<bool>(RTC_FROM_HERE, Bind(
+      &ChannelManager::SetCryptoOptions_w, this, crypto_options));
+}
+
+bool ChannelManager::SetCryptoOptions_w(
+    const rtc::CryptoOptions& crypto_options) {
+  if (!video_channels_.empty() || !voice_channels_.empty() ||
+      !data_channels_.empty()) {
+    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;
+}
+
 void ChannelManager::GetSupportedAudioSendCodecs(
     std::vector<AudioCodec>* codecs) const {
   *codecs = media_engine_->audio_send_codecs();
@@ -218,6 +243,7 @@
   VoiceChannel* voice_channel =
       new VoiceChannel(worker_thread_, network_thread_, media_engine_.get(),
                        media_channel, transport_controller, content_name, rtcp);
+  voice_channel->SetCryptoOptions(crypto_options_);
   if (!voice_channel->Init_w(bundle_transport_name)) {
     delete voice_channel;
     return nullptr;
@@ -281,6 +307,7 @@
   VideoChannel* video_channel =
       new VideoChannel(worker_thread_, network_thread_, media_channel,
                        transport_controller, content_name, rtcp);
+  video_channel->SetCryptoOptions(crypto_options_);
   if (!video_channel->Init_w(bundle_transport_name)) {
     delete video_channel;
     return NULL;
@@ -344,6 +371,7 @@
   DataChannel* data_channel =
       new DataChannel(worker_thread_, network_thread_, media_channel,
                       transport_controller, content_name, rtcp);
+  data_channel->SetCryptoOptions(crypto_options_);
   if (!data_channel->Init_w(bundle_transport_name)) {
     LOG(LS_WARNING) << "Failed to init data channel.";
     delete data_channel;
diff --git a/webrtc/pc/channelmanager.h b/webrtc/pc/channelmanager.h
index c6a67df..15a3752 100644
--- a/webrtc/pc/channelmanager.h
+++ b/webrtc/pc/channelmanager.h
@@ -125,6 +125,10 @@
   // engines will start offering an RTX codec. Must be called before Init().
   bool SetVideoRtxEnabled(bool enable);
 
+  // Define crypto options to set on newly created channels. Doesn't change
+  // options on already created channels.
+  bool SetCryptoOptions(const rtc::CryptoOptions& crypto_options);
+
   // Starts/stops the local microphone and enables polling of the input level.
   bool capturing() const { return capturing_; }
 
@@ -150,6 +154,7 @@
   bool InitMediaEngine_w();
   void DestructorDeletes_w();
   void Terminate_w();
+  bool SetCryptoOptions_w(const rtc::CryptoOptions& crypto_options);
   VoiceChannel* CreateVoiceChannel_w(
       webrtc::MediaControllerInterface* media_controller,
       TransportController* transport_controller,
@@ -185,6 +190,7 @@
   DataChannels data_channels_;
 
   bool enable_rtx_;
+  rtc::CryptoOptions crypto_options_;
 
   bool capturing_;
 };
diff --git a/webrtc/pc/mediasession.cc b/webrtc/pc/mediasession.cc
index 937a2c1..28da731 100644
--- a/webrtc/pc/mediasession.cc
+++ b/webrtc/pc/mediasession.cc
@@ -18,6 +18,7 @@
 #include <unordered_map>
 #include <utility>
 
+#include "webrtc/base/base64.h"
 #include "webrtc/base/helpers.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/base/stringutils.h"
@@ -36,11 +37,13 @@
 namespace {
 const char kInline[] = "inline:";
 
-void GetSupportedCryptoSuiteNames(void (*func)(std::vector<int>*),
+void GetSupportedCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
+                                      std::vector<int>*),
+                                  const rtc::CryptoOptions& crypto_options,
                                   std::vector<std::string>* names) {
 #ifdef HAVE_SRTP
   std::vector<int> crypto_suites;
-  func(&crypto_suites);
+  func(crypto_options, &crypto_suites);
   for (const auto crypto : crypto_suites) {
     names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
   }
@@ -107,12 +110,22 @@
 
 static bool CreateCryptoParams(int tag, const std::string& cipher,
                                CryptoParams *out) {
-  std::string key;
-  key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
-
-  if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
+  int key_len;
+  int salt_len;
+  if (!rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
     return false;
   }
+
+  int master_key_len = key_len + salt_len;
+  std::string master_key;
+  if (!rtc::CreateRandomData(master_key_len, &master_key)) {
+    return false;
+  }
+
+  RTC_CHECK_EQ(static_cast<size_t>(master_key_len), master_key.size());
+  std::string key = rtc::Base64::Encode(master_key);
+
   out->tag = tag;
   out->cipher_suite = cipher;
   out->key_params = kInline;
@@ -171,63 +184,80 @@
   return false;
 }
 
-// For audio, HMAC 32 is prefered because of the low overhead.
-void GetSupportedAudioCryptoSuites(std::vector<int>* crypto_suites) {
+// For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
+void GetSupportedAudioCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites) {
 #ifdef HAVE_SRTP
+  if (crypto_options.enable_gcm_crypto_suites) {
+    crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
+    crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  }
   crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
   crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
 #endif
 }
 
-void GetSupportedAudioCryptoSuiteNames(
+void GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names) {
   GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
-                               crypto_suite_names);
+                               crypto_options, crypto_suite_names);
 }
 
-void GetSupportedVideoCryptoSuites(std::vector<int>* crypto_suites) {
-  GetDefaultSrtpCryptoSuites(crypto_suites);
+void GetSupportedVideoCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites) {
+  GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
 }
 
-void GetSupportedVideoCryptoSuiteNames(
+void GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names) {
   GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
-                               crypto_suite_names);
+                               crypto_options, crypto_suite_names);
 }
 
-void GetSupportedDataCryptoSuites(std::vector<int>* crypto_suites) {
-  GetDefaultSrtpCryptoSuites(crypto_suites);
+void GetSupportedDataCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites) {
+  GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
 }
 
-void GetSupportedDataCryptoSuiteNames(
+void GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names) {
   GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
-                               crypto_suite_names);
+                               crypto_options, crypto_suite_names);
 }
 
-void GetDefaultSrtpCryptoSuites(std::vector<int>* crypto_suites) {
+void GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites) {
 #ifdef HAVE_SRTP
+  if (crypto_options.enable_gcm_crypto_suites) {
+    crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
+    crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  }
   crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
 #endif
 }
 
-void GetDefaultSrtpCryptoSuiteNames(
+void GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names) {
-  GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites, crypto_suite_names);
+  GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites,
+                               crypto_options, crypto_suite_names);
 }
 
-// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
-// tolerated unless bundle is enabled because it is low overhead. Pick the
-// crypto in the list that is supported.
+// Support any GCM cipher (if enabled through options). For video support only
+// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
+// because it is low overhead.
+// Pick the crypto in the list that is supported.
 static bool SelectCrypto(const MediaContentDescription* offer,
                          bool bundle,
+                         const rtc::CryptoOptions& crypto_options,
                          CryptoParams *crypto) {
   bool audio = offer->type() == MEDIA_TYPE_AUDIO;
   const CryptoParamsVec& cryptos = offer->cryptos();
 
   for (CryptoParamsVec::const_iterator i = cryptos.begin();
        i != cryptos.end(); ++i) {
-    if (rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
+    if ((crypto_options.enable_gcm_crypto_suites &&
+         rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
+        rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
         (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
          !bundle)) {
       return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
@@ -1034,7 +1064,7 @@
 
   if (sdes_policy != SEC_DISABLED) {
     CryptoParams crypto;
-    if (SelectCrypto(offer, bundle_enabled, &crypto)) {
+    if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) {
       if (current_cryptos) {
         FindMatchingCrypto(*current_cryptos, crypto, &crypto);
       }
@@ -1672,7 +1702,7 @@
 
   std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
   std::vector<std::string> crypto_suites;
-  GetSupportedAudioCryptoSuiteNames(&crypto_suites);
+  GetSupportedAudioCryptoSuiteNames(options.crypto_options, &crypto_suites);
   if (!CreateMediaContentOffer(
           options,
           audio_codecs,
@@ -1722,7 +1752,7 @@
 
   std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
   std::vector<std::string> crypto_suites;
-  GetSupportedVideoCryptoSuiteNames(&crypto_suites);
+  GetSupportedVideoCryptoSuiteNames(options.crypto_options, &crypto_suites);
   if (!CreateMediaContentOffer(
           options,
           video_codecs,
@@ -1798,7 +1828,7 @@
     data->set_protocol(
         secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
   } else {
-    GetSupportedDataCryptoSuiteNames(&crypto_suites);
+    GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
   }
 
   if (!CreateMediaContentOffer(
diff --git a/webrtc/pc/mediasession.h b/webrtc/pc/mediasession.h
index 34354dc..b39a8e5 100644
--- a/webrtc/pc/mediasession.h
+++ b/webrtc/pc/mediasession.h
@@ -163,6 +163,7 @@
   // content name ("mid") => options.
   std::map<std::string, TransportOptions> transport_options;
   std::string rtcp_cname;
+  rtc::CryptoOptions crypto_options;
 
   struct Stream {
     Stream(MediaType type,
@@ -594,17 +595,21 @@
 DataContentDescription* GetFirstDataContentDescription(
     SessionDescription* sdesc);
 
-void GetSupportedAudioCryptoSuites(std::vector<int>* crypto_suites);
-void GetSupportedVideoCryptoSuites(std::vector<int>* crypto_suites);
-void GetSupportedDataCryptoSuites(std::vector<int>* crypto_suites);
-void GetDefaultSrtpCryptoSuites(std::vector<int>* crypto_suites);
-void GetSupportedAudioCryptoSuiteNames(
+void GetSupportedAudioCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites);
+void GetSupportedVideoCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites);
+void GetSupportedDataCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites);
+void GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions& crypto_options,
+    std::vector<int>* crypto_suites);
+void GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names);
-void GetSupportedVideoCryptoSuiteNames(
+void GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names);
-void GetSupportedDataCryptoSuiteNames(
+void GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names);
-void GetDefaultSrtpCryptoSuiteNames(
+void GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names);
 
 }  // namespace cricket
diff --git a/webrtc/pc/mediasession_unittest.cc b/webrtc/pc/mediasession_unittest.cc
index 8ad6526..281d306 100644
--- a/webrtc/pc/mediasession_unittest.cc
+++ b/webrtc/pc/mediasession_unittest.cc
@@ -73,6 +73,8 @@
 using cricket::SEC_REQUIRED;
 using rtc::CS_AES_CM_128_HMAC_SHA1_32;
 using rtc::CS_AES_CM_128_HMAC_SHA1_80;
+using rtc::CS_AEAD_AES_128_GCM;
+using rtc::CS_AEAD_AES_256_GCM;
 using webrtc::RtpExtension;
 
 static const AudioCodec kAudioCodecs1[] = {
@@ -453,6 +455,52 @@
     return true;
   }
 
+  void TestVideoGcmCipher(bool gcm_offer, bool gcm_answer) {
+    MediaSessionOptions offer_opts;
+    offer_opts.recv_video = true;
+    offer_opts.crypto_options.enable_gcm_crypto_suites = gcm_offer;
+    MediaSessionOptions answer_opts;
+    answer_opts.recv_video = true;
+    answer_opts.crypto_options.enable_gcm_crypto_suites = gcm_answer;
+    f1_.set_secure(SEC_ENABLED);
+    f2_.set_secure(SEC_ENABLED);
+    std::unique_ptr<SessionDescription> offer(
+        f1_.CreateOffer(offer_opts, NULL));
+    ASSERT_TRUE(offer.get() != NULL);
+    std::unique_ptr<SessionDescription> answer(
+        f2_.CreateAnswer(offer.get(), answer_opts, NULL));
+    const ContentInfo* ac = answer->GetContentByName("audio");
+    const ContentInfo* vc = answer->GetContentByName("video");
+    ASSERT_TRUE(ac != NULL);
+    ASSERT_TRUE(vc != NULL);
+    EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type);
+    EXPECT_EQ(std::string(NS_JINGLE_RTP), vc->type);
+    const AudioContentDescription* acd =
+        static_cast<const AudioContentDescription*>(ac->description);
+    const VideoContentDescription* vcd =
+        static_cast<const VideoContentDescription*>(vc->description);
+    EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
+    EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
+    EXPECT_EQ(kAutoBandwidth, acd->bandwidth());  // negotiated auto bw
+    EXPECT_NE(0U, acd->first_ssrc());             // a random nonzero ssrc
+    EXPECT_TRUE(acd->rtcp_mux());                 // negotiated rtcp-mux
+    if (gcm_offer && gcm_answer) {
+      ASSERT_CRYPTO(acd, 1U, CS_AEAD_AES_256_GCM);
+    } else {
+      ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32);
+    }
+    EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
+    EXPECT_EQ(MAKE_VECTOR(kVideoCodecsAnswer), vcd->codecs());
+    EXPECT_NE(0U, vcd->first_ssrc());             // a random nonzero ssrc
+    EXPECT_TRUE(vcd->rtcp_mux());                 // negotiated rtcp-mux
+    if (gcm_offer && gcm_answer) {
+      ASSERT_CRYPTO(vcd, 1U, CS_AEAD_AES_256_GCM);
+    } else {
+      ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80);
+    }
+    EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol());
+  }
+
  protected:
   MediaSessionDescriptionFactory f1_;
   MediaSessionDescriptionFactory f2_;
@@ -766,6 +814,34 @@
   EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol());
 }
 
+// Create a typical audio answer with GCM ciphers enabled, and ensure it
+// matches what we expect.
+TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerGcm) {
+  f1_.set_secure(SEC_ENABLED);
+  f2_.set_secure(SEC_ENABLED);
+  MediaSessionOptions options;
+  options.crypto_options.enable_gcm_crypto_suites = true;
+  std::unique_ptr<SessionDescription> offer(
+      f1_.CreateOffer(options, NULL));
+  ASSERT_TRUE(offer.get() != NULL);
+  std::unique_ptr<SessionDescription> answer(
+      f2_.CreateAnswer(offer.get(), options, NULL));
+  const ContentInfo* ac = answer->GetContentByName("audio");
+  const ContentInfo* vc = answer->GetContentByName("video");
+  ASSERT_TRUE(ac != NULL);
+  ASSERT_TRUE(vc == NULL);
+  EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type);
+  const AudioContentDescription* acd =
+      static_cast<const AudioContentDescription*>(ac->description);
+  EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
+  EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
+  EXPECT_NE(0U, acd->first_ssrc());             // a random nonzero ssrc
+  EXPECT_EQ(kAutoBandwidth, acd->bandwidth());  // negotiated auto bw
+  EXPECT_TRUE(acd->rtcp_mux());                 // negotiated rtcp-mux
+  ASSERT_CRYPTO(acd, 1U, CS_AEAD_AES_256_GCM);
+  EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol());
+}
+
 // Create a typical video answer, and ensure it matches what we expect.
 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
   MediaSessionOptions opts;
@@ -800,6 +876,24 @@
   EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol());
 }
 
+// Create a typical video answer with GCM ciphers enabled, and ensure it
+// matches what we expect.
+TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerGcm) {
+  TestVideoGcmCipher(true, true);
+}
+
+// Create a typical video answer with GCM ciphers enabled for the offer only,
+// and ensure it matches what we expect.
+TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerGcmOffer) {
+  TestVideoGcmCipher(true, false);
+}
+
+// Create a typical video answer with GCM ciphers enabled for the answer only,
+// and ensure it matches what we expect.
+TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerGcmAnswer) {
+  TestVideoGcmCipher(false, true);
+}
+
 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswer) {
   MediaSessionOptions opts;
   opts.data_channel_type = cricket::DCT_RTP;
@@ -833,6 +927,40 @@
   EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol());
 }
 
+TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerGcm) {
+  MediaSessionOptions opts;
+  opts.data_channel_type = cricket::DCT_RTP;
+  opts.crypto_options.enable_gcm_crypto_suites = true;
+  f1_.set_secure(SEC_ENABLED);
+  f2_.set_secure(SEC_ENABLED);
+  std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
+  ASSERT_TRUE(offer.get() != NULL);
+  std::unique_ptr<SessionDescription> answer(
+      f2_.CreateAnswer(offer.get(), opts, NULL));
+  const ContentInfo* ac = answer->GetContentByName("audio");
+  const ContentInfo* vc = answer->GetContentByName("data");
+  ASSERT_TRUE(ac != NULL);
+  ASSERT_TRUE(vc != NULL);
+  EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type);
+  EXPECT_EQ(std::string(NS_JINGLE_RTP), vc->type);
+  const AudioContentDescription* acd =
+      static_cast<const AudioContentDescription*>(ac->description);
+  const DataContentDescription* vcd =
+      static_cast<const DataContentDescription*>(vc->description);
+  EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
+  EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs());
+  EXPECT_EQ(kAutoBandwidth, acd->bandwidth());  // negotiated auto bw
+  EXPECT_NE(0U, acd->first_ssrc());             // a random nonzero ssrc
+  EXPECT_TRUE(acd->rtcp_mux());                 // negotiated rtcp-mux
+  ASSERT_CRYPTO(acd, 1U, CS_AEAD_AES_256_GCM);
+  EXPECT_EQ(MEDIA_TYPE_DATA, vcd->type());
+  EXPECT_EQ(MAKE_VECTOR(kDataCodecsAnswer), vcd->codecs());
+  EXPECT_NE(0U, vcd->first_ssrc());             // a random nonzero ssrc
+  EXPECT_TRUE(vcd->rtcp_mux());                 // negotiated rtcp-mux
+  ASSERT_CRYPTO(vcd, 1U, CS_AEAD_AES_256_GCM);
+  EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol());
+}
+
 // Verifies that the order of the media contents in the offer is preserved in
 // the answer.
 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAnswerContentOrder) {
diff --git a/webrtc/pc/srtpfilter.cc b/webrtc/pc/srtpfilter.cc
index 60dd4f1..9e7cc66 100644
--- a/webrtc/pc/srtpfilter.cc
+++ b/webrtc/pc/srtpfilter.cc
@@ -15,6 +15,7 @@
 #include <algorithm>
 
 #include "webrtc/base/base64.h"
+#include "webrtc/base/buffer.h"
 #include "webrtc/base/byteorder.h"
 #include "webrtc/base/checks.h"
 #include "webrtc/base/common.h"
@@ -48,17 +49,10 @@
 extern "C" debug_module_t mod_aes_icm;
 extern "C" debug_module_t mod_aes_hmac;
 #endif
-#else
-// SrtpFilter needs that constant.
-#define SRTP_MASTER_KEY_LEN 30
 #endif  // HAVE_SRTP
 
 namespace cricket {
 
-const int SRTP_MASTER_KEY_BASE64_LEN = SRTP_MASTER_KEY_LEN * 4 / 3;
-const int SRTP_MASTER_KEY_KEY_LEN = 16;
-const int SRTP_MASTER_KEY_SALT_LEN = 14;
-
 #ifndef HAVE_SRTP
 
 // This helper function is used on systems that don't (yet) have SRTP,
@@ -403,19 +397,45 @@
     // We do not want to reset the ROC if the keys are the same. So just return.
     return true;
   }
+
+  int send_suite = rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite);
+  int recv_suite = rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite);
+  if (send_suite == rtc::SRTP_INVALID_CRYPTO_SUITE ||
+      recv_suite == rtc::SRTP_INVALID_CRYPTO_SUITE) {
+    LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
+                    << " send cipher_suite " << send_params.cipher_suite
+                    << " recv cipher_suite " << recv_params.cipher_suite;
+    return false;
+  }
+
+  int send_key_len, send_salt_len;
+  int recv_key_len, recv_salt_len;
+  if (!rtc::GetSrtpKeyAndSaltLengths(send_suite, &send_key_len,
+                                     &send_salt_len) ||
+      !rtc::GetSrtpKeyAndSaltLengths(recv_suite, &recv_key_len,
+                                     &recv_salt_len)) {
+    LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
+                    << " send cipher_suite " << send_params.cipher_suite
+                    << " recv cipher_suite " << recv_params.cipher_suite;
+    return false;
+  }
+
   // TODO(juberti): Zero these buffers after use.
   bool ret;
-  uint8_t send_key[SRTP_MASTER_KEY_LEN], recv_key[SRTP_MASTER_KEY_LEN];
-  ret = (ParseKeyParams(send_params.key_params, send_key, sizeof(send_key)) &&
-         ParseKeyParams(recv_params.key_params, recv_key, sizeof(recv_key)));
+  rtc::Buffer send_key(send_key_len + send_salt_len);
+  rtc::Buffer recv_key(recv_key_len + recv_salt_len);
+  ret = (ParseKeyParams(send_params.key_params, send_key.data(),
+                        send_key.size()) &&
+         ParseKeyParams(recv_params.key_params, recv_key.data(),
+                        recv_key.size()));
   if (ret) {
     CreateSrtpSessions();
     ret = (send_session_->SetSend(
-               rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite), send_key,
-               sizeof(send_key)) &&
+               rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite),
+               send_key.data(), send_key.size()) &&
            recv_session_->SetRecv(
-               rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite), recv_key,
-               sizeof(recv_key)));
+               rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite),
+               recv_key.data(), recv_key.size()));
   }
   if (ret) {
     LOG(LS_INFO) << "SRTP activated with negotiated parameters:"
@@ -442,7 +462,7 @@
 
 bool SrtpFilter::ParseKeyParams(const std::string& key_params,
                                 uint8_t* key,
-                                int len) {
+                                size_t len) {
   // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
 
   // Fail if key-method is wrong.
@@ -453,8 +473,7 @@
   // Fail if base64 decode fails, or the key is the wrong size.
   std::string key_b64(key_params.substr(7)), key_str;
   if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT,
-                           &key_str, nullptr) ||
-      static_cast<int>(key_str.size()) != len) {
+                           &key_str, nullptr) || key_str.size() != len) {
     return false;
   }
 
@@ -488,11 +507,11 @@
   }
 }
 
-bool SrtpSession::SetSend(int cs, const uint8_t* key, int len) {
+bool SrtpSession::SetSend(int cs, const uint8_t* key, size_t len) {
   return SetKey(ssrc_any_outbound, cs, key, len);
 }
 
-bool SrtpSession::SetRecv(int cs, const uint8_t* key, int len) {
+bool SrtpSession::SetRecv(int cs, const uint8_t* key, size_t len) {
   return SetKey(ssrc_any_inbound, cs, key, len);
 }
 
@@ -646,7 +665,7 @@
   srtp_stat_->set_signal_silent_time(signal_silent_time_in_ms);
 }
 
-bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, int len) {
+bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t len) {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
   if (session_) {
     LOG(LS_ERROR) << "Failed to create SRTP session: "
@@ -660,20 +679,39 @@
 
   srtp_policy_t policy;
   memset(&policy, 0, sizeof(policy));
-
   if (cs == rtc::SRTP_AES128_CM_SHA1_80) {
     crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
     crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
   } else if (cs == rtc::SRTP_AES128_CM_SHA1_32) {
     crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);   // rtp is 32,
     crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);  // rtcp still 80
+#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) {
+    crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
+    crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
+  } else if (cs == rtc::SRTP_AEAD_AES_256_GCM) {
+    crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
+    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;
     return false;
   }
 
-  if (!key || len != SRTP_MASTER_KEY_LEN) {
+  int expected_key_len;
+  int expected_salt_len;
+  if (!rtc::GetSrtpKeyAndSaltLengths(cs, &expected_key_len,
+      &expected_salt_len)) {
+    // This should never happen.
+    LOG(LS_WARNING) << "Failed to create SRTP session: unsupported"
+                    << " cipher_suite without length information" << cs;
+    return false;
+  }
+
+  if (!key ||
+      len != static_cast<size_t>(expected_key_len + expected_salt_len)) {
     LOG(LS_WARNING) << "Failed to create SRTP session: invalid key";
     return false;
   }
diff --git a/webrtc/pc/srtpfilter.h b/webrtc/pc/srtpfilter.h
index cde9ad7..26a335f 100644
--- a/webrtc/pc/srtpfilter.h
+++ b/webrtc/pc/srtpfilter.h
@@ -33,13 +33,6 @@
 
 namespace cricket {
 
-// Key is 128 bits and salt is 112 bits == 30 bytes. B64 bloat => 40 bytes.
-extern const int SRTP_MASTER_KEY_BASE64_LEN;
-
-// Needed for DTLS-SRTP
-extern const int SRTP_MASTER_KEY_KEY_LEN;
-extern const int SRTP_MASTER_KEY_SALT_LEN;
-
 class SrtpSession;
 class SrtpStat;
 
@@ -140,7 +133,9 @@
                        CryptoParams* selected_params);
   bool ApplyParams(const CryptoParams& send_params,
                    const CryptoParams& recv_params);
-  static bool ParseKeyParams(const std::string& params, uint8_t* key, int len);
+  static bool ParseKeyParams(const std::string& params,
+                             uint8_t* key,
+                             size_t len);
 
  private:
   enum State {
@@ -185,10 +180,10 @@
 
   // Configures the session for sending data using the specified
   // cipher-suite and key. Receiving must be done by a separate session.
-  bool SetSend(int cs, const uint8_t* key, int len);
+  bool SetSend(int cs, const uint8_t* key, size_t len);
   // Configures the session for receiving data using the specified
   // cipher-suite and key. Sending must be done by a separate session.
-  bool SetRecv(int cs, const uint8_t* key, int len);
+  bool SetRecv(int cs, const uint8_t* key, size_t len);
 
   // Encrypts/signs an individual RTP/RTCP packet, in-place.
   // If an HMAC is used, this will increase the packet size.
@@ -218,7 +213,7 @@
       SignalSrtpError;
 
  private:
-  bool SetKey(int type, int cs, const uint8_t* key, int len);
+  bool SetKey(int type, int cs, const uint8_t* key, size_t len);
     // Returns send stream current packet index from srtp db.
   bool GetSendStreamPacketIndex(void* data, int in_len, int64_t* index);
 
diff --git a/webrtc/pc/srtpfilter_unittest.cc b/webrtc/pc/srtpfilter_unittest.cc
index cc5b3e5..cf80bdf 100644
--- a/webrtc/pc/srtpfilter_unittest.cc
+++ b/webrtc/pc/srtpfilter_unittest.cc
@@ -26,6 +26,8 @@
 
 using rtc::CS_AES_CM_128_HMAC_SHA1_80;
 using rtc::CS_AES_CM_128_HMAC_SHA1_32;
+using rtc::CS_AEAD_AES_128_GCM;
+using rtc::CS_AEAD_AES_256_GCM;
 using cricket::CryptoParams;
 using cricket::CS_LOCAL;
 using cricket::CS_REMOTE;
@@ -41,10 +43,26 @@
     "inline:1234X19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz";
 static const std::string kTestKeyParams4 =
     "inline:4567QCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR";
+static const std::string kTestKeyParamsGcm1 =
+    "inline:e166KFlKzJsGW0d5apX+rrI05vxbrvMJEzFI14aTDCa63IRTlLK4iH66uOI=";
+static const std::string kTestKeyParamsGcm2 =
+    "inline:6X0oCd55zfz4VgtOwsuqcFq61275PDYN5uwuu3p7ZUHbfUY2FMpdP4m2PEo=";
+static const std::string kTestKeyParamsGcm3 =
+    "inline:YKlABGZWMgX32xuMotrG0v0T7G83veegaVzubQ==";
+static const std::string kTestKeyParamsGcm4 =
+    "inline:gJ6tWoUym2v+/F6xjr7xaxiS3QbJJozl3ZD/0A==";
 static const cricket::CryptoParams kTestCryptoParams1(
     1, "AES_CM_128_HMAC_SHA1_80", kTestKeyParams1, "");
 static const cricket::CryptoParams kTestCryptoParams2(
     1, "AES_CM_128_HMAC_SHA1_80", kTestKeyParams2, "");
+static const cricket::CryptoParams kTestCryptoParamsGcm1(
+    1, "AEAD_AES_256_GCM", kTestKeyParamsGcm1, "");
+static const cricket::CryptoParams kTestCryptoParamsGcm2(
+    1, "AEAD_AES_256_GCM", kTestKeyParamsGcm2, "");
+static const cricket::CryptoParams kTestCryptoParamsGcm3(
+    1, "AEAD_AES_128_GCM", kTestKeyParamsGcm3, "");
+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;
@@ -133,6 +151,13 @@
   EXPECT_TRUE(f1_.IsActive());
 }
 
+TEST_F(SrtpFilterTest, TestGoodSetupOneCipherSuiteGcm) {
+  EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParamsGcm1), CS_LOCAL));
+  EXPECT_FALSE(f1_.IsActive());
+  EXPECT_TRUE(f1_.SetAnswer(MakeVector(kTestCryptoParamsGcm2), CS_REMOTE));
+  EXPECT_TRUE(f1_.IsActive());
+}
+
 // Test that we can set up things with multiple params.
 TEST_F(SrtpFilterTest, TestGoodSetupMultipleCipherSuites) {
   std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
@@ -148,6 +173,18 @@
   EXPECT_TRUE(f1_.IsActive());
 }
 
+TEST_F(SrtpFilterTest, TestGoodSetupMultipleCipherSuitesGcm) {
+  std::vector<CryptoParams> offer(MakeVector(kTestCryptoParamsGcm1));
+  std::vector<CryptoParams> answer(MakeVector(kTestCryptoParamsGcm3));
+  offer.push_back(kTestCryptoParamsGcm4);
+  offer[1].tag = 2;
+  answer[0].tag = 2;
+  EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
+  EXPECT_FALSE(f1_.IsActive());
+  EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
+  EXPECT_TRUE(f1_.IsActive());
+}
+
 // Test that we handle the cases where crypto is not desired.
 TEST_F(SrtpFilterTest, TestGoodSetupNoCipherSuites) {
   std::vector<CryptoParams> offer, answer;