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}
diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc
index e217f80..4ccd6e8 100644
--- a/webrtc/api/peerconnection.cc
+++ b/webrtc/api/peerconnection.cc
@@ -1623,6 +1623,7 @@
}
session_options->rtcp_cname = rtcp_cname_;
+ session_options->crypto_options = factory_->options().crypto_options;
return true;
}
@@ -1650,6 +1651,7 @@
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(
diff --git a/webrtc/api/peerconnection_unittest.cc b/webrtc/api/peerconnection_unittest.cc
index 4b06b90..18d3606 100644
--- a/webrtc/api/peerconnection_unittest.cc
+++ b/webrtc/api/peerconnection_unittest.cc
@@ -100,6 +100,7 @@
// 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 @@
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 @@
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) {
diff --git a/webrtc/api/peerconnectionfactory.cc b/webrtc/api/peerconnectionfactory.cc
index 26ca666..82cd5d4 100644
--- a/webrtc/api/peerconnectionfactory.cc
+++ b/webrtc/api/peerconnectionfactory.cc
@@ -164,6 +164,7 @@
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 @@
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) {
diff --git a/webrtc/api/peerconnectionfactory.h b/webrtc/api/peerconnectionfactory.h
index c209fdb..377ad73 100644
--- a/webrtc/api/peerconnectionfactory.h
+++ b/webrtc/api/peerconnectionfactory.h
@@ -31,9 +31,7 @@
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(
diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h
index 39c4856..e0eb1a4 100644
--- a/webrtc/api/peerconnectioninterface.h
+++ b/webrtc/api/peerconnectioninterface.h
@@ -597,7 +597,8 @@
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 @@
// 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;
diff --git a/webrtc/api/webrtcsession_unittest.cc b/webrtc/api/webrtcsession_unittest.cc
index b96236d..1aff50f 100644
--- a/webrtc/api/webrtcsession_unittest.cc
+++ b/webrtc/api/webrtcsession_unittest.cc
@@ -461,6 +461,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
SetLocalDescriptionWithoutError(offer);
}
+TEST_F(WebRtcSessionTest, SetSetupGcm) {
+ InitWithGcm();
+ SendAudioVideoStream1();
+ CreateAndSetRemoteOfferAndLocalAnswer();
+}
+
TEST_F(WebRtcSessionTest, CanNotInsertDtmf) {
TestCanInsertDtmf(false);
}
diff --git a/webrtc/base/helpers.cc b/webrtc/base/helpers.cc
index 0a39ee9..b284cd7 100644
--- a/webrtc/base/helpers.cc
+++ b/webrtc/base/helpers.cc
@@ -245,6 +245,13 @@
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.
diff --git a/webrtc/base/helpers.h b/webrtc/base/helpers.h
index 0e79373..c75dba5 100644
--- a/webrtc/base/helpers.h
+++ b/webrtc/base/helpers.h
@@ -39,6 +39,10 @@
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();
diff --git a/webrtc/base/helpers_unittest.cc b/webrtc/base/helpers_unittest.cc
index 83cc685..e4903f5 100644
--- a/webrtc/base/helpers_unittest.cc
+++ b/webrtc/base/helpers_unittest.cc
@@ -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 @@
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 @@
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);
diff --git a/webrtc/base/opensslstreamadapter.cc b/webrtc/base/opensslstreamadapter.cc
index e04eb04..89f628d 100644
--- a/webrtc/base/opensslstreamadapter.cc
+++ b/webrtc/base/opensslstreamadapter.cc
@@ -56,6 +56,8 @@
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
diff --git a/webrtc/base/sslstreamadapter.cc b/webrtc/base/sslstreamadapter.cc
index 44158d4..c34fc90 100644
--- a/webrtc/base/sslstreamadapter.cc
+++ b/webrtc/base/sslstreamadapter.cc
@@ -25,13 +25,22 @@
// 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 @@
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);
diff --git a/webrtc/base/sslstreamadapter.h b/webrtc/base/sslstreamadapter.h
index ba60ce3..4dbe457 100644
--- a/webrtc/base/sslstreamadapter.h
+++ b/webrtc/base/sslstreamadapter.h
@@ -31,6 +31,12 @@
#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 @@
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 @@
// 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
diff --git a/webrtc/base/sslstreamadapter_unittest.cc b/webrtc/base/sslstreamadapter_unittest.cc
index dc62ac0..07ee855 100644
--- a/webrtc/base/sslstreamadapter_unittest.cc
+++ b/webrtc/base/sslstreamadapter_unittest.cc
@@ -947,7 +947,6 @@
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 @@
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);
diff --git a/webrtc/pc/channel.cc b/webrtc/pc/channel.cc
index cde1355..e464124 100644
--- a/webrtc/pc/channel.cc
+++ b/webrtc/pc/channel.cc
@@ -555,6 +555,11 @@
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 @@
if (!rtcp) {
GetSrtpCryptoSuites_n(&crypto_suites);
} else {
- GetDefaultSrtpCryptoSuites(&crypto_suites);
+ GetDefaultSrtpCryptoSuites(crypto_options(), &crypto_suites);
}
return tc->SetSrtpCryptoSuites(crypto_suites);
}
@@ -996,9 +1001,16 @@
<< 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 @@
}
// 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::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::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::GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const {
- GetSupportedDataCryptoSuites(crypto_suites);
+ GetSupportedDataCryptoSuites(crypto_options(), crypto_suites);
}
bool DataChannel::ShouldSetupDtlsSrtp_n() const {
diff --git a/webrtc/pc/channel.h b/webrtc/pc/channel.h
index 37eee47..3c00ee3 100644
--- a/webrtc/pc/channel.h
+++ b/webrtc/pc/channel.h
@@ -170,6 +170,8 @@
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 @@
// 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 @@
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.
diff --git a/webrtc/pc/channel_unittest.cc b/webrtc/pc/channel_unittest.cc
index b36dcd1..7b30547 100644
--- a/webrtc/pc/channel_unittest.cc
+++ b/webrtc/pc/channel_unittest.cc
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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::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 @@
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 @@
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::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;
diff --git a/webrtc/pc/channelmanager.cc b/webrtc/pc/channelmanager.cc
index c2ce1cc..06475e9 100644
--- a/webrtc/pc/channelmanager.cc
+++ b/webrtc/pc/channelmanager.cc
@@ -64,6 +64,7 @@
network_thread_ = network_thread;
capturing_ = false;
enable_rtx_ = false;
+ crypto_options_ = rtc::CryptoOptions::NoGcm();
}
ChannelManager::~ChannelManager() {
@@ -97,6 +98,30 @@
}
}
+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* 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* 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* 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;
diff --git a/webrtc/pc/channelmanager.h b/webrtc/pc/channelmanager.h
index c6a67df..15a3752 100644
--- a/webrtc/pc/channelmanager.h
+++ b/webrtc/pc/channelmanager.h
@@ -125,6 +125,10 @@
// 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 @@
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 @@
DataChannels data_channels_;
bool enable_rtx_;
+ rtc::CryptoOptions crypto_options_;
bool capturing_;
};
diff --git a/webrtc/pc/mediasession.cc b/webrtc/pc/mediasession.cc
index 937a2c1..28da731 100644
--- a/webrtc/pc/mediasession.cc
+++ b/webrtc/pc/mediasession.cc
@@ -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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
data->set_protocol(
secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
} else {
- GetSupportedDataCryptoSuiteNames(&crypto_suites);
+ GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
}
if (!CreateMediaContentOffer(
diff --git a/webrtc/pc/mediasession.h b/webrtc/pc/mediasession.h
index 34354dc..b39a8e5 100644
--- a/webrtc/pc/mediasession.h
+++ b/webrtc/pc/mediasession.h
@@ -163,6 +163,7 @@
// 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 @@
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
diff --git a/webrtc/pc/mediasession_unittest.cc b/webrtc/pc/mediasession_unittest.cc
index 8ad6526..281d306 100644
--- a/webrtc/pc/mediasession_unittest.cc
+++ b/webrtc/pc/mediasession_unittest.cc
@@ -73,6 +73,8 @@
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 @@
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 @@
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 @@
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 @@
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) {
diff --git a/webrtc/pc/srtpfilter.cc b/webrtc/pc/srtpfilter.cc
index 60dd4f1..9e7cc66 100644
--- a/webrtc/pc/srtpfilter.cc
+++ b/webrtc/pc/srtpfilter.cc
@@ -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_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 @@
// 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::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 @@
// 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 @@
}
}
-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 @@
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 @@
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;
}
diff --git a/webrtc/pc/srtpfilter.h b/webrtc/pc/srtpfilter.h
index cde9ad7..26a335f 100644
--- a/webrtc/pc/srtpfilter.h
+++ b/webrtc/pc/srtpfilter.h
@@ -33,13 +33,6 @@
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 @@
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 @@
// 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 @@
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);
diff --git a/webrtc/pc/srtpfilter_unittest.cc b/webrtc/pc/srtpfilter_unittest.cc
index cc5b3e5..cf80bdf 100644
--- a/webrtc/pc/srtpfilter_unittest.cc
+++ b/webrtc/pc/srtpfilter_unittest.cc
@@ -26,6 +26,8 @@
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 @@
"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 @@
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 @@
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;