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,