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}
This commit is contained in:
jbauch 2016-08-04 05:20:32 -07:00 committed by Commit bot
parent bfc7a30c66
commit cb56065c62
24 changed files with 811 additions and 123 deletions

View File

@ -1623,6 +1623,7 @@ bool PeerConnection::GetOptionsForOffer(
}
session_options->rtcp_cname = rtcp_cname_;
session_options->crypto_options = factory_->options().crypto_options;
return true;
}
@ -1650,6 +1651,7 @@ void PeerConnection::FinishOptionsForAnswer(
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(

View File

@ -100,6 +100,7 @@ static const char kDataChannelLabel[] = "data_channel";
// 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 @@ class P2PTestConductor : public testing::Test {
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 @@ TEST_F(P2PTestConductor, GetDtls12Recv) {
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) {

View File

@ -164,6 +164,7 @@ bool PeerConnectionFactory::Initialize() {
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 @@ bool PeerConnectionFactory::Initialize() {
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) {

View File

@ -31,9 +31,7 @@ namespace webrtc {
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(

View File

@ -597,7 +597,8 @@ class PeerConnectionFactoryInterface : public rtc::RefCountInterface {
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 @@ class PeerConnectionFactoryInterface : public rtc::RefCountInterface {
// 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;

View File

@ -461,6 +461,14 @@ class WebRtcSessionTest
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 @@ class WebRtcSessionTest
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 @@ class WebRtcSessionTest
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 @@ class WebRtcSessionTest
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 @@ class WebRtcSessionTest
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 @@ class WebRtcSessionTest
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 @@ class WebRtcSessionTest
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 @@ TEST_F(WebRtcSessionTest, VerifyCryptoParamsInSDP) {
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 @@ TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) {
SetLocalDescriptionWithoutError(offer);
}
TEST_F(WebRtcSessionTest, SetSetupGcm) {
InitWithGcm();
SendAudioVideoStream1();
CreateAndSetRemoteOfferAndLocalAnswer();
}
TEST_F(WebRtcSessionTest, CanNotInsertDtmf) {
TestCanInsertDtmf(false);
}

View File

@ -245,6 +245,13 @@ bool CreateRandomString(size_t len, const std::string& table,
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.

View File

@ -39,6 +39,10 @@ bool CreateRandomString(size_t length, std::string* str);
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();

View File

@ -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 @@ TEST_F(RandomTest, TestCreateRandomString) {
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 @@ TEST_F(RandomTest, TestCreateRandomForTest) {
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);

View File

@ -56,6 +56,8 @@ struct SrtpCipherMapEntry {
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

View File

@ -25,13 +25,22 @@ namespace rtc {
// 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 @@ int SrtpCryptoSuiteFromName(const std::string& crypto_suite) {
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);

View File

@ -31,6 +31,12 @@ const int SRTP_AES128_CM_SHA1_80 = 0x0001;
#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 @@ const int SRTP_AES128_CM_SHA1_32 = 0x0002;
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 @@ std::string SrtpCryptoSuiteToName(int crypto_suite);
// 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

View File

@ -947,7 +947,6 @@ TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpLow) {
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 @@ TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpMixed) {
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);

View File

@ -555,6 +555,11 @@ int BaseChannel::SetOption_n(SocketType type,
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 @@ bool BaseChannel::SetDtlsSrtpCryptoSuites_n(TransportChannel* tc, bool rtcp) {
if (!rtcp) {
GetSrtpCryptoSuites_n(&crypto_suites);
} else {
GetDefaultSrtpCryptoSuites(&crypto_suites);
GetDefaultSrtpCryptoSuites(crypto_options(), &crypto_suites);
}
return tc->SetSrtpCryptoSuites(crypto_suites);
}
@ -996,9 +1001,16 @@ bool BaseChannel::SetupDtlsSrtp_n(bool rtcp_channel) {
<< 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 @@ bool BaseChannel::SetupDtlsSrtp_n(bool rtcp_channel) {
}
// 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::OnAudioMonitorUpdate(AudioMonitor* monitor,
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::OnMediaMonitorUpdate(
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::OnDataChannelReadyToSend(bool writable) {
}
void DataChannel::GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const {
GetSupportedDataCryptoSuites(crypto_suites);
GetSupportedDataCryptoSuites(crypto_options(), crypto_suites);
}
bool DataChannel::ShouldSetupDtlsSrtp_n() const {

View File

@ -170,6 +170,8 @@ class BaseChannel
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 @@ class BaseChannel
// 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 @@ class BaseChannel
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.

View File

@ -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 @@ template<class T>
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 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
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 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
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 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
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 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
// 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 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
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::VideoChannel* ChannelTest<VideoTraits>::CreateChannel(
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 @@ TEST_F(VoiceChannelSingleThreadTest, SendDtlsSrtpToDtlsSrtp) {
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 @@ TEST_F(VoiceChannelDoubleThreadTest, SendDtlsSrtpToDtlsSrtp) {
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::DataChannel* ChannelTest<DataTraits>::CreateChannel(
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;

View File

@ -64,6 +64,7 @@ void ChannelManager::Construct(MediaEngineInterface* me,
network_thread_ = network_thread;
capturing_ = false;
enable_rtx_ = false;
crypto_options_ = rtc::CryptoOptions::NoGcm();
}
ChannelManager::~ChannelManager() {
@ -97,6 +98,30 @@ bool ChannelManager::SetVideoRtxEnabled(bool enable) {
}
}
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* ChannelManager::CreateVoiceChannel_w(
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* ChannelManager::CreateVideoChannel_w(
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* ChannelManager::CreateDataChannel_w(
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;

View File

@ -125,6 +125,10 @@ class ChannelManager {
// 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 @@ class ChannelManager {
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 @@ class ChannelManager {
DataChannels data_channels_;
bool enable_rtx_;
rtc::CryptoOptions crypto_options_;
bool capturing_;
};

View File

@ -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 @@ static const uint32_t kMaxSctpSid = 1023;
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 IsMediaContentOfType(const ContentInfo* content,
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 @@ bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
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 @@ static bool CreateMediaContentAnswer(
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 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
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 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
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 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer(
data->set_protocol(
secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
} else {
GetSupportedDataCryptoSuiteNames(&crypto_suites);
GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
}
if (!CreateMediaContentOffer(

View File

@ -163,6 +163,7 @@ struct MediaSessionOptions {
// 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 @@ VideoContentDescription* GetFirstVideoContentDescription(
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

View File

@ -73,6 +73,8 @@ using cricket::SEC_ENABLED;
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 @@ class MediaSessionDescriptionFactoryTest : public testing::Test {
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 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswer) {
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 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
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 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswer) {
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) {

View File

@ -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_alloc;
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 @@ bool SrtpFilter::ApplyParams(const CryptoParams& send_params,
// 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::ResetParams() {
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 @@ bool SrtpFilter::ParseKeyParams(const std::string& key_params,
// 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 @@ SrtpSession::~SrtpSession() {
}
}
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 @@ void SrtpSession::set_signal_silent_time(int signal_silent_time_in_ms) {
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 @@ bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, int len) {
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;
}

View File

@ -33,13 +33,6 @@ struct srtp_policy_t;
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 @@ class SrtpFilter {
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 @@ class SrtpSession {
// 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 @@ class SrtpSession {
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);

View File

@ -26,6 +26,8 @@ extern "C" {
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 @@ static const std::string kTestKeyParams3 =
"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 @@ TEST_F(SrtpFilterTest, TestGoodSetupOneCipherSuite) {
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 @@ TEST_F(SrtpFilterTest, TestGoodSetupMultipleCipherSuites) {
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;