This CL adds an API to the SSL stream adapters and transport channels to get the SSL cipher that was negotiated with the remote peer.

BUG=3976
R=davidben@chromium.org, juberti@webrtc.org, pthatcher@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/26009004

Cr-Commit-Position: refs/heads/master@{#8275}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8275 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/AUTHORS b/AUTHORS
index 09e4f62..069c332 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -37,5 +37,6 @@
 MIPS Technologies
 Mozilla Foundation
 Opera Software ASA
+struktur AG
 Temasys Communications
 Vonage Holdings Corp.
diff --git a/webrtc/base/nssstreamadapter.cc b/webrtc/base/nssstreamadapter.cc
index 7152eda..1286bf9 100644
--- a/webrtc/base/nssstreamadapter.cc
+++ b/webrtc/base/nssstreamadapter.cc
@@ -66,6 +66,10 @@
 };
 #endif
 
+// Default cipher used between NSS stream adapters.
+// This needs to be updated when the default of the SSL library changes.
+static const char kDefaultSslCipher[] = "TLS_RSA_WITH_AES_128_CBC_SHA";
+
 
 // Implementation of NSPR methods
 static PRStatus StreamClose(PRFileDesc *socket) {
@@ -866,6 +870,27 @@
   return SECSuccess;
 }
 
+bool NSSStreamAdapter::GetSslCipher(std::string* cipher) {
+  ASSERT(state_ == SSL_CONNECTED);
+  if (state_ != SSL_CONNECTED)
+    return false;
+
+  SSLChannelInfo channel_info;
+  SECStatus rv = SSL_GetChannelInfo(ssl_fd_, &channel_info,
+                                    sizeof(channel_info));
+  if (rv == SECFailure)
+    return false;
+
+  SSLCipherSuiteInfo ciphersuite_info;
+  rv = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, &ciphersuite_info,
+                              sizeof(ciphersuite_info));
+  if (rv == SECFailure)
+    return false;
+
+  *cipher = ciphersuite_info.cipherSuiteName;
+  return true;
+}
+
 // RFC 5705 Key Exporter
 bool NSSStreamAdapter::ExportKeyingMaterial(const std::string& label,
                                             const uint8* context,
@@ -1011,6 +1036,10 @@
   return true;
 }
 
+std::string NSSStreamAdapter::GetDefaultSslCipher() {
+  return kDefaultSslCipher;
+}
+
 }  // namespace rtc
 
 #endif  // HAVE_NSS_SSL_H
diff --git a/webrtc/base/nssstreamadapter.h b/webrtc/base/nssstreamadapter.h
index 210a479..afa2eb6 100644
--- a/webrtc/base/nssstreamadapter.h
+++ b/webrtc/base/nssstreamadapter.h
@@ -61,6 +61,8 @@
                              size_t* written, int* error);
   void OnMessage(Message *msg);
 
+  virtual bool GetSslCipher(std::string* cipher);
+
   // Key Extractor interface
   virtual bool ExportKeyingMaterial(const std::string& label,
                                     const uint8* context,
@@ -77,6 +79,7 @@
   static bool HaveDtls();
   static bool HaveDtlsSrtp();
   static bool HaveExporter();
+  static std::string GetDefaultSslCipher();
 
  protected:
   // Override SSLStreamAdapter
diff --git a/webrtc/base/opensslstreamadapter.cc b/webrtc/base/opensslstreamadapter.cc
index c18295f..0f82d28 100644
--- a/webrtc/base/opensslstreamadapter.cc
+++ b/webrtc/base/opensslstreamadapter.cc
@@ -20,6 +20,7 @@
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
+#include <openssl/tls1.h>
 #include <openssl/x509v3.h>
 
 #include <vector>
@@ -56,6 +57,99 @@
 };
 #endif
 
