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:
parent
bfc7a30c66
commit
cb56065c62
@ -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(
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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_;
|
||||
};
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user