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