+// Cipher name table. Maps internal OpenSSL cipher ids to the RFC name.
+struct SslCipherMapEntry {
+  uint32_t openssl_id;
+  const char* rfc_name;
+};
+
+#define DEFINE_CIPHER_ENTRY_SSL3(name)  {SSL3_CK_##name, "TLS_"#name}
+#define DEFINE_CIPHER_ENTRY_TLS1(name)  {TLS1_CK_##name, "TLS_"#name}
+
+// There currently is no method available to get a RFC-compliant name for a
+// cipher suite from BoringSSL, so we need to define the mapping manually here.
+// This should go away once BoringSSL supports "SSL_CIPHER_standard_name"
+// (as available in OpenSSL if compiled with tracing enabled) or a similar
+// method.
+static const SslCipherMapEntry kSslCipherMap[] = {
+  // TLS v1.0 ciphersuites from RFC2246.
+  DEFINE_CIPHER_ENTRY_SSL3(RSA_RC4_128_SHA),
+  {SSL3_CK_RSA_DES_192_CBC3_SHA,
+      "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
+
+  // AES ciphersuites from RFC3268.
+  {TLS1_CK_RSA_WITH_AES_128_SHA,
+      "TLS_RSA_WITH_AES_128_CBC_SHA"},
+  {TLS1_CK_DHE_RSA_WITH_AES_128_SHA,
+      "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"},
+  {TLS1_CK_RSA_WITH_AES_256_SHA,
+      "TLS_RSA_WITH_AES_256_CBC_SHA"},
+  {TLS1_CK_DHE_RSA_WITH_AES_256_SHA,
+      "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"},
+
+  // ECC ciphersuites from RFC4492.
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_RC4_128_SHA),
+  {TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA,
+      "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"},
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
+
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_RC4_128_SHA),
+  {TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA,
+      "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"},
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_CBC_SHA),
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_CBC_SHA),
+
+  // TLS v1.2 ciphersuites.
+  {TLS1_CK_RSA_WITH_AES_128_SHA256,
+      "TLS_RSA_WITH_AES_128_CBC_SHA256"},
+  {TLS1_CK_RSA_WITH_AES_256_SHA256,
+      "TLS_RSA_WITH_AES_256_CBC_SHA256"},
+  {TLS1_CK_DHE_RSA_WITH_AES_128_SHA256,
+      "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"},
+  {TLS1_CK_DHE_RSA_WITH_AES_256_SHA256,
+      "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"},
+
+  // TLS v1.2 GCM ciphersuites from RFC5288.
+  DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_128_GCM_SHA256),
+  DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_256_GCM_SHA384),
+  DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_128_GCM_SHA256),
+  DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_256_GCM_SHA384),
+  DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_128_GCM_SHA256),
+  DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_256_GCM_SHA384),
+
+  // ECDH HMAC based ciphersuites from RFC5289.
+  {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256,
+      "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"},
+  {TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384,
+      "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"},
+  {TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
+      "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"},
+  {TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
+      "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
+
+  // ECDH GCM based ciphersuites from RFC5289.
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_GCM_SHA256),
+  DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_GCM_SHA384),
+
+#ifdef OPENSSL_IS_BORINGSSL
+  {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305,
+      "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
+  {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305,
+      "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"},
+  {TLS1_CK_DHE_RSA_CHACHA20_POLY1305,
+      "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
+#endif
+
+  {0, NULL}
+};
+
+// Default cipher used between OpenSSL/BoringSSL stream adapters.
+// This needs to be updated when the default of the SSL library changes.
+static const char kDefaultSslCipher[] = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA";
+
 //////////////////////////////////////////////////////////////////////
 // StreamBIO
 //////////////////////////////////////////////////////////////////////
@@ -222,6 +316,36 @@
   return true;
 }
 
+const char* OpenSSLStreamAdapter::GetRfcSslCipherName(
+    const SSL_CIPHER* cipher) {
+  ASSERT(cipher != NULL);
+  for (const SslCipherMapEntry* entry = kSslCipherMap; entry->rfc_name;
+       ++entry) {
+    if (cipher->id == entry->openssl_id) {
+      return entry->rfc_name;
+    }
+  }
+  return NULL;
+}
+
+bool OpenSSLStreamAdapter::GetSslCipher(std::string* cipher) {
+  if (state_ != SSL_CONNECTED)
+    return false;
+
+  const SSL_CIPHER* current_cipher = SSL_get_current_cipher(ssl_);
+  if (current_cipher == NULL) {
+    return false;
+  }
+
+  const char* cipher_name = GetRfcSslCipherName(current_cipher);
+  if (cipher_name == NULL) {
+    return false;
+  }
+
+  *cipher = cipher_name;
+  return true;
+}
+
 // Key Extractor interface
 bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label,
                                                 const uint8* context,
@@ -877,6 +1001,10 @@
 #endif
 }
 
+std::string OpenSSLStreamAdapter::GetDefaultSslCipher() {
+  return kDefaultSslCipher;
+}
+
 }  // namespace rtc
 
 #endif  // HAVE_OPENSSL_SSL_H
diff --git a/webrtc/base/opensslstreamadapter.h b/webrtc/base/opensslstreamadapter.h
index 9506217..a9d98fd 100644
--- a/webrtc/base/opensslstreamadapter.h
+++ b/webrtc/base/opensslstreamadapter.h
@@ -20,6 +20,7 @@
 
 typedef struct ssl_st SSL;
 typedef struct ssl_ctx_st SSL_CTX;
