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
This commit is contained in:
pthatcher@webrtc.org 2015-02-06 19:46:53 +00:00
parent 63da1dd972
commit 1d11c8202b
19 changed files with 273 additions and 5 deletions

View File

@ -37,5 +37,6 @@ Intel Corporation
MIPS Technologies
Mozilla Foundation
Opera Software ASA
struktur AG
Temasys Communications
Vonage Holdings Corp.

View File

@ -66,6 +66,10 @@ static const SrtpCipherMapEntry kSrtpCipherMap[] = {
};
#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 @@ SECStatus NSSStreamAdapter::GetClientAuthDataHook(void *arg, PRFileDesc *fd,
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 @@ bool NSSStreamAdapter::HaveExporter() {
return true;
}
std::string NSSStreamAdapter::GetDefaultSslCipher() {
return kDefaultSslCipher;
}
} // namespace rtc
#endif // HAVE_NSS_SSL_H

View File

@ -61,6 +61,8 @@ class NSSStreamAdapter : public SSLStreamAdapterHelper {
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 @@ class NSSStreamAdapter : public SSLStreamAdapterHelper {
static bool HaveDtls();
static bool HaveDtlsSrtp();
static bool HaveExporter();
static std::string GetDefaultSslCipher();
protected:
// Override SSLStreamAdapter

View File

@ -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 @@ static SrtpCipherMapEntry SrtpCipherMap[] = {
};
#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 @@ bool OpenSSLStreamAdapter::SetPeerCertificateDigest(const std::string
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 @@ bool OpenSSLStreamAdapter::HaveExporter() {
#endif
}
std::string OpenSSLStreamAdapter::GetDefaultSslCipher() {
return kDefaultSslCipher;
}
} // namespace rtc
#endif // HAVE_OPENSSL_SSL_H

View File

@ -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 @@ class OpenSSLStreamAdapter : public SSLStreamAdapter {
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 @@ class OpenSSLStreamAdapter : public SSLStreamAdapter {
static bool HaveDtls();
static bool HaveDtlsSrtp();
static bool HaveExporter();
static std::string GetDefaultSslCipher();
protected:
virtual void OnEvent(StreamInterface* stream, int events, int err);

View File

@ -50,6 +50,9 @@ SSLStreamAdapter* SSLStreamAdapter::Create(StreamInterface* stream) {
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::HaveDtlsSrtp() {
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::HaveDtlsSrtp() {
bool SSLStreamAdapter::HaveExporter() {
return NSSStreamAdapter::HaveExporter();
}
std::string SSLStreamAdapter::GetDefaultSslCipher() {
return NSSStreamAdapter::GetDefaultSslCipher();
}
#endif // !SSL_USE_SCHANNEL && !SSL_USE_OPENSSL && !SSL_USE_NSS
///////////////////////////////////////////////////////////////////////////////

View File

@ -119,6 +119,12 @@ class SSLStreamAdapter : public StreamAdapterInterface {
// 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 @@ class SSLStreamAdapter : public StreamAdapterInterface {
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

View File

@ -388,6 +388,13 @@ class SSLStreamAdapterTestBase : public testing::Test,
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 @@ TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestDTLSGetPeerCertificate) {
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);
}

View File

@ -186,6 +186,14 @@ bool DtlsTransportChannelWrapper::GetSslRole(rtc::SSLRole* role) const {
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,

View File

@ -150,6 +150,9 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
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;

View File

@ -213,6 +213,22 @@ class DtlsTestClient : public sigslot::has_slots<> {
}
}
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 @@ class DtlsTransportChannelTest : public testing::Test {
client1_.CheckSrtp("");
client2_.CheckSrtp("");
}
client1_.CheckSsl(rtc::SSLStreamAdapter::GetDefaultSslCipher());
client2_.CheckSsl(rtc::SSLStreamAdapter::GetDefaultSslCipher());
return true;
}

View File

@ -246,6 +246,10 @@ class FakeTransportChannel : public TransportChannelImpl,
return false;
}
virtual bool GetSslCipher(std::string* cipher) {
return false;
}
virtual bool GetLocalIdentity(rtc::SSLIdentity** identity) const {
if (!identity_)
return false;

View File

@ -109,11 +109,16 @@ class P2PTransportChannel : public TransportChannelImpl,
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;

View File

@ -114,11 +114,16 @@ class RawTransportChannel : public TransportChannelImpl,
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;

View File

@ -454,9 +454,12 @@ bool Transport::GetStats_w(TransportStats* stats) {
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);

View File

@ -106,6 +106,8 @@ typedef std::vector<ConnectionInfo> ConnectionInfos;
struct TransportChannelStats {
int component;
ConnectionInfos connection_infos;
std::string srtp_cipher;
std::string ssl_cipher;
};
// Information about all the channels of a transport.

View File

@ -100,9 +100,12 @@ class TransportChannel : public sigslot::has_slots<> {
// 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;

View File

@ -186,6 +186,14 @@ bool TransportChannelProxy::GetSrtpCipher(std::string* cipher) {
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_);

View File

@ -61,6 +61,7 @@ class TransportChannelProxy : public TransportChannel,
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,