+typedef struct ssl_cipher_st SSL_CIPHER;
 typedef struct x509_store_ctx_st X509_STORE_CTX;
 
 namespace rtc {
@@ -81,6 +82,11 @@
   virtual void Close();
   virtual StreamState GetState() const;
 
+  // Return the RFC (5246, 3268, etc.) cipher name for an OpenSSL cipher.
+  static const char* GetRfcSslCipherName(const SSL_CIPHER* cipher);
+
+  virtual bool GetSslCipher(std::string* cipher);
+
   // Key Extractor interface
   virtual bool ExportKeyingMaterial(const std::string& label,
                                     const uint8* context,
@@ -98,6 +104,7 @@
   static bool HaveDtls();
   static bool HaveDtlsSrtp();
   static bool HaveExporter();
+  static std::string GetDefaultSslCipher();
 
  protected:
   virtual void OnEvent(StreamInterface* stream, int events, int err);
diff --git a/webrtc/base/sslstreamadapter.cc b/webrtc/base/sslstreamadapter.cc
index 44df2ee..513ae8c 100644
--- a/webrtc/base/sslstreamadapter.cc
+++ b/webrtc/base/sslstreamadapter.cc
@@ -50,6 +50,9 @@
 bool SSLStreamAdapter::HaveDtls() { return false; }
 bool SSLStreamAdapter::HaveDtlsSrtp() { return false; }
 bool SSLStreamAdapter::HaveExporter() { return false; }
+std::string SSLStreamAdapter::GetDefaultSslCipher() {
+  return std::string();
+}
 #elif SSL_USE_OPENSSL
 bool SSLStreamAdapter::HaveDtls() {
   return OpenSSLStreamAdapter::HaveDtls();
@@ -60,6 +63,9 @@
 bool SSLStreamAdapter::HaveExporter() {
   return OpenSSLStreamAdapter::HaveExporter();
 }
+std::string SSLStreamAdapter::GetDefaultSslCipher() {
+  return OpenSSLStreamAdapter::GetDefaultSslCipher();
+}
 #elif SSL_USE_NSS
 bool SSLStreamAdapter::HaveDtls() {
   return NSSStreamAdapter::HaveDtls();
@@ -70,6 +76,9 @@
 bool SSLStreamAdapter::HaveExporter() {
   return NSSStreamAdapter::HaveExporter();
 }
+std::string SSLStreamAdapter::GetDefaultSslCipher() {
+  return NSSStreamAdapter::GetDefaultSslCipher();
+}
 #endif  // !SSL_USE_SCHANNEL && !SSL_USE_OPENSSL && !SSL_USE_NSS
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/webrtc/base/sslstreamadapter.h b/webrtc/base/sslstreamadapter.h
index ea966c5..c940ecb 100644
--- a/webrtc/base/sslstreamadapter.h
+++ b/webrtc/base/sslstreamadapter.h
@@ -119,6 +119,12 @@
   // chain. The returned certificate is owned by the caller.
   virtual bool GetPeerCertificate(SSLCertificate** cert) const = 0;
 
+  // Retrieves the name of the cipher suite used for the connection
+  // (e.g. "TLS_RSA_WITH_AES_128_CBC_SHA").
+  virtual bool GetSslCipher(std::string* cipher) {
+    return false;
+  }
+
   // Key Exporter interface from RFC 5705
   // Arguments are:
   // label               -- the exporter label.
@@ -155,6 +161,10 @@
   static bool HaveDtlsSrtp();
   static bool HaveExporter();
 
+  // Returns the default Ssl cipher used between streams of this class.
+  // This is used by the unit tests.
+  static std::string GetDefaultSslCipher();
+
  private:
   // If true, the server certificate need not match the configured
   // server_name, and in fact missing certificate authority and other
diff --git a/webrtc/base/sslstreamadapter_unittest.cc b/webrtc/base/sslstreamadapter_unittest.cc
index b9d477d..677be35 100644
--- a/webrtc/base/sslstreamadapter_unittest.cc
+++ b/webrtc/base/sslstreamadapter_unittest.cc
@@ -388,6 +388,13 @@
       return server_ssl_->GetPeerCertificate(cert);
   }
 
+  bool GetSslCipher(bool client, std::string *retval) {
+    if (client)
+      return client_ssl_->GetSslCipher(retval);
+    else
+      return server_ssl_->GetSslCipher(retval);
+  }
+
   bool ExportKeyingMaterial(const char *label,
                             const unsigned char *context,
                             size_t context_len,
@@ -939,3 +946,17 @@
   rtc::SSLCertChain* server_peer_chain;
   ASSERT_FALSE(server_peer_cert->GetChain(&server_peer_chain));
 }
+
+// Test getting the used DTLS ciphers.
+TEST_F(SSLStreamAdapterTestDTLS, TestGetSslCipher) {
+  MAYBE_SKIP_TEST(HaveDtls);
+  TestHandshake();
+
+  std::string client_cipher;
+  ASSERT_TRUE(GetSslCipher(true, &client_cipher));
+  std::string server_cipher;
+  ASSERT_TRUE(GetSslCipher(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(rtc::SSLStreamAdapter::GetDefaultSslCipher(), client_cipher);
+}
diff --git a/webrtc/p2p/base/dtlstransportchannel.cc b/webrtc/p2p/base/dtlstransportchannel.cc
index 9cceca3..956c52a 100644
--- a/webrtc/p2p/base/dtlstransportchannel.cc
+++ b/webrtc/p2p/base/dtlstransportchannel.cc
@@ -186,6 +186,14 @@
   return true;
 }
 
+bool DtlsTransportChannelWrapper::GetSslCipher(std::string* cipher) {
+  if (dtls_state_ != STATE_OPEN) {
+    return false;
+  }
+
+  return dtls_->GetSslCipher(cipher);
+}
+
 bool DtlsTransportChannelWrapper::SetRemoteFingerprint(
     const std::string& digest_alg,
     const uint8* digest,
diff --git a/webrtc/p2p/base/dtlstransportchannel.h b/webrtc/p2p/base/dtlstransportchannel.h
index 4c9c879..03a916b 100644
--- a/webrtc/p2p/base/dtlstransportchannel.h
+++ b/webrtc/p2p/base/dtlstransportchannel.h
@@ -150,6 +150,9 @@
   virtual bool GetSslRole(rtc::SSLRole* role) const;
   virtual bool SetSslRole(rtc::SSLRole role);
 
+  // Find out which DTLS cipher was negotiated
+  virtual bool GetSslCipher(std::string* cipher);
+
   // Once DTLS has been established, this method retrieves the certificate in
   // use by the remote peer, for use in external identity verification.
   virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const;
diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
index 52f8c1e..f3086bb 100644
--- a/webrtc/p2p/base/dtlstransportchannel_unittest.cc
+++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
@@ -213,6 +213,22 @@
     }
   }
 
+  void CheckSsl(const std::string& expected_cipher) {
+    for (std::vector<cricket::DtlsTransportChannelWrapper*>::iterator it =
+           channels_.begin(); it != channels_.end(); ++it) {
+      std::string cipher;
+
+      bool rv = (*it)->GetSslCipher(&cipher);
+      if (negotiated_dtls_ && !expected_cipher.empty()) {
+        ASSERT_TRUE(rv);
+
+        ASSERT_EQ(cipher, expected_cipher);
+      } else {
+        ASSERT_FALSE(rv);
+      }
+    }
+  }
+
   void SendPackets(size_t channel, size_t size, size_t count, bool srtp) {
     ASSERT(channel < channels_.size());
     rtc::scoped_ptr<char[]> packet(new char[size]);
@@ -433,6 +449,8 @@
       client1_.CheckSrtp("");
       client2_.CheckSrtp("");
     }
+    client1_.CheckSsl(rtc::SSLStreamAdapter::GetDefaultSslCipher());
+    client2_.CheckSsl(rtc::SSLStreamAdapter::GetDefaultSslCipher());
 
     return true;
   }
diff --git a/webrtc/p2p/base/fakesession.h b/webrtc/p2p/base/fakesession.h
index 13eceef..6f43b65 100644
--- a/webrtc/p2p/base/fakesession.h
+++ b/webrtc/p2p/base/fakesession.h
@@ -246,6 +246,10 @@
     return false;
   }
 
+  virtual bool GetSslCipher(std::string* cipher) {
+    return false;
+  }
+
   virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const {
     if (!identity_)
       return false;
diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h
index f8756dc..cd852d0 100644
--- a/webrtc/p2p/base/p2ptransportchannel.h
+++ b/webrtc/p2p/base/p2ptransportchannel.h
@@ -109,11 +109,16 @@
     return false;
   }
 
-  // Find out which DTLS-SRTP cipher was negotiated
+  // Find out which DTLS-SRTP cipher was negotiated.
   virtual bool GetSrtpCipher(std::string* cipher) {
     return false;
   }
 
+  // Find out which DTLS cipher was negotiated.
+  virtual bool GetSslCipher(std::string* cipher) {
+    return false;
+  }
+
   // Returns false because the channel is not encrypted by default.
   virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const {
     return false;
diff --git a/webrtc/p2p/base/rawtransportchannel.h b/webrtc/p2p/base/rawtransportchannel.h
index a4d9ce0..3455325 100644
--- a/webrtc/p2p/base/rawtransportchannel.h
+++ b/webrtc/p2p/base/rawtransportchannel.h
@@ -114,11 +114,16 @@
     return false;
   }
 
-  // Find out which DTLS-SRTP cipher was negotiated
+  // Find out which DTLS-SRTP cipher was negotiated.
   virtual bool GetSrtpCipher(std::string* cipher) {
     return false;
   }
 
+  // Find out which DTLS cipher was negotiated.
+  virtual bool GetSslCipher(std::string* cipher) {
+    return false;
+  }
+
   // Returns false because the channel is not DTLS.
   virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const {
     return false;
diff --git a/webrtc/p2p/base/transport.cc b/webrtc/p2p/base/transport.cc
index b03e2d5..0569cc0 100644
--- a/webrtc/p2p/base/transport.cc
+++ b/webrtc/p2p/base/transport.cc
@@ -454,9 +454,12 @@
   for (ChannelMap::iterator iter = channels_.begin();
        iter != channels_.end();
        ++iter) {
+    ChannelMapEntry& entry = iter->second;
     TransportChannelStats substats;
-    substats.component = iter->second->component();
-    if (!iter->second->GetStats(&substats.connection_infos)) {
+    substats.component = entry->component();
+    entry->GetSrtpCipher(&substats.srtp_cipher);
+    entry->GetSslCipher(&substats.ssl_cipher);
+    if (!entry->GetStats(&substats.connection_infos)) {
       return false;
     }
     stats->channel_stats.push_back(substats);
diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h
index abead7f..32c6bb3 100644
--- a/webrtc/p2p/base/transport.h
+++ b/webrtc/p2p/base/transport.h
@@ -106,6 +106,8 @@
 struct TransportChannelStats {
   int component;
   ConnectionInfos connection_infos;
+  std::string srtp_cipher;
+  std::string ssl_cipher;
 };
 
 // Information about all the channels of a transport.
diff --git a/webrtc/p2p/base/transportchannel.h b/webrtc/p2p/base/transportchannel.h
index d26a185..3d32b63 100644
--- a/webrtc/p2p/base/transportchannel.h
+++ b/webrtc/p2p/base/transportchannel.h
@@ -100,9 +100,12 @@
   // Sets up the ciphers to use for DTLS-SRTP.
   virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) = 0;
 
-  // Finds out which DTLS-SRTP cipher was negotiated
+  // Finds out which DTLS-SRTP cipher was negotiated.
   virtual bool GetSrtpCipher(std::string* cipher) = 0;
 
+  // Finds out which DTLS cipher was negotiated.
+  virtual bool GetSslCipher(std::string* cipher) = 0;
+
   // Gets a copy of the local SSL identity, owned by the caller.
   virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const = 0;
 
diff --git a/webrtc/p2p/base/transportchannelproxy.cc b/webrtc/p2p/base/transportchannelproxy.cc
index a8535fa..a278d96 100644
--- a/webrtc/p2p/base/transportchannelproxy.cc
+++ b/webrtc/p2p/base/transportchannelproxy.cc
@@ -186,6 +186,14 @@
   return impl_->GetSrtpCipher(cipher);
 }
 
+bool TransportChannelProxy::GetSslCipher(std::string* cipher) {
+  ASSERT(rtc::Thread::Current() == worker_thread_);
+  if (!impl_) {
+    return false;
+  }
+  return impl_->GetSslCipher(cipher);
+}
+
 bool TransportChannelProxy::GetLocalIdentity(
     rtc::SSLIdentity** identity) const {
   ASSERT(rtc::Thread::Current() == worker_thread_);
diff --git a/webrtc/p2p/base/transportchannelproxy.h b/webrtc/p2p/base/transportchannelproxy.h
index 46803f7..1987002 100644
--- a/webrtc/p2p/base/transportchannelproxy.h
+++ b/webrtc/p2p/base/transportchannelproxy.h
@@ -61,6 +61,7 @@
   virtual bool SetSslRole(rtc::SSLRole role);
   virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers);
   virtual bool GetSrtpCipher(std::string* cipher);
+  virtual bool GetSslCipher(std::string* cipher);
   virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const;
   virtual bool GetRemoteCertificate(rtc::SSLCertificate** cert) const;
   virtual bool ExportKeyingMaterial(const std::string& label,