Support encrypted RTP extensions (RFC 6904)
Can be enabled by setting "enable_encrypted_rtp_header_extensions" in "crypto_options" of "PeerConnectionFactoryInterface::Options" and will only be used if both peers support it. BUG=webrtc:3411 Review-Url: https://codereview.webrtc.org/2761143002 Cr-Commit-Position: refs/heads/master@{#18842}
This commit is contained in:
parent
c9be3d5e8b
commit
5869f50f7a
@ -385,10 +385,17 @@ if (rtc_include_tests) {
|
||||
|
||||
rtc_test("rtc_unittests") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"config_unittest.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":webrtc_common",
|
||||
"api:rtc_api_unittests",
|
||||
"api/audio_codecs/test:audio_codecs_api_unittests",
|
||||
"base:rtc_base_approved_unittests",
|
||||
"base:rtc_base_tests_main",
|
||||
"base:rtc_base_tests_utils",
|
||||
"base:rtc_base_unittests",
|
||||
"base:rtc_numerics_unittests",
|
||||
"base:rtc_task_queue_unittests",
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#include "webrtc/config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
@ -41,6 +42,9 @@ std::string RtpExtension::ToString() const {
|
||||
std::stringstream ss;
|
||||
ss << "{uri: " << uri;
|
||||
ss << ", id: " << id;
|
||||
if (encrypt) {
|
||||
ss << ", encrypt";
|
||||
}
|
||||
ss << '}';
|
||||
return ss.str();
|
||||
}
|
||||
@ -80,6 +84,9 @@ const char* RtpExtension::kVideoTimingUri =
|
||||
"http://www.webrtc.org/experiments/rtp-hdrext/video-timing";
|
||||
const int RtpExtension::kVideoTimingDefaultId = 8;
|
||||
|
||||
const char* RtpExtension::kEncryptHeaderExtensionsUri =
|
||||
"urn:ietf:params:rtp-hdrext:encrypt";
|
||||
|
||||
const int RtpExtension::kMinId = 1;
|
||||
const int RtpExtension::kMaxId = 14;
|
||||
|
||||
@ -98,6 +105,61 @@ bool RtpExtension::IsSupportedForVideo(const std::string& uri) {
|
||||
uri == webrtc::RtpExtension::kVideoTimingUri;
|
||||
}
|
||||
|
||||
bool RtpExtension::IsEncryptionSupported(const std::string& uri) {
|
||||
return uri == webrtc::RtpExtension::kAudioLevelUri ||
|
||||
uri == webrtc::RtpExtension::kTimestampOffsetUri ||
|
||||
#if !defined(ENABLE_EXTERNAL_AUTH)
|
||||
// TODO(jbauch): Figure out a way to always allow "kAbsSendTimeUri"
|
||||
// here and filter out later if external auth is really used in
|
||||
// srtpfilter. External auth is used by Chromium and replaces the
|
||||
// extension header value of "kAbsSendTimeUri", so it must not be
|
||||
// encrypted (which can't be done by Chromium).
|
||||
uri == webrtc::RtpExtension::kAbsSendTimeUri ||
|
||||
#endif
|
||||
uri == webrtc::RtpExtension::kVideoRotationUri ||
|
||||
uri == webrtc::RtpExtension::kTransportSequenceNumberUri ||
|
||||
uri == webrtc::RtpExtension::kPlayoutDelayUri ||
|
||||
uri == webrtc::RtpExtension::kVideoContentTypeUri;
|
||||
}
|
||||
|
||||
const RtpExtension* RtpExtension::FindHeaderExtensionByUri(
|
||||
const std::vector<RtpExtension>& extensions,
|
||||
const std::string& uri) {
|
||||
for (const auto& extension : extensions) {
|
||||
if (extension.uri == uri) {
|
||||
return &extension;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<RtpExtension> RtpExtension::FilterDuplicateNonEncrypted(
|
||||
const std::vector<RtpExtension>& extensions) {
|
||||
std::vector<RtpExtension> filtered;
|
||||
for (auto extension = extensions.begin(); extension != extensions.end();
|
||||
++extension) {
|
||||
if (extension->encrypt) {
|
||||
filtered.push_back(*extension);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only add non-encrypted extension if no encrypted with the same URI
|
||||
// is also present...
|
||||
if (std::find_if(extension + 1, extensions.end(),
|
||||
[extension](const RtpExtension& check) {
|
||||
return extension->uri == check.uri;
|
||||
}) != extensions.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ...and has not been added before.
|
||||
if (!FindHeaderExtensionByUri(filtered, extension->uri)) {
|
||||
filtered.push_back(*extension);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
VideoStream::VideoStream()
|
||||
: width(0),
|
||||
height(0),
|
||||
|
||||
@ -58,14 +58,30 @@ struct UlpfecConfig {
|
||||
|
||||
// RTP header extension, see RFC 5285.
|
||||
struct RtpExtension {
|
||||
RtpExtension() : id(0) {}
|
||||
RtpExtension() {}
|
||||
RtpExtension(const std::string& uri, int id) : uri(uri), id(id) {}
|
||||
RtpExtension(const std::string& uri, int id, bool encrypt) : uri(uri),
|
||||
id(id), encrypt(encrypt) {}
|
||||
std::string ToString() const;
|
||||
bool operator==(const RtpExtension& rhs) const {
|
||||
return uri == rhs.uri && id == rhs.id;
|
||||
return uri == rhs.uri && id == rhs.id && encrypt == rhs.encrypt;
|
||||
}
|
||||
static bool IsSupportedForAudio(const std::string& uri);
|
||||
static bool IsSupportedForVideo(const std::string& uri);
|
||||
// Return "true" if the given RTP header extension URI may be encrypted.
|
||||
static bool IsEncryptionSupported(const std::string& uri);
|
||||
|
||||
// Returns the named header extension if found among all extensions,
|
||||
// nullptr otherwise.
|
||||
static const RtpExtension* FindHeaderExtensionByUri(
|
||||
const std::vector<RtpExtension>& extensions,
|
||||
const std::string& uri);
|
||||
|
||||
// Return a list of RTP header extensions with the non-encrypted extensions
|
||||
// removed if both the encrypted and non-encrypted extension is present for
|
||||
// the same URI.
|
||||
static std::vector<RtpExtension> FilterDuplicateNonEncrypted(
|
||||
const std::vector<RtpExtension>& extensions);
|
||||
|
||||
// Header extension for audio levels, as defined in:
|
||||
// http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-03
|
||||
@ -104,12 +120,17 @@ struct RtpExtension {
|
||||
static const char* kPlayoutDelayUri;
|
||||
static const int kPlayoutDelayDefaultId;
|
||||
|
||||
// Encryption of Header Extensions, see RFC 6904 for details:
|
||||
// https://tools.ietf.org/html/rfc6904
|
||||
static const char* kEncryptHeaderExtensionsUri;
|
||||
|
||||
// Inclusive min and max IDs for one-byte header extensions, per RFC5285.
|
||||
static const int kMinId;
|
||||
static const int kMaxId;
|
||||
|
||||
std::string uri;
|
||||
int id;
|
||||
int id = 0;
|
||||
bool encrypt = false;
|
||||
};
|
||||
|
||||
struct VideoStream {
|
||||
|
||||
49
webrtc/config_unittest.cc
Normal file
49
webrtc/config_unittest.cc
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/config.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/gunit.h"
|
||||
|
||||
using webrtc::RtpExtension;
|
||||
|
||||
static const char kExtensionUri1[] = "extension-uri1";
|
||||
static const char kExtensionUri2[] = "extension-uri2";
|
||||
|
||||
static const RtpExtension kExtension1(kExtensionUri1, 1);
|
||||
static const RtpExtension kExtension1Encrypted(kExtensionUri1, 10, true);
|
||||
static const RtpExtension kExtension2(kExtensionUri2, 2);
|
||||
|
||||
TEST(RtpExtensionTest, FilterDuplicateNonEncrypted) {
|
||||
std::vector<RtpExtension> extensions;
|
||||
std::vector<RtpExtension> filtered;
|
||||
|
||||
extensions.push_back(kExtension1);
|
||||
extensions.push_back(kExtension1Encrypted);
|
||||
filtered = RtpExtension::FilterDuplicateNonEncrypted(extensions);
|
||||
EXPECT_EQ(1u, filtered.size());
|
||||
EXPECT_EQ(std::vector<RtpExtension>{kExtension1Encrypted}, filtered);
|
||||
|
||||
extensions.clear();
|
||||
extensions.push_back(kExtension1Encrypted);
|
||||
extensions.push_back(kExtension1);
|
||||
filtered = RtpExtension::FilterDuplicateNonEncrypted(extensions);
|
||||
EXPECT_EQ(1u, filtered.size());
|
||||
EXPECT_EQ(std::vector<RtpExtension>{kExtension1Encrypted}, filtered);
|
||||
|
||||
extensions.clear();
|
||||
extensions.push_back(kExtension1);
|
||||
extensions.push_back(kExtension2);
|
||||
filtered = RtpExtension::FilterDuplicateNonEncrypted(extensions);
|
||||
EXPECT_EQ(2u, filtered.size());
|
||||
EXPECT_EQ(extensions, filtered);
|
||||
}
|
||||
@ -327,6 +327,7 @@ if (rtc_include_tests) {
|
||||
sources = [
|
||||
"base/fakemediaengine.h",
|
||||
"base/fakenetworkinterface.h",
|
||||
"base/fakertp.cc",
|
||||
"base/fakertp.h",
|
||||
"base/fakevideocapturer.h",
|
||||
"base/fakevideorenderer.h",
|
||||
|
||||
66
webrtc/media/base/fakertp.cc
Normal file
66
webrtc/media/base/fakertp.cc
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/media/base/fakertp.h"
|
||||
|
||||
void CompareHeaderExtensions(const char* packet1, size_t packet1_size,
|
||||
const char* packet2, size_t packet2_size,
|
||||
const std::vector<int> encrypted_headers, bool expect_equal) {
|
||||
// Sanity check: packets must be large enough to contain the RTP header and
|
||||
// extensions header.
|
||||
RTC_CHECK_GE(packet1_size, 12 + 4);
|
||||
RTC_CHECK_GE(packet2_size, 12 + 4);
|
||||
// RTP extension headers are the same.
|
||||
EXPECT_EQ(0, memcmp(packet1 + 12, packet2 + 12, 4));
|
||||
// Check for one-byte header extensions.
|
||||
EXPECT_EQ('\xBE', packet1[12]);
|
||||
EXPECT_EQ('\xDE', packet1[13]);
|
||||
// Determine position and size of extension headers.
|
||||
size_t extension_words = packet1[14] << 8 | packet1[15];
|
||||
const char* extension_data1 = packet1 + 12 + 4;
|
||||
const char* extension_end1 = extension_data1 + extension_words * 4;
|
||||
const char* extension_data2 = packet2 + 12 + 4;
|
||||
// Sanity check: packets must be large enough to contain the RTP header
|
||||
// extensions.
|
||||
RTC_CHECK_GE(packet1_size, 12 + 4 + extension_words * 4);
|
||||
RTC_CHECK_GE(packet2_size, 12 + 4 + extension_words * 4);
|
||||
while (extension_data1 < extension_end1) {
|
||||
uint8_t id = (*extension_data1 & 0xf0) >> 4;
|
||||
uint8_t len = (*extension_data1 & 0x0f) +1;
|
||||
extension_data1++;
|
||||
extension_data2++;
|
||||
EXPECT_LE(extension_data1, extension_end1);
|
||||
if (id == 15) {
|
||||
// Finished parsing.
|
||||
break;
|
||||
}
|
||||
|
||||
// The header extension doesn't get encrypted if the id is not in the
|
||||
// list of header extensions to encrypt.
|
||||
if (expect_equal ||
|
||||
std::find(encrypted_headers.begin(), encrypted_headers.end(), id)
|
||||
== encrypted_headers.end()) {
|
||||
EXPECT_EQ(0, memcmp(extension_data1, extension_data2, len));
|
||||
} else {
|
||||
EXPECT_NE(0, memcmp(extension_data1, extension_data2, len));
|
||||
}
|
||||
|
||||
extension_data1 += len;
|
||||
extension_data2 += len;
|
||||
// Skip padding.
|
||||
while (extension_data1 < extension_end1 && *extension_data1 == 0) {
|
||||
extension_data1++;
|
||||
extension_data2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,8 @@
|
||||
#ifndef WEBRTC_MEDIA_BASE_FAKERTP_H_
|
||||
#define WEBRTC_MEDIA_BASE_FAKERTP_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
// A typical PCMU RTP packet.
|
||||
// PT=0, SN=1, TS=0, SSRC=1
|
||||
// all data FF
|
||||
@ -40,6 +42,48 @@ static const unsigned char kPcmuFrame[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
// A typical PCMU RTP packet with header extensions.
|
||||
// PT=0, SN=1, TS=0, SSRC=1
|
||||
// all data FF
|
||||
static const unsigned char kPcmuFrameWithExtensions[] = {
|
||||
0x90, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
// RFC 5285, section 4.2. One-Byte Header.
|
||||
0xBE, 0xDE,
|
||||
// Header extension length 6 * 32 bits.
|
||||
0x00, 0x06,
|
||||
// 8 bytes header id 1.
|
||||
0x17, 0x41, 0x42, 0x73, 0xA4, 0x75, 0x26, 0x27, 0x48,
|
||||
// 3 bytes header id 2.
|
||||
0x22, 0x00, 0x00, 0xC8,
|
||||
// 1 byte header id 3.
|
||||
0x30, 0x8E,
|
||||
// 7 bytes header id 4.
|
||||
0x46, 0x55, 0x99, 0x63, 0x86, 0xB3, 0x95, 0xFB,
|
||||
// 1 byte header padding.
|
||||
0x00,
|
||||
// Payload data.
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
// A typical Receiver Report RTCP packet.
|
||||
// PT=RR, LN=1, SSRC=1
|
||||
// send SSRC=2, all other fields 0
|
||||
@ -84,4 +128,11 @@ static const unsigned char kDataPacket[] = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
};
|
||||
|
||||
// This expects both packets to be based on kPcmuFrameWithExtensions.
|
||||
// Header extensions with an id in "encrypted_headers" are expected to be
|
||||
// different in the packets unless "expect_equal" is set to "true".
|
||||
void CompareHeaderExtensions(const char* packet1, size_t packet1_size,
|
||||
const char* packet2, size_t packet2_size,
|
||||
const std::vector<int> encrypted_headers, bool expect_equal);
|
||||
|
||||
#endif // WEBRTC_MEDIA_BASE_FAKERTP_H_
|
||||
|
||||
@ -208,18 +208,22 @@ std::vector<webrtc::RtpExtension> FilterRtpExtensions(
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by name, ascending, so that we don't reset extensions if they were
|
||||
// specified in a different order (also allows us to use std::unique below).
|
||||
// Sort by name, ascending (prioritise encryption), so that we don't reset
|
||||
// extensions if they were specified in a different order (also allows us
|
||||
// to use std::unique below).
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const webrtc::RtpExtension& rhs,
|
||||
const webrtc::RtpExtension& lhs) { return rhs.uri < lhs.uri; });
|
||||
const webrtc::RtpExtension& lhs) {
|
||||
return rhs.encrypt == lhs.encrypt ? rhs.uri < lhs.uri
|
||||
: rhs.encrypt > lhs.encrypt;
|
||||
});
|
||||
|
||||
// Remove unnecessary extensions (used on send side).
|
||||
if (filter_redundant_extensions) {
|
||||
auto it = std::unique(
|
||||
result.begin(), result.end(),
|
||||
[](const webrtc::RtpExtension& rhs, const webrtc::RtpExtension& lhs) {
|
||||
return rhs.uri == lhs.uri;
|
||||
return rhs.uri == lhs.uri && rhs.encrypt == lhs.encrypt;
|
||||
});
|
||||
result.erase(it, result.end());
|
||||
|
||||
|
||||
@ -145,6 +145,38 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundant) {
|
||||
EXPECT_NE(filtered[0].uri, filtered[1].uri);
|
||||
}
|
||||
|
||||
TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantEncrypted_1) {
|
||||
std::vector<RtpExtension> extensions;
|
||||
extensions.push_back(webrtc::RtpExtension("b", 1));
|
||||
extensions.push_back(webrtc::RtpExtension("b", 2, true));
|
||||
extensions.push_back(webrtc::RtpExtension("c", 3));
|
||||
extensions.push_back(webrtc::RtpExtension("b", 4));
|
||||
std::vector<webrtc::RtpExtension> filtered =
|
||||
FilterRtpExtensions(extensions, SupportedExtensions2, true);
|
||||
EXPECT_EQ(3, filtered.size());
|
||||
EXPECT_TRUE(IsSorted(filtered));
|
||||
EXPECT_EQ(filtered[0].uri, filtered[1].uri);
|
||||
EXPECT_NE(filtered[0].encrypt, filtered[1].encrypt);
|
||||
EXPECT_NE(filtered[0].uri, filtered[2].uri);
|
||||
EXPECT_NE(filtered[1].uri, filtered[2].uri);
|
||||
}
|
||||
|
||||
TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantEncrypted_2) {
|
||||
std::vector<RtpExtension> extensions;
|
||||
extensions.push_back(webrtc::RtpExtension("b", 1, true));
|
||||
extensions.push_back(webrtc::RtpExtension("b", 2));
|
||||
extensions.push_back(webrtc::RtpExtension("c", 3));
|
||||
extensions.push_back(webrtc::RtpExtension("b", 4));
|
||||
std::vector<webrtc::RtpExtension> filtered =
|
||||
FilterRtpExtensions(extensions, SupportedExtensions2, true);
|
||||
EXPECT_EQ(3, filtered.size());
|
||||
EXPECT_TRUE(IsSorted(filtered));
|
||||
EXPECT_EQ(filtered[0].uri, filtered[1].uri);
|
||||
EXPECT_NE(filtered[0].encrypt, filtered[1].encrypt);
|
||||
EXPECT_NE(filtered[0].uri, filtered[2].uri);
|
||||
EXPECT_NE(filtered[1].uri, filtered[2].uri);
|
||||
}
|
||||
|
||||
TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_1) {
|
||||
std::vector<RtpExtension> extensions;
|
||||
extensions.push_back(
|
||||
@ -160,6 +192,27 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_1) {
|
||||
EXPECT_EQ(RtpExtension::kTransportSequenceNumberUri, filtered[0].uri);
|
||||
}
|
||||
|
||||
TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBweEncrypted_1) {
|
||||
std::vector<RtpExtension> extensions;
|
||||
extensions.push_back(
|
||||
RtpExtension(RtpExtension::kTransportSequenceNumberUri, 3));
|
||||
extensions.push_back(
|
||||
RtpExtension(RtpExtension::kTransportSequenceNumberUri, 4, true));
|
||||
extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 9));
|
||||
extensions.push_back(RtpExtension(RtpExtension::kAbsSendTimeUri, 6));
|
||||
extensions.push_back(
|
||||
RtpExtension(RtpExtension::kTransportSequenceNumberUri, 1));
|
||||
extensions.push_back(
|
||||
RtpExtension(RtpExtension::kTransportSequenceNumberUri, 2, true));
|
||||
extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 14));
|
||||
std::vector<webrtc::RtpExtension> filtered =
|
||||
FilterRtpExtensions(extensions, SupportedExtensions2, true);
|
||||
EXPECT_EQ(2, filtered.size());
|
||||
EXPECT_EQ(RtpExtension::kTransportSequenceNumberUri, filtered[0].uri);
|
||||
EXPECT_EQ(RtpExtension::kTransportSequenceNumberUri, filtered[1].uri);
|
||||
EXPECT_NE(filtered[0].encrypt, filtered[1].encrypt);
|
||||
}
|
||||
|
||||
TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_2) {
|
||||
std::vector<RtpExtension> extensions;
|
||||
extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 1));
|
||||
|
||||
@ -117,7 +117,8 @@ DtlsTransport::DtlsTransport(IceTransportInternal* ice_transport,
|
||||
downward_(NULL),
|
||||
srtp_ciphers_(GetSupportedDtlsSrtpCryptoSuites(crypto_options)),
|
||||
ssl_role_(rtc::SSL_CLIENT),
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12) {
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12),
|
||||
crypto_options_(crypto_options) {
|
||||
ice_transport_->SignalWritableState.connect(this,
|
||||
&DtlsTransport::OnWritableState);
|
||||
ice_transport_->SignalReadPacket.connect(this, &DtlsTransport::OnReadPacket);
|
||||
|
||||
@ -94,6 +94,10 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
const rtc::CryptoOptions& crypto_options);
|
||||
~DtlsTransport() override;
|
||||
|
||||
const rtc::CryptoOptions& crypto_options() const override {
|
||||
return crypto_options_;
|
||||
}
|
||||
|
||||
DtlsTransportState dtls_state() const override { return dtls_state_; }
|
||||
|
||||
const std::string& transport_name() const override { return transport_name_; }
|
||||
@ -218,6 +222,7 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
|
||||
rtc::SSLRole ssl_role_;
|
||||
rtc::SSLProtocolVersion ssl_max_version_;
|
||||
rtc::CryptoOptions crypto_options_;
|
||||
rtc::Buffer remote_fingerprint_value_;
|
||||
std::string remote_fingerprint_algorithm_;
|
||||
|
||||
|
||||
@ -39,6 +39,8 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal {
|
||||
public:
|
||||
virtual ~DtlsTransportInternal() {}
|
||||
|
||||
virtual const rtc::CryptoOptions& crypto_options() const = 0;
|
||||
|
||||
virtual DtlsTransportState dtls_state() const = 0;
|
||||
|
||||
virtual const std::string& transport_name() const = 0;
|
||||
|
||||
@ -91,6 +91,7 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
if (!asymmetric) {
|
||||
dest->SetDestination(this, true);
|
||||
}
|
||||
dtls_state_ = DTLS_TRANSPORT_CONNECTED;
|
||||
ice_transport_->SetDestination(
|
||||
static_cast<FakeIceTransport*>(dest->ice_transport()), asymmetric);
|
||||
} else {
|
||||
@ -122,6 +123,12 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
*role = ssl_role_;
|
||||
return true;
|
||||
}
|
||||
const rtc::CryptoOptions& crypto_options() const override {
|
||||
return crypto_options_;
|
||||
}
|
||||
void SetCryptoOptions(const rtc::CryptoOptions& crypto_options) {
|
||||
crypto_options_ = crypto_options;
|
||||
}
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
|
||||
local_cert_ = certificate;
|
||||
@ -135,9 +142,12 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
if (!do_dtls_) {
|
||||
return false;
|
||||
}
|
||||
*crypto_suite = rtc::SRTP_AES128_CM_SHA1_80;
|
||||
*crypto_suite = crypto_suite_;
|
||||
return true;
|
||||
}
|
||||
void SetSrtpCryptoSuite(int crypto_suite) {
|
||||
crypto_suite_ = crypto_suite;
|
||||
}
|
||||
bool GetSslCipherSuite(int* cipher_suite) override { return false; }
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override {
|
||||
return local_cert_;
|
||||
@ -230,6 +240,8 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
|
||||
rtc::SSLFingerprint dtls_fingerprint_;
|
||||
rtc::SSLRole ssl_role_ = rtc::SSL_CLIENT;
|
||||
int crypto_suite_ = rtc::SRTP_AES128_CM_SHA1_80;
|
||||
rtc::CryptoOptions crypto_options_;
|
||||
|
||||
DtlsTransportState dtls_state_ = DTLS_TRANSPORT_NEW;
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "webrtc/pc/channel.h"
|
||||
@ -46,20 +48,6 @@ struct SendPacketMessageData : public rtc::MessageData {
|
||||
rtc::PacketOptions options;
|
||||
};
|
||||
|
||||
#if defined(ENABLE_EXTERNAL_AUTH)
|
||||
// Returns the named header extension if found among all extensions,
|
||||
// nullptr otherwise.
|
||||
const webrtc::RtpExtension* FindHeaderExtension(
|
||||
const std::vector<webrtc::RtpExtension>& extensions,
|
||||
const std::string& uri) {
|
||||
for (const auto& extension : extensions) {
|
||||
if (extension.uri == uri)
|
||||
return &extension;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
enum {
|
||||
@ -130,6 +118,7 @@ static const MediaContentDescription* GetContentDescription(
|
||||
template <class Codec>
|
||||
void RtpParametersFromMediaDescription(
|
||||
const MediaContentDescriptionImpl<Codec>* desc,
|
||||
const RtpHeaderExtensions& extensions,
|
||||
RtpParameters<Codec>* params) {
|
||||
// TODO(pthatcher): Remove this once we're sure no one will give us
|
||||
// a description without codecs (currently a CA_UPDATE with just
|
||||
@ -140,7 +129,7 @@ void RtpParametersFromMediaDescription(
|
||||
// TODO(pthatcher): See if we really need
|
||||
// rtp_header_extensions_set() and remove it if we don't.
|
||||
if (desc->rtp_header_extensions_set()) {
|
||||
params->extensions = desc->rtp_header_extensions();
|
||||
params->extensions = extensions;
|
||||
}
|
||||
params->rtcp.reduced_size = desc->rtcp_reduced_size();
|
||||
}
|
||||
@ -148,8 +137,9 @@ void RtpParametersFromMediaDescription(
|
||||
template <class Codec>
|
||||
void RtpSendParametersFromMediaDescription(
|
||||
const MediaContentDescriptionImpl<Codec>* desc,
|
||||
const RtpHeaderExtensions& extensions,
|
||||
RtpSendParameters<Codec>* send_params) {
|
||||
RtpParametersFromMediaDescription(desc, send_params);
|
||||
RtpParametersFromMediaDescription(desc, extensions, send_params);
|
||||
send_params->max_bandwidth_bps = desc->bandwidth();
|
||||
}
|
||||
|
||||
@ -998,16 +988,30 @@ bool BaseChannel::SetupDtlsSrtp_n(bool rtcp) {
|
||||
recv_key = &server_write_key;
|
||||
}
|
||||
|
||||
if (rtcp) {
|
||||
ret = srtp_filter_.SetRtcpParams(selected_crypto_suite, &(*send_key)[0],
|
||||
static_cast<int>(send_key->size()),
|
||||
selected_crypto_suite, &(*recv_key)[0],
|
||||
static_cast<int>(recv_key->size()));
|
||||
if (!srtp_filter_.IsActive()) {
|
||||
if (rtcp) {
|
||||
ret = srtp_filter_.SetRtcpParams(selected_crypto_suite, &(*send_key)[0],
|
||||
static_cast<int>(send_key->size()),
|
||||
selected_crypto_suite, &(*recv_key)[0],
|
||||
static_cast<int>(recv_key->size()));
|
||||
} else {
|
||||
ret = srtp_filter_.SetRtpParams(selected_crypto_suite, &(*send_key)[0],
|
||||
static_cast<int>(send_key->size()),
|
||||
selected_crypto_suite, &(*recv_key)[0],
|
||||
static_cast<int>(recv_key->size()));
|
||||
}
|
||||
} else {
|
||||
ret = srtp_filter_.SetRtpParams(selected_crypto_suite, &(*send_key)[0],
|
||||
static_cast<int>(send_key->size()),
|
||||
selected_crypto_suite, &(*recv_key)[0],
|
||||
static_cast<int>(recv_key->size()));
|
||||
if (rtcp) {
|
||||
// RTCP doesn't need to be updated because UpdateRtpParams is only used
|
||||
// to update the set of encrypted RTP header extension IDs.
|
||||
ret = true;
|
||||
} else {
|
||||
ret = srtp_filter_.UpdateRtpParams(
|
||||
selected_crypto_suite,
|
||||
&(*send_key)[0], static_cast<int>(send_key->size()),
|
||||
selected_crypto_suite,
|
||||
&(*recv_key)[0], static_cast<int>(recv_key->size()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
@ -1055,26 +1059,39 @@ bool BaseChannel::SetRtpTransportParameters(
|
||||
const MediaContentDescription* content,
|
||||
ContentAction action,
|
||||
ContentSource src,
|
||||
const RtpHeaderExtensions& extensions,
|
||||
std::string* error_desc) {
|
||||
if (action == CA_UPDATE) {
|
||||
// These parameters never get changed by a CA_UDPATE.
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<int> encrypted_extension_ids;
|
||||
for (const webrtc::RtpExtension& extension : extensions) {
|
||||
if (extension.encrypt) {
|
||||
LOG(LS_INFO) << "Using " << (src == CS_LOCAL ? "local" : "remote")
|
||||
<< " encrypted extension: " << extension.ToString();
|
||||
encrypted_extension_ids.push_back(extension.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache srtp_required_ for belt and suspenders check on SendPacket
|
||||
return network_thread_->Invoke<bool>(
|
||||
RTC_FROM_HERE, Bind(&BaseChannel::SetRtpTransportParameters_n, this,
|
||||
content, action, src, error_desc));
|
||||
content, action, src, encrypted_extension_ids,
|
||||
error_desc));
|
||||
}
|
||||
|
||||
bool BaseChannel::SetRtpTransportParameters_n(
|
||||
const MediaContentDescription* content,
|
||||
ContentAction action,
|
||||
ContentSource src,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
std::string* error_desc) {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
|
||||
if (!SetSrtp_n(content->cryptos(), action, src, error_desc)) {
|
||||
if (!SetSrtp_n(content->cryptos(), action, src, encrypted_extension_ids,
|
||||
error_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1101,6 +1118,7 @@ bool BaseChannel::CheckSrtpConfig_n(const std::vector<CryptoParams>& cryptos,
|
||||
bool BaseChannel::SetSrtp_n(const std::vector<CryptoParams>& cryptos,
|
||||
ContentAction action,
|
||||
ContentSource src,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
std::string* error_desc) {
|
||||
TRACE_EVENT0("webrtc", "BaseChannel::SetSrtp_w");
|
||||
if (action == CA_UPDATE) {
|
||||
@ -1113,6 +1131,7 @@ bool BaseChannel::SetSrtp_n(const std::vector<CryptoParams>& cryptos,
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
srtp_filter_.SetEncryptedHeaderExtensionIds(src, encrypted_extension_ids);
|
||||
switch (action) {
|
||||
case CA_OFFER:
|
||||
// If DTLS is already active on the channel, we could be renegotiating
|
||||
@ -1138,6 +1157,14 @@ bool BaseChannel::SetSrtp_n(const std::vector<CryptoParams>& cryptos,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Only update SRTP filter if using DTLS. SDES is handled internally
|
||||
// by the SRTP filter.
|
||||
// TODO(jbauch): Only update if encrypted extension ids have changed.
|
||||
if (ret && dtls_keyed_ && rtp_dtls_transport_ &&
|
||||
rtp_dtls_transport_->dtls_state() == DTLS_TRANSPORT_CONNECTED) {
|
||||
bool rtcp = false;
|
||||
ret = SetupDtlsSrtp_n(rtcp);
|
||||
}
|
||||
if (!ret) {
|
||||
SafeSetError("Failed to setup SRTP filter.", error_desc);
|
||||
return false;
|
||||
@ -1369,6 +1396,23 @@ bool BaseChannel::UpdateRemoteStreams_w(
|
||||
return ret;
|
||||
}
|
||||
|
||||
RtpHeaderExtensions BaseChannel::GetFilteredRtpHeaderExtensions(
|
||||
const RtpHeaderExtensions& extensions) {
|
||||
if (!rtp_dtls_transport_ ||
|
||||
!rtp_dtls_transport_->crypto_options()
|
||||
.enable_encrypted_rtp_header_extensions) {
|
||||
RtpHeaderExtensions filtered;
|
||||
auto pred = [](const webrtc::RtpExtension& extension) {
|
||||
return !extension.encrypt;
|
||||
};
|
||||
std::copy_if(extensions.begin(), extensions.end(),
|
||||
std::back_inserter(filtered), pred);
|
||||
return filtered;
|
||||
}
|
||||
|
||||
return webrtc::RtpExtension::FilterDuplicateNonEncrypted(extensions);
|
||||
}
|
||||
|
||||
void BaseChannel::MaybeCacheRtpAbsSendTimeHeaderExtension_w(
|
||||
const std::vector<webrtc::RtpExtension>& extensions) {
|
||||
// Absolute Send Time extension id is used only with external auth,
|
||||
@ -1376,7 +1420,8 @@ void BaseChannel::MaybeCacheRtpAbsSendTimeHeaderExtension_w(
|
||||
// something that is not used.
|
||||
#if defined(ENABLE_EXTERNAL_AUTH)
|
||||
const webrtc::RtpExtension* send_time_extension =
|
||||
FindHeaderExtension(extensions, webrtc::RtpExtension::kAbsSendTimeUri);
|
||||
webrtc::RtpExtension::FindHeaderExtensionByUri(
|
||||
extensions, webrtc::RtpExtension::kAbsSendTimeUri);
|
||||
int rtp_abs_sendtime_extn_id =
|
||||
send_time_extension ? send_time_extension->id : -1;
|
||||
invoker_.AsyncInvoke<void>(
|
||||
@ -1724,12 +1769,16 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) {
|
||||
RtpHeaderExtensions rtp_header_extensions =
|
||||
GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_LOCAL,
|
||||
rtp_header_extensions, error_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioRecvParameters recv_params = last_recv_params_;
|
||||
RtpParametersFromMediaDescription(audio, &recv_params);
|
||||
RtpParametersFromMediaDescription(audio, rtp_header_extensions, &recv_params);
|
||||
if (!media_channel()->SetRecvParameters(recv_params)) {
|
||||
SafeSetError("Failed to set local audio description recv parameters.",
|
||||
error_desc);
|
||||
@ -1769,12 +1818,17 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) {
|
||||
RtpHeaderExtensions rtp_header_extensions =
|
||||
GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_REMOTE,
|
||||
rtp_header_extensions, error_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioSendParameters send_params = last_send_params_;
|
||||
RtpSendParametersFromMediaDescription(audio, &send_params);
|
||||
RtpSendParametersFromMediaDescription(audio, rtp_header_extensions,
|
||||
&send_params);
|
||||
if (audio->agc_minus_10db()) {
|
||||
send_params.options.adjust_agc_delta = rtc::Optional<int>(kAgcMinus10db);
|
||||
}
|
||||
@ -1797,7 +1851,7 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content,
|
||||
}
|
||||
|
||||
if (audio->rtp_header_extensions_set()) {
|
||||
MaybeCacheRtpAbsSendTimeHeaderExtension_w(audio->rtp_header_extensions());
|
||||
MaybeCacheRtpAbsSendTimeHeaderExtension_w(rtp_header_extensions);
|
||||
}
|
||||
|
||||
set_remote_content_direction(content->direction());
|
||||
@ -2002,12 +2056,16 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) {
|
||||
RtpHeaderExtensions rtp_header_extensions =
|
||||
GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_LOCAL,
|
||||
rtp_header_extensions, error_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VideoRecvParameters recv_params = last_recv_params_;
|
||||
RtpParametersFromMediaDescription(video, &recv_params);
|
||||
RtpParametersFromMediaDescription(video, rtp_header_extensions, &recv_params);
|
||||
if (!media_channel()->SetRecvParameters(recv_params)) {
|
||||
SafeSetError("Failed to set local video description recv parameters.",
|
||||
error_desc);
|
||||
@ -2047,12 +2105,17 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) {
|
||||
RtpHeaderExtensions rtp_header_extensions =
|
||||
GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_REMOTE,
|
||||
rtp_header_extensions, error_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VideoSendParameters send_params = last_send_params_;
|
||||
RtpSendParametersFromMediaDescription(video, &send_params);
|
||||
RtpSendParametersFromMediaDescription(video, rtp_header_extensions,
|
||||
&send_params);
|
||||
if (video->conference_mode()) {
|
||||
send_params.conference_mode = true;
|
||||
}
|
||||
@ -2076,7 +2139,7 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
|
||||
}
|
||||
|
||||
if (video->rtp_header_extensions_set()) {
|
||||
MaybeCacheRtpAbsSendTimeHeaderExtension_w(video->rtp_header_extensions());
|
||||
MaybeCacheRtpAbsSendTimeHeaderExtension_w(rtp_header_extensions);
|
||||
}
|
||||
|
||||
set_remote_content_direction(content->direction());
|
||||
@ -2197,12 +2260,16 @@ bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) {
|
||||
RtpHeaderExtensions rtp_header_extensions =
|
||||
GetFilteredRtpHeaderExtensions(data->rtp_header_extensions());
|
||||
|
||||
if (!SetRtpTransportParameters(content, action, CS_LOCAL,
|
||||
rtp_header_extensions, error_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataRecvParameters recv_params = last_recv_params_;
|
||||
RtpParametersFromMediaDescription(data, &recv_params);
|
||||
RtpParametersFromMediaDescription(data, rtp_header_extensions, &recv_params);
|
||||
if (!media_channel()->SetRecvParameters(recv_params)) {
|
||||
SafeSetError("Failed to set remote data description recv parameters.",
|
||||
error_desc);
|
||||
@ -2251,13 +2318,18 @@ bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content,
|
||||
return false;
|
||||
}
|
||||
|
||||
RtpHeaderExtensions rtp_header_extensions =
|
||||
GetFilteredRtpHeaderExtensions(data->rtp_header_extensions());
|
||||
|
||||
LOG(LS_INFO) << "Setting remote data description";
|
||||
if (!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) {
|
||||
if (!SetRtpTransportParameters(content, action, CS_REMOTE,
|
||||
rtp_header_extensions, error_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataSendParameters send_params = last_send_params_;
|
||||
RtpSendParametersFromMediaDescription<DataCodec>(data, &send_params);
|
||||
RtpSendParametersFromMediaDescription<DataCodec>(data, rtp_header_extensions,
|
||||
&send_params);
|
||||
if (!media_channel()->SetSendParameters(send_params)) {
|
||||
SafeSetError("Failed to set remote data description send parameters.",
|
||||
error_desc);
|
||||
|
||||
@ -319,13 +319,18 @@ class BaseChannel
|
||||
ContentAction action,
|
||||
std::string* error_desc) = 0;
|
||||
bool SetRtpTransportParameters(const MediaContentDescription* content,
|
||||
ContentAction action,
|
||||
ContentSource src,
|
||||
std::string* error_desc);
|
||||
ContentAction action, ContentSource src,
|
||||
const RtpHeaderExtensions& extensions, std::string* error_desc);
|
||||
bool SetRtpTransportParameters_n(const MediaContentDescription* content,
|
||||
ContentAction action,
|
||||
ContentSource src,
|
||||
std::string* error_desc);
|
||||
ContentAction action, ContentSource src,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
std::string* error_desc);
|
||||
|
||||
// Return a list of RTP header extensions with the non-encrypted extensions
|
||||
// removed depending on the current crypto_options_ and only if both the
|
||||
// non-encrypted and encrypted extension is present for the same URI.
|
||||
RtpHeaderExtensions GetFilteredRtpHeaderExtensions(
|
||||
const RtpHeaderExtensions& extensions);
|
||||
|
||||
// Helper method to get RTP Absoulute SendTime extension header id if
|
||||
// present in remote supported extensions list.
|
||||
@ -338,6 +343,7 @@ class BaseChannel
|
||||
bool SetSrtp_n(const std::vector<CryptoParams>& params,
|
||||
ContentAction action,
|
||||
ContentSource src,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
std::string* error_desc);
|
||||
bool SetRtcpMux_n(bool enable,
|
||||
ContentAction action,
|
||||
|
||||
@ -100,6 +100,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
// Use BaseChannel with PacketTransportInternal rather than
|
||||
// DtlsTransportInternal.
|
||||
RAW_PACKET_TRANSPORT = 0x20,
|
||||
GCM_CIPHER = 0x40,
|
||||
ENCRYPTED_HEADERS = 0x80,
|
||||
};
|
||||
|
||||
ChannelTest(bool verify_playout,
|
||||
@ -175,6 +177,22 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
fake_rtcp_dtls_transport1_->SetLocalCertificate(cert1);
|
||||
}
|
||||
}
|
||||
if (flags1 & ENCRYPTED_HEADERS) {
|
||||
rtc::CryptoOptions crypto_options;
|
||||
crypto_options.enable_encrypted_rtp_header_extensions = true;
|
||||
fake_rtp_dtls_transport1_->SetCryptoOptions(crypto_options);
|
||||
if (fake_rtcp_dtls_transport1_) {
|
||||
fake_rtcp_dtls_transport1_->SetCryptoOptions(crypto_options);
|
||||
}
|
||||
}
|
||||
if (flags1 & GCM_CIPHER) {
|
||||
fake_rtp_dtls_transport1_->SetSrtpCryptoSuite(
|
||||
rtc::SRTP_AEAD_AES_256_GCM);
|
||||
if (fake_rtcp_dtls_transport1_) {
|
||||
fake_rtcp_dtls_transport1_->SetSrtpCryptoSuite(
|
||||
rtc::SRTP_AEAD_AES_256_GCM);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Based on flags, create fake DTLS or raw packet transports.
|
||||
if (flags2 & RAW_PACKET_TRANSPORT) {
|
||||
@ -205,6 +223,22 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
fake_rtcp_dtls_transport2_->SetLocalCertificate(cert2);
|
||||
}
|
||||
}
|
||||
if (flags2 & ENCRYPTED_HEADERS) {
|
||||
rtc::CryptoOptions crypto_options;
|
||||
crypto_options.enable_encrypted_rtp_header_extensions = true;
|
||||
fake_rtp_dtls_transport2_->SetCryptoOptions(crypto_options);
|
||||
if (fake_rtcp_dtls_transport2_) {
|
||||
fake_rtcp_dtls_transport2_->SetCryptoOptions(crypto_options);
|
||||
}
|
||||
}
|
||||
if (flags2 & GCM_CIPHER) {
|
||||
fake_rtp_dtls_transport2_->SetSrtpCryptoSuite(
|
||||
rtc::SRTP_AEAD_AES_256_GCM);
|
||||
if (fake_rtcp_dtls_transport2_) {
|
||||
fake_rtcp_dtls_transport2_->SetSrtpCryptoSuite(
|
||||
rtc::SRTP_AEAD_AES_256_GCM);
|
||||
}
|
||||
}
|
||||
}
|
||||
channel1_.reset(
|
||||
CreateChannel(worker_thread, network_thread_, &media_engine_, ch1,
|
||||
@ -869,6 +903,183 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
||||
}
|
||||
|
||||
enum EncryptedHeaderTestScenario {
|
||||
// Offer/Answer are processed before DTLS completes.
|
||||
DEFAULT,
|
||||
// DTLS completes before any Offer/Answer have been sent.
|
||||
DTLS_BEFORE_OFFER_ANSWER,
|
||||
// DTLS completes after channel 2 has processed (remote) Offer and (local)
|
||||
// Answer.
|
||||
DTLS_AFTER_CHANNEL2_READY,
|
||||
};
|
||||
|
||||
// Test that encrypted header extensions are working and can be changed when
|
||||
// sending a new OFFER/ANSWER.
|
||||
void TestChangeEncryptedHeaderExtensions(int flags,
|
||||
EncryptedHeaderTestScenario scenario = DEFAULT) {
|
||||
RTC_CHECK(scenario == 0 || (flags & DTLS));
|
||||
struct PacketListener : public sigslot::has_slots<> {
|
||||
PacketListener() {}
|
||||
void OnReadPacket(rtc::PacketTransportInternal* transport,
|
||||
const char* data, size_t size, const rtc::PacketTime& time,
|
||||
int flags) {
|
||||
CompareHeaderExtensions(
|
||||
reinterpret_cast<const char*>(kPcmuFrameWithExtensions),
|
||||
sizeof(kPcmuFrameWithExtensions), data, size, encrypted_headers,
|
||||
false);
|
||||
}
|
||||
std::vector<int> encrypted_headers;
|
||||
} packet_listener1, packet_listener2;
|
||||
|
||||
cricket::StreamParams stream1;
|
||||
stream1.groupid = "group1";
|
||||
stream1.id = "stream1";
|
||||
stream1.ssrcs.push_back(kSsrc1);
|
||||
stream1.cname = "stream1_cname";
|
||||
|
||||
cricket::StreamParams stream2;
|
||||
stream2.groupid = "group1";
|
||||
stream2.id = "stream2";
|
||||
stream2.ssrcs.push_back(kSsrc2);
|
||||
stream2.cname = "stream2_cname";
|
||||
|
||||
// Use SRTP when testing encrypted extensions.
|
||||
int channel_flags = flags | SECURE | ENCRYPTED_HEADERS;
|
||||
// Enable SDES if channel is not using DTLS.
|
||||
int content_flags = (channel_flags & DTLS) == 0 ? SECURE : 0;
|
||||
|
||||
// kPcmuFrameWithExtensions contains RTP extension headers with ids 1-4.
|
||||
// Make sure to use URIs that are supported for encryption.
|
||||
cricket::RtpHeaderExtensions extensions1;
|
||||
extensions1.push_back(
|
||||
RtpExtension(RtpExtension::kAudioLevelUri, 10));
|
||||
extensions1.push_back(
|
||||
RtpExtension(RtpExtension::kAudioLevelUri, 1, true));
|
||||
|
||||
cricket::RtpHeaderExtensions extensions2;
|
||||
extensions2.push_back(
|
||||
RtpExtension(RtpExtension::kAudioLevelUri, 10));
|
||||
extensions2.push_back(
|
||||
RtpExtension(RtpExtension::kAudioLevelUri, 2, true));
|
||||
extensions2.push_back(
|
||||
RtpExtension(RtpExtension::kVideoRotationUri, 3));
|
||||
extensions2.push_back(
|
||||
RtpExtension(RtpExtension::kTimestampOffsetUri, 4, true));
|
||||
|
||||
// Setup a call where channel 1 send |stream1| to channel 2.
|
||||
CreateChannels(channel_flags, channel_flags);
|
||||
fake_rtp_dtls_transport1_->fake_ice_transport()->SignalReadPacket.connect(
|
||||
&packet_listener1, &PacketListener::OnReadPacket);
|
||||
fake_rtp_dtls_transport2_->fake_ice_transport()->SignalReadPacket.connect(
|
||||
&packet_listener2, &PacketListener::OnReadPacket);
|
||||
|
||||
if (scenario == DTLS_BEFORE_OFFER_ANSWER) {
|
||||
ConnectFakeTransports();
|
||||
WaitForThreads();
|
||||
}
|
||||
|
||||
typename T::Content content1;
|
||||
CreateContent(content_flags, kPcmuCodec, kH264Codec, &content1);
|
||||
content1.AddStream(stream1);
|
||||
content1.set_rtp_header_extensions(extensions1);
|
||||
EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER, NULL));
|
||||
EXPECT_TRUE(channel1_->Enable(true));
|
||||
EXPECT_EQ(1u, media_channel1_->send_streams().size());
|
||||
packet_listener1.encrypted_headers.push_back(1);
|
||||
|
||||
EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL));
|
||||
EXPECT_EQ(1u, media_channel2_->recv_streams().size());
|
||||
|
||||
// Channel 2 sends back |stream2|.
|
||||
typename T::Content content2;
|
||||
CreateContent(content_flags, kPcmuCodec, kH264Codec, &content2);
|
||||
content2.AddStream(stream2);
|
||||
content2.set_rtp_header_extensions(extensions1);
|
||||
EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER, NULL));
|
||||
EXPECT_TRUE(channel2_->Enable(true));
|
||||
EXPECT_EQ(1u, media_channel2_->send_streams().size());
|
||||
packet_listener2.encrypted_headers.push_back(1);
|
||||
|
||||
if (scenario == DTLS_AFTER_CHANNEL2_READY) {
|
||||
ConnectFakeTransports();
|
||||
WaitForThreads();
|
||||
}
|
||||
|
||||
if (scenario == DTLS_BEFORE_OFFER_ANSWER ||
|
||||
scenario == DTLS_AFTER_CHANNEL2_READY) {
|
||||
// In both scenarios with partially completed Offer/Answer, sending
|
||||
// packets from Channel 2 to Channel 1 should work.
|
||||
SendCustomRtp2(kSsrc2, 0);
|
||||
WaitForThreads();
|
||||
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_ANSWER, NULL));
|
||||
EXPECT_EQ(1u, media_channel1_->recv_streams().size());
|
||||
|
||||
if (scenario == DEFAULT) {
|
||||
ConnectFakeTransports();
|
||||
WaitForThreads();
|
||||
}
|
||||
|
||||
SendCustomRtp1(kSsrc1, 0);
|
||||
SendCustomRtp2(kSsrc2, 0);
|
||||
WaitForThreads();
|
||||
EXPECT_TRUE(CheckCustomRtp2(kSsrc1, 0));
|
||||
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
||||
|
||||
// Let channel 2 update the encrypted header extensions.
|
||||
typename T::Content content3;
|
||||
CreateContent(content_flags, kPcmuCodec, kH264Codec, &content3);
|
||||
content3.AddStream(stream2);
|
||||
content3.set_rtp_header_extensions(extensions2);
|
||||
EXPECT_TRUE(channel2_->SetLocalContent(&content3, CA_OFFER, NULL));
|
||||
ASSERT_EQ(1u, media_channel2_->send_streams().size());
|
||||
EXPECT_EQ(stream2, media_channel2_->send_streams()[0]);
|
||||
packet_listener2.encrypted_headers.clear();
|
||||
packet_listener2.encrypted_headers.push_back(2);
|
||||
packet_listener2.encrypted_headers.push_back(4);
|
||||
|
||||
EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_OFFER, NULL));
|
||||
ASSERT_EQ(1u, media_channel1_->recv_streams().size());
|
||||
EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]);
|
||||
|
||||
// Channel 1 is already sending the new encrypted extensions. These
|
||||
// can be decrypted by channel 2. Channel 2 is still sending the old
|
||||
// encrypted extensions (which can be decrypted by channel 1).
|
||||
|
||||
if (flags & DTLS) {
|
||||
// DTLS supports updating the encrypted extensions with only the OFFER
|
||||
// being processed. For SDES both the OFFER and ANSWER must have been
|
||||
// processed to update encrypted extensions, so we can't check this case.
|
||||
SendCustomRtp1(kSsrc1, 0);
|
||||
SendCustomRtp2(kSsrc2, 0);
|
||||
WaitForThreads();
|
||||
EXPECT_TRUE(CheckCustomRtp2(kSsrc1, 0));
|
||||
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
||||
}
|
||||
|
||||
// Channel 1 replies with the same extensions.
|
||||
typename T::Content content4;
|
||||
CreateContent(content_flags, kPcmuCodec, kH264Codec, &content4);
|
||||
content4.AddStream(stream1);
|
||||
content4.set_rtp_header_extensions(extensions2);
|
||||
EXPECT_TRUE(channel1_->SetLocalContent(&content4, CA_ANSWER, NULL));
|
||||
EXPECT_EQ(1u, media_channel1_->send_streams().size());
|
||||
packet_listener1.encrypted_headers.clear();
|
||||
packet_listener1.encrypted_headers.push_back(2);
|
||||
packet_listener1.encrypted_headers.push_back(4);
|
||||
|
||||
EXPECT_TRUE(channel2_->SetRemoteContent(&content4, CA_ANSWER, NULL));
|
||||
EXPECT_EQ(1u, media_channel2_->recv_streams().size());
|
||||
|
||||
SendCustomRtp1(kSsrc1, 0);
|
||||
SendCustomRtp2(kSsrc2, 0);
|
||||
WaitForThreads();
|
||||
EXPECT_TRUE(CheckCustomRtp2(kSsrc1, 0));
|
||||
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
|
||||
}
|
||||
|
||||
// Test that we only start playout and sending at the right times.
|
||||
void TestPlayoutAndSendingStates() {
|
||||
CreateChannels(0, 0);
|
||||
@ -1955,6 +2166,24 @@ class VoiceChannelDoubleThreadTest : public ChannelTest<VoiceTraits> {
|
||||
: Base(true, kPcmuFrame, kRtcpReport, NetworkIsWorker::No) {}
|
||||
};
|
||||
|
||||
class VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest
|
||||
: public ChannelTest<VoiceTraits> {
|
||||
public:
|
||||
typedef ChannelTest<VoiceTraits> Base;
|
||||
VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest()
|
||||
: Base(true, kPcmuFrameWithExtensions, kRtcpReport,
|
||||
NetworkIsWorker::Yes) {}
|
||||
};
|
||||
|
||||
class VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest
|
||||
: public ChannelTest<VoiceTraits> {
|
||||
public:
|
||||
typedef ChannelTest<VoiceTraits> Base;
|
||||
VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest()
|
||||
: Base(true, kPcmuFrameWithExtensions, kRtcpReport,
|
||||
NetworkIsWorker::No) {}
|
||||
};
|
||||
|
||||
// override to add NULL parameter
|
||||
template <>
|
||||
cricket::VideoChannel* ChannelTest<VideoTraits>::CreateChannel(
|
||||
@ -2091,6 +2320,48 @@ TEST_F(VoiceChannelSingleThreadTest, TestChangeStreamParamsInContent) {
|
||||
Base::TestChangeStreamParamsInContent();
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtls) {
|
||||
int flags = DTLS;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsScenario1) {
|
||||
int flags = DTLS;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsScenario2) {
|
||||
int flags = DTLS;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsGcm) {
|
||||
int flags = DTLS | GCM_CIPHER;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsGcmScenario1) {
|
||||
int flags = DTLS | GCM_CIPHER;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsGcmScenario2) {
|
||||
int flags = DTLS | GCM_CIPHER;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsSDES) {
|
||||
int flags = 0;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelSingleThreadTest, TestPlayoutAndSendingStates) {
|
||||
Base::TestPlayoutAndSendingStates();
|
||||
}
|
||||
@ -2408,6 +2679,48 @@ TEST_F(VoiceChannelDoubleThreadTest, TestChangeStreamParamsInContent) {
|
||||
Base::TestChangeStreamParamsInContent();
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtls) {
|
||||
int flags = DTLS;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsScenario1) {
|
||||
int flags = DTLS;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsScenario2) {
|
||||
int flags = DTLS;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsGcm) {
|
||||
int flags = DTLS | GCM_CIPHER;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsGcmScenario1) {
|
||||
int flags = DTLS | GCM_CIPHER;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_BEFORE_OFFER_ANSWER);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsDtlsGcmScenario2) {
|
||||
int flags = DTLS | GCM_CIPHER;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags, DTLS_AFTER_CHANNEL2_READY);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest,
|
||||
TestChangeEncryptedHeaderExtensionsSDES) {
|
||||
int flags = 0;
|
||||
Base::TestChangeEncryptedHeaderExtensions(flags);
|
||||
}
|
||||
|
||||
TEST_F(VoiceChannelDoubleThreadTest, TestPlayoutAndSendingStates) {
|
||||
Base::TestPlayoutAndSendingStates();
|
||||
}
|
||||
|
||||
@ -956,33 +956,70 @@ static void FindCodecsToOffer(
|
||||
static bool FindByUri(const RtpHeaderExtensions& extensions,
|
||||
const webrtc::RtpExtension& ext_to_match,
|
||||
webrtc::RtpExtension* found_extension) {
|
||||
// We assume that all URIs are given in a canonical format.
|
||||
const webrtc::RtpExtension* found =
|
||||
webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
|
||||
ext_to_match.uri);
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
if (found_extension) {
|
||||
*found_extension = *found;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FindByUriWithEncryptionPreference(
|
||||
const RtpHeaderExtensions& extensions,
|
||||
const webrtc::RtpExtension& ext_to_match, bool encryption_preference,
|
||||
webrtc::RtpExtension* found_extension) {
|
||||
const webrtc::RtpExtension* unencrypted_extension = nullptr;
|
||||
for (RtpHeaderExtensions::const_iterator it = extensions.begin();
|
||||
it != extensions.end(); ++it) {
|
||||
// We assume that all URIs are given in a canonical format.
|
||||
if (it->uri == ext_to_match.uri) {
|
||||
if (found_extension != NULL) {
|
||||
*found_extension = *it;
|
||||
if (!encryption_preference || it->encrypt) {
|
||||
if (found_extension) {
|
||||
*found_extension = *it;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
unencrypted_extension = &(*it);
|
||||
}
|
||||
}
|
||||
if (unencrypted_extension) {
|
||||
if (found_extension) {
|
||||
*found_extension = *unencrypted_extension;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterates through |offered_extensions|, adding each one to |all_extensions|
|
||||
// and |used_ids|, and resolving ID conflicts. If an offered extension has the
|
||||
// same URI as one in |all_extensions|, it will re-use the same ID and won't be
|
||||
// treated as a conflict.
|
||||
// Iterates through |offered_extensions|, adding each one to
|
||||
// |regular_extensions| (or |encrypted_extensions| if encrypted) and |used_ids|,
|
||||
// and resolving ID conflicts.
|
||||
// If an offered extension has the same URI as one in |regular_extensions| or
|
||||
// |encrypted_extensions|, it will re-use the same ID and won't be treated as
|
||||
// a conflict.
|
||||
static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
|
||||
RtpHeaderExtensions* all_extensions,
|
||||
RtpHeaderExtensions* regular_extensions,
|
||||
RtpHeaderExtensions* encrypted_extensions,
|
||||
UsedRtpHeaderExtensionIds* used_ids) {
|
||||
for (auto& extension : *offered_extensions) {
|
||||
webrtc::RtpExtension existing;
|
||||
if (FindByUri(*all_extensions, extension, &existing)) {
|
||||
if ((extension.encrypt &&
|
||||
FindByUri(*encrypted_extensions, extension, &existing)) ||
|
||||
(!extension.encrypt &&
|
||||
FindByUri(*regular_extensions, extension, &existing))) {
|
||||
extension.id = existing.id;
|
||||
} else {
|
||||
used_ids->FindAndSetIdUsed(&extension);
|
||||
all_extensions->push_back(extension);
|
||||
if (extension.encrypt) {
|
||||
encrypted_extensions->push_back(extension);
|
||||
} else {
|
||||
regular_extensions->push_back(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1008,15 +1045,47 @@ static void FindRtpHdrExtsToOffer(
|
||||
}
|
||||
}
|
||||
|
||||
static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
|
||||
RtpHeaderExtensions* all_extensions,
|
||||
UsedRtpHeaderExtensionIds* used_ids) {
|
||||
RtpHeaderExtensions encrypted_extensions;
|
||||
for (const webrtc::RtpExtension& extension : *extensions) {
|
||||
webrtc::RtpExtension existing;
|
||||
// Don't add encrypted extensions again that were already included in a
|
||||
// previous offer or regular extensions that are also included as encrypted
|
||||
// extensions.
|
||||
if (extension.encrypt ||
|
||||
!webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
|
||||
(FindByUriWithEncryptionPreference(*extensions, extension, true,
|
||||
&existing) && existing.encrypt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FindByUri(*all_extensions, extension, &existing)) {
|
||||
encrypted_extensions.push_back(existing);
|
||||
} else {
|
||||
webrtc::RtpExtension encrypted(extension);
|
||||
encrypted.encrypt = true;
|
||||
used_ids->FindAndSetIdUsed(&encrypted);
|
||||
all_extensions->push_back(encrypted);
|
||||
encrypted_extensions.push_back(encrypted);
|
||||
}
|
||||
}
|
||||
extensions->insert(extensions->end(), encrypted_extensions.begin(),
|
||||
encrypted_extensions.end());
|
||||
}
|
||||
|
||||
static void NegotiateRtpHeaderExtensions(
|
||||
const RtpHeaderExtensions& local_extensions,
|
||||
const RtpHeaderExtensions& offered_extensions,
|
||||
bool enable_encrypted_rtp_header_extensions,
|
||||
RtpHeaderExtensions* negotiated_extenstions) {
|
||||
RtpHeaderExtensions::const_iterator ours;
|
||||
for (ours = local_extensions.begin();
|
||||
ours != local_extensions.end(); ++ours) {
|
||||
webrtc::RtpExtension theirs;
|
||||
if (FindByUri(offered_extensions, *ours, &theirs)) {
|
||||
if (FindByUriWithEncryptionPreference(offered_extensions, *ours,
|
||||
enable_encrypted_rtp_header_extensions, &theirs)) {
|
||||
// We respond with their RTP header extension id.
|
||||
negotiated_extenstions->push_back(theirs);
|
||||
}
|
||||
@ -1051,6 +1120,7 @@ static bool CreateMediaContentAnswer(
|
||||
const SecurePolicy& sdes_policy,
|
||||
const CryptoParamsVec* current_cryptos,
|
||||
const RtpHeaderExtensions& local_rtp_extenstions,
|
||||
bool enable_encrypted_rtp_header_extensions,
|
||||
StreamParamsVec* current_streams,
|
||||
bool add_legacy_stream,
|
||||
bool bundle_enabled,
|
||||
@ -1062,6 +1132,7 @@ static bool CreateMediaContentAnswer(
|
||||
RtpHeaderExtensions negotiated_rtp_extensions;
|
||||
NegotiateRtpHeaderExtensions(local_rtp_extenstions,
|
||||
offer->rtp_header_extensions(),
|
||||
enable_encrypted_rtp_header_extensions,
|
||||
&negotiated_rtp_extensions);
|
||||
answer->set_rtp_header_extensions(negotiated_rtp_extensions);
|
||||
|
||||
@ -1580,7 +1651,8 @@ void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
|
||||
// All header extensions allocated from the same range to avoid potential
|
||||
// issues when using BUNDLE.
|
||||
UsedRtpHeaderExtensionIds used_ids;
|
||||
RtpHeaderExtensions all_extensions;
|
||||
RtpHeaderExtensions all_regular_extensions;
|
||||
RtpHeaderExtensions all_encrypted_extensions;
|
||||
audio_extensions->clear();
|
||||
video_extensions->clear();
|
||||
|
||||
@ -1593,22 +1665,32 @@ void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
|
||||
GetFirstAudioContentDescription(current_description);
|
||||
if (audio) {
|
||||
*audio_extensions = audio->rtp_header_extensions();
|
||||
FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
|
||||
FindAndSetRtpHdrExtUsed(audio_extensions, &all_regular_extensions,
|
||||
&all_encrypted_extensions, &used_ids);
|
||||
}
|
||||
const VideoContentDescription* video =
|
||||
GetFirstVideoContentDescription(current_description);
|
||||
if (video) {
|
||||
*video_extensions = video->rtp_header_extensions();
|
||||
FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
|
||||
FindAndSetRtpHdrExtUsed(video_extensions, &all_regular_extensions,
|
||||
&all_encrypted_extensions, &used_ids);
|
||||
}
|
||||
}
|
||||
|
||||
// Add our default RTP header extensions that are not in
|
||||
// |current_description|.
|
||||
FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
|
||||
&all_extensions, &used_ids);
|
||||
&all_regular_extensions, &used_ids);
|
||||
FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
|
||||
&all_extensions, &used_ids);
|
||||
&all_regular_extensions, &used_ids);
|
||||
// TODO(jbauch): Support adding encrypted header extensions to existing
|
||||
// sessions.
|
||||
if (enable_encrypted_rtp_header_extensions_ && !current_description) {
|
||||
AddEncryptedVersionsOfHdrExts(audio_extensions, &all_encrypted_extensions,
|
||||
&used_ids);
|
||||
AddEncryptedVersionsOfHdrExts(video_extensions, &all_encrypted_extensions,
|
||||
&used_ids);
|
||||
}
|
||||
}
|
||||
|
||||
bool MediaSessionDescriptionFactory::AddTransportOffer(
|
||||
@ -1886,6 +1968,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
|
||||
sdes_policy,
|
||||
GetCryptos(GetFirstAudioContentDescription(current_description)),
|
||||
audio_rtp_extensions_,
|
||||
enable_encrypted_rtp_header_extensions_,
|
||||
current_streams,
|
||||
add_legacy_,
|
||||
bundle_enabled,
|
||||
@ -1942,6 +2025,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
||||
sdes_policy,
|
||||
GetCryptos(GetFirstVideoContentDescription(current_description)),
|
||||
video_rtp_extensions_,
|
||||
enable_encrypted_rtp_header_extensions_,
|
||||
current_streams,
|
||||
add_legacy_,
|
||||
bundle_enabled,
|
||||
@ -2003,6 +2087,7 @@ bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
|
||||
sdes_policy,
|
||||
GetCryptos(GetFirstDataContentDescription(current_description)),
|
||||
RtpHeaderExtensions(),
|
||||
enable_encrypted_rtp_header_extensions_,
|
||||
current_streams,
|
||||
add_legacy_,
|
||||
bundle_enabled,
|
||||
|
||||
@ -475,6 +475,10 @@ class MediaSessionDescriptionFactory {
|
||||
// applications. |add_legacy_| is true per default.
|
||||
void set_add_legacy_streams(bool add_legacy) { add_legacy_ = add_legacy; }
|
||||
|
||||
void set_enable_encrypted_rtp_header_extensions(bool enable) {
|
||||
enable_encrypted_rtp_header_extensions_ = enable;
|
||||
}
|
||||
|
||||
SessionDescription* CreateOffer(
|
||||
const MediaSessionOptions& options,
|
||||
const SessionDescription* current_description) const;
|
||||
@ -574,6 +578,7 @@ class MediaSessionDescriptionFactory {
|
||||
DataCodecs data_codecs_;
|
||||
SecurePolicy secure_;
|
||||
bool add_legacy_;
|
||||
bool enable_encrypted_rtp_header_extensions_ = false;
|
||||
std::string lang_;
|
||||
const TransportDescriptionFactory* transport_desc_factory_;
|
||||
};
|
||||
|
||||
@ -112,6 +112,12 @@ static const RtpExtension kAudioRtpExtension1[] = {
|
||||
RtpExtension("http://google.com/testing/audio_something", 10),
|
||||
};
|
||||
|
||||
static const RtpExtension kAudioRtpExtensionEncrypted1[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
|
||||
RtpExtension("http://google.com/testing/audio_something", 10),
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 12, true),
|
||||
};
|
||||
|
||||
static const RtpExtension kAudioRtpExtension2[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 2),
|
||||
RtpExtension("http://google.com/testing/audio_something_else", 8),
|
||||
@ -123,15 +129,37 @@ static const RtpExtension kAudioRtpExtension3[] = {
|
||||
RtpExtension("http://google.com/testing/both_audio_and_video", 3),
|
||||
};
|
||||
|
||||
static const RtpExtension kAudioRtpExtension3ForEncryption[] = {
|
||||
RtpExtension("http://google.com/testing/audio_something", 2),
|
||||
// Use RTP extension that supports encryption.
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
|
||||
};
|
||||
|
||||
static const RtpExtension kAudioRtpExtension3ForEncryptionOffer[] = {
|
||||
RtpExtension("http://google.com/testing/audio_something", 2),
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14, true),
|
||||
};
|
||||
|
||||
static const RtpExtension kAudioRtpExtensionAnswer[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
|
||||
};
|
||||
|
||||
static const RtpExtension kAudioRtpExtensionEncryptedAnswer[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 12, true),
|
||||
};
|
||||
|
||||
static const RtpExtension kVideoRtpExtension1[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
|
||||
RtpExtension("http://google.com/testing/video_something", 13),
|
||||
};
|
||||
|
||||
static const RtpExtension kVideoRtpExtensionEncrypted1[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
|
||||
RtpExtension("http://google.com/testing/video_something", 13),
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 11, true),
|
||||
};
|
||||
|
||||
static const RtpExtension kVideoRtpExtension2[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 2),
|
||||
RtpExtension("http://google.com/testing/video_something_else", 14),
|
||||
@ -143,10 +171,20 @@ static const RtpExtension kVideoRtpExtension3[] = {
|
||||
RtpExtension("http://google.com/testing/both_audio_and_video", 5),
|
||||
};
|
||||
|
||||
static const RtpExtension kVideoRtpExtension3ForEncryption[] = {
|
||||
RtpExtension("http://google.com/testing/video_something", 4),
|
||||
// Use RTP extension that supports encryption.
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 5),
|
||||
};
|
||||
|
||||
static const RtpExtension kVideoRtpExtensionAnswer[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
|
||||
};
|
||||
|
||||
static const RtpExtension kVideoRtpExtensionEncryptedAnswer[] = {
|
||||
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 11, true),
|
||||
};
|
||||
|
||||
static const uint32_t kSimulcastParamsSsrc[] = {10, 11, 20, 21, 30, 31};
|
||||
static const uint32_t kSimSsrc[] = {10, 20, 30};
|
||||
static const uint32_t kFec1Ssrc[] = {10, 11};
|
||||
@ -1188,6 +1226,112 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestOfferAnswerWithRtpExtensions) {
|
||||
answer.get())->rtp_header_extensions());
|
||||
}
|
||||
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestOfferAnswerWithEncryptedRtpExtensionsBoth) {
|
||||
MediaSessionOptions opts;
|
||||
opts.recv_video = true;
|
||||
|
||||
f1_.set_enable_encrypted_rtp_header_extensions(true);
|
||||
f2_.set_enable_encrypted_rtp_header_extensions(true);
|
||||
|
||||
f1_.set_audio_rtp_header_extensions(
|
||||
MAKE_VECTOR(kAudioRtpExtension1));
|
||||
f1_.set_video_rtp_header_extensions(
|
||||
MAKE_VECTOR(kVideoRtpExtension1));
|
||||
f2_.set_audio_rtp_header_extensions(
|
||||
MAKE_VECTOR(kAudioRtpExtension2));
|
||||
f2_.set_video_rtp_header_extensions(
|
||||
MAKE_VECTOR(kVideoRtpExtension2));
|
||||
|
||||
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));
|
||||
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtensionEncrypted1),
|
||||
GetFirstAudioContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtensionEncrypted1),
|
||||
GetFirstVideoContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtensionEncryptedAnswer),
|
||||
GetFirstAudioContentDescription(
|
||||
answer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtensionEncryptedAnswer),
|
||||
GetFirstVideoContentDescription(
|
||||
answer.get())->rtp_header_extensions());
|
||||
}
|
||||
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestOfferAnswerWithEncryptedRtpExtensionsOffer) {
|
||||
MediaSessionOptions opts;
|
||||
opts.recv_video = true;
|
||||
|
||||
f1_.set_enable_encrypted_rtp_header_extensions(true);
|
||||
|
||||
f1_.set_audio_rtp_header_extensions(
|
||||
MAKE_VECTOR(kAudioRtpExtension1));
|
||||
f1_.set_video_rtp_header_extensions(
|
||||
MAKE_VECTOR(kVideoRtpExtension1));
|
||||
f2_.set_audio_rtp_header_extensions(
|
||||
MAKE_VECTOR(kAudioRtpExtension2));
|
||||
f2_.set_video_rtp_header_extensions(
|
||||
MAKE_VECTOR(kVideoRtpExtension2));
|
||||
|
||||
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));
|
||||
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtensionEncrypted1),
|
||||
GetFirstAudioContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtensionEncrypted1),
|
||||
GetFirstVideoContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtensionAnswer),
|
||||
GetFirstAudioContentDescription(
|
||||
answer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtensionAnswer),
|
||||
GetFirstVideoContentDescription(
|
||||
answer.get())->rtp_header_extensions());
|
||||
}
|
||||
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestOfferAnswerWithEncryptedRtpExtensionsAnswer) {
|
||||
MediaSessionOptions opts;
|
||||
opts.recv_video = true;
|
||||
|
||||
f2_.set_enable_encrypted_rtp_header_extensions(true);
|
||||
|
||||
f1_.set_audio_rtp_header_extensions(
|
||||
MAKE_VECTOR(kAudioRtpExtension1));
|
||||
f1_.set_video_rtp_header_extensions(
|
||||
MAKE_VECTOR(kVideoRtpExtension1));
|
||||
f2_.set_audio_rtp_header_extensions(
|
||||
MAKE_VECTOR(kAudioRtpExtension2));
|
||||
f2_.set_video_rtp_header_extensions(
|
||||
MAKE_VECTOR(kVideoRtpExtension2));
|
||||
|
||||
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));
|
||||
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtension1),
|
||||
GetFirstAudioContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtension1),
|
||||
GetFirstVideoContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtensionAnswer),
|
||||
GetFirstAudioContentDescription(
|
||||
answer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtensionAnswer),
|
||||
GetFirstVideoContentDescription(
|
||||
answer.get())->rtp_header_extensions());
|
||||
}
|
||||
|
||||
// Create an audio, video, data answer without legacy StreamParams.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestCreateAnswerWithoutLegacyStreams) {
|
||||
@ -2262,6 +2406,49 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtpExtensionIdReused) {
|
||||
updated_offer.get())->rtp_header_extensions());
|
||||
}
|
||||
|
||||
// Same as "RtpExtensionIdReused" above for encrypted RTP extensions.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, RtpExtensionIdReusedEncrypted) {
|
||||
MediaSessionOptions opts;
|
||||
opts.recv_audio = true;
|
||||
opts.recv_video = true;
|
||||
|
||||
f1_.set_enable_encrypted_rtp_header_extensions(true);
|
||||
f2_.set_enable_encrypted_rtp_header_extensions(true);
|
||||
|
||||
f1_.set_audio_rtp_header_extensions(
|
||||
MAKE_VECTOR(kAudioRtpExtension3ForEncryption));
|
||||
f1_.set_video_rtp_header_extensions(
|
||||
MAKE_VECTOR(kVideoRtpExtension3ForEncryption));
|
||||
|
||||
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
|
||||
|
||||
// The extensions that are shared between audio and video should use the same
|
||||
// id.
|
||||
const RtpExtension kExpectedVideoRtpExtension[] = {
|
||||
kVideoRtpExtension3ForEncryption[0],
|
||||
kAudioRtpExtension3ForEncryptionOffer[1],
|
||||
kAudioRtpExtension3ForEncryptionOffer[2],
|
||||
};
|
||||
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtension3ForEncryptionOffer),
|
||||
GetFirstAudioContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kExpectedVideoRtpExtension),
|
||||
GetFirstVideoContentDescription(
|
||||
offer.get())->rtp_header_extensions());
|
||||
|
||||
// Nothing should change when creating a new offer
|
||||
std::unique_ptr<SessionDescription> updated_offer(
|
||||
f1_.CreateOffer(opts, offer.get()));
|
||||
|
||||
EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtension3ForEncryptionOffer),
|
||||
GetFirstAudioContentDescription(
|
||||
updated_offer.get())->rtp_header_extensions());
|
||||
EXPECT_EQ(MAKE_VECTOR(kExpectedVideoRtpExtension),
|
||||
GetFirstVideoContentDescription(
|
||||
updated_offer.get())->rtp_header_extensions());
|
||||
}
|
||||
|
||||
TEST(MediaSessionDescription, CopySessionDescription) {
|
||||
SessionDescription source;
|
||||
cricket::ContentGroup group(cricket::CN_AUDIO);
|
||||
|
||||
@ -77,11 +77,17 @@ bool SrtpFilter::SetRtpParams(int send_cs,
|
||||
return false;
|
||||
}
|
||||
CreateSrtpSessions();
|
||||
if (!send_session_->SetSend(send_cs, send_key, send_key_len))
|
||||
send_session_->SetEncryptedHeaderExtensionIds(
|
||||
send_encrypted_header_extension_ids_);
|
||||
if (!send_session_->SetSend(send_cs, send_key, send_key_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!recv_session_->SetRecv(recv_cs, recv_key, recv_key_len))
|
||||
recv_session_->SetEncryptedHeaderExtensionIds(
|
||||
recv_encrypted_header_extension_ids_);
|
||||
if (!recv_session_->SetRecv(recv_cs, recv_key, recv_key_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state_ = ST_ACTIVE;
|
||||
|
||||
@ -91,6 +97,34 @@ bool SrtpFilter::SetRtpParams(int send_cs,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrtpFilter::UpdateRtpParams(int send_cs,
|
||||
const uint8_t* send_key,
|
||||
int send_key_len,
|
||||
int recv_cs,
|
||||
const uint8_t* recv_key,
|
||||
int recv_key_len) {
|
||||
if (!IsActive()) {
|
||||
LOG(LS_ERROR) << "Tried to update SRTP Params when filter is not active";
|
||||
return false;
|
||||
}
|
||||
send_session_->SetEncryptedHeaderExtensionIds(
|
||||
send_encrypted_header_extension_ids_);
|
||||
if (!send_session_->UpdateSend(send_cs, send_key, send_key_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
recv_session_->SetEncryptedHeaderExtensionIds(
|
||||
recv_encrypted_header_extension_ids_);
|
||||
if (!recv_session_->UpdateRecv(recv_cs, recv_key, recv_key_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(LS_INFO) << "SRTP updated with negotiated parameters:"
|
||||
<< " send cipher_suite " << send_cs
|
||||
<< " recv cipher_suite " << recv_cs;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function is provided separately because DTLS-SRTP behaves
|
||||
// differently in RTP/RTCP mux and non-mux modes.
|
||||
//
|
||||
@ -113,12 +147,14 @@ bool SrtpFilter::SetRtcpParams(int send_cs,
|
||||
}
|
||||
|
||||
send_rtcp_session_.reset(new SrtpSession());
|
||||
if (!send_rtcp_session_->SetRecv(send_cs, send_key, send_key_len))
|
||||
if (!send_rtcp_session_->SetRecv(send_cs, send_key, send_key_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
recv_rtcp_session_.reset(new SrtpSession());
|
||||
if (!recv_rtcp_session_->SetRecv(recv_cs, recv_key, recv_key_len))
|
||||
if (!recv_rtcp_session_->SetRecv(recv_cs, recv_key, recv_key_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(LS_INFO) << "SRTCP activated with negotiated parameters:"
|
||||
<< " send cipher_suite " << send_cs
|
||||
@ -224,6 +260,15 @@ bool SrtpFilter::IsExternalAuthActive() const {
|
||||
return send_session_->IsExternalAuthActive();
|
||||
}
|
||||
|
||||
void SrtpFilter::SetEncryptedHeaderExtensionIds(ContentSource source,
|
||||
const std::vector<int>& extension_ids) {
|
||||
if (source == CS_LOCAL) {
|
||||
recv_encrypted_header_extension_ids_ = extension_ids;
|
||||
} else {
|
||||
send_encrypted_header_extension_ids_ = extension_ids;
|
||||
}
|
||||
}
|
||||
|
||||
bool SrtpFilter::ExpectOffer(ContentSource source) {
|
||||
return ((state_ == ST_INIT) ||
|
||||
(state_ == ST_ACTIVE) ||
|
||||
@ -384,6 +429,10 @@ bool SrtpFilter::ApplyParams(const CryptoParams& send_params,
|
||||
recv_key.size()));
|
||||
if (ret) {
|
||||
CreateSrtpSessions();
|
||||
send_session_->SetEncryptedHeaderExtensionIds(
|
||||
send_encrypted_header_extension_ids_);
|
||||
recv_session_->SetEncryptedHeaderExtensionIds(
|
||||
recv_encrypted_header_extension_ids_);
|
||||
ret = (send_session_->SetSend(
|
||||
rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite),
|
||||
send_key.data(), send_key.size()) &&
|
||||
@ -456,10 +505,18 @@ bool SrtpSession::SetSend(int cs, const uint8_t* key, size_t len) {
|
||||
return SetKey(ssrc_any_outbound, cs, key, len);
|
||||
}
|
||||
|
||||
bool SrtpSession::UpdateSend(int cs, const uint8_t* key, size_t len) {
|
||||
return UpdateKey(ssrc_any_outbound, cs, key, len);
|
||||
}
|
||||
|
||||
bool SrtpSession::SetRecv(int cs, const uint8_t* key, size_t len) {
|
||||
return SetKey(ssrc_any_inbound, cs, key, len);
|
||||
}
|
||||
|
||||
bool SrtpSession::UpdateRecv(int cs, const uint8_t* key, size_t len) {
|
||||
return UpdateKey(ssrc_any_inbound, cs, key, len);
|
||||
}
|
||||
|
||||
bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
if (!session_) {
|
||||
@ -626,17 +683,9 @@ bool SrtpSession::GetSendStreamPacketIndex(void* p,
|
||||
return true;
|
||||
}
|
||||
|
||||
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: "
|
||||
<< "SRTP session already created";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Init()) {
|
||||
return false;
|
||||
}
|
||||
bool SrtpSession::DoSetKey(int type, int cs, const uint8_t* key, size_t len) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
|
||||
srtp_policy_t policy;
|
||||
memset(&policy, 0, sizeof(policy));
|
||||
@ -654,8 +703,8 @@ bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t len) {
|
||||
srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
|
||||
srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
|
||||
} else {
|
||||
LOG(LS_WARNING) << "Failed to create SRTP session: unsupported"
|
||||
<< " cipher_suite " << cs;
|
||||
LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
|
||||
<< " SRTP session: unsupported cipher_suite " << cs;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -664,14 +713,16 @@ bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t 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;
|
||||
LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "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";
|
||||
LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
|
||||
<< " SRTP session: invalid key";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -691,22 +742,66 @@ bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t len) {
|
||||
!rtc::IsGcmCryptoSuite(cs)) {
|
||||
policy.rtp.auth_type = EXTERNAL_HMAC_SHA1;
|
||||
}
|
||||
if (!encrypted_header_extension_ids_.empty()) {
|
||||
policy.enc_xtn_hdr = const_cast<int*>(&encrypted_header_extension_ids_[0]);
|
||||
policy.enc_xtn_hdr_count =
|
||||
static_cast<int>(encrypted_header_extension_ids_.size());
|
||||
}
|
||||
policy.next = nullptr;
|
||||
|
||||
int err = srtp_create(&session_, &policy);
|
||||
if (err != srtp_err_status_ok) {
|
||||
session_ = nullptr;
|
||||
LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err;
|
||||
return false;
|
||||
if (!session_) {
|
||||
int err = srtp_create(&session_, &policy);
|
||||
if (err != srtp_err_status_ok) {
|
||||
session_ = nullptr;
|
||||
LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err;
|
||||
return false;
|
||||
}
|
||||
srtp_set_user_data(session_, this);
|
||||
} else {
|
||||
int err = srtp_update(session_, &policy);
|
||||
if (err != srtp_err_status_ok) {
|
||||
LOG(LS_ERROR) << "Failed to update SRTP session, err=" << err;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
srtp_set_user_data(session_, this);
|
||||
rtp_auth_tag_len_ = policy.rtp.auth_tag_len;
|
||||
rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len;
|
||||
external_auth_active_ = (policy.rtp.auth_type == EXTERNAL_HMAC_SHA1);
|
||||
return true;
|
||||
}
|
||||
|
||||
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: "
|
||||
<< "SRTP session already created";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoSetKey(type, cs, key, len);
|
||||
}
|
||||
|
||||
bool SrtpSession::UpdateKey(int type, int cs, const uint8_t* key, size_t len) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
if (!session_) {
|
||||
LOG(LS_ERROR) << "Failed to update non-existing SRTP session";
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoSetKey(type, cs, key, len);
|
||||
}
|
||||
|
||||
void SrtpSession::SetEncryptedHeaderExtensionIds(
|
||||
const std::vector<int>& encrypted_header_extension_ids) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
encrypted_header_extension_ids_ = encrypted_header_extension_ids;
|
||||
}
|
||||
|
||||
bool SrtpSession::Init() {
|
||||
rtc::GlobalLockScope ls(&lock_);
|
||||
|
||||
|
||||
@ -76,6 +76,10 @@ class SrtpFilter {
|
||||
bool SetAnswer(const std::vector<CryptoParams>& answer_params,
|
||||
ContentSource source);
|
||||
|
||||
// Set the header extension ids that should be encrypted for the given source.
|
||||
void SetEncryptedHeaderExtensionIds(ContentSource source,
|
||||
const std::vector<int>& extension_ids);
|
||||
|
||||
// Just set up both sets of keys directly.
|
||||
// Used with DTLS-SRTP.
|
||||
bool SetRtpParams(int send_cs,
|
||||
@ -84,6 +88,12 @@ class SrtpFilter {
|
||||
int recv_cs,
|
||||
const uint8_t* recv_key,
|
||||
int recv_key_len);
|
||||
bool UpdateRtpParams(int send_cs,
|
||||
const uint8_t* send_key,
|
||||
int send_key_len,
|
||||
int recv_cs,
|
||||
const uint8_t* recv_key,
|
||||
int recv_key_len);
|
||||
bool SetRtcpParams(int send_cs,
|
||||
const uint8_t* send_key,
|
||||
int send_key_len,
|
||||
@ -177,6 +187,8 @@ class SrtpFilter {
|
||||
std::unique_ptr<SrtpSession> recv_rtcp_session_;
|
||||
CryptoParams applied_send_params_;
|
||||
CryptoParams applied_recv_params_;
|
||||
std::vector<int> send_encrypted_header_extension_ids_;
|
||||
std::vector<int> recv_encrypted_header_extension_ids_;
|
||||
};
|
||||
|
||||
// Class that wraps a libSRTP session.
|
||||
@ -188,9 +200,15 @@ 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, size_t len);
|
||||
bool UpdateSend(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, size_t len);
|
||||
bool UpdateRecv(int cs, const uint8_t* key, size_t len);
|
||||
|
||||
void SetEncryptedHeaderExtensionIds(
|
||||
const std::vector<int>& encrypted_header_extension_ids);
|
||||
|
||||
// Encrypts/signs an individual RTP/RTCP packet, in-place.
|
||||
// If an HMAC is used, this will increase the packet size.
|
||||
@ -229,7 +247,11 @@ class SrtpSession {
|
||||
static void Terminate();
|
||||
|
||||
private:
|
||||
bool DoSetKey(int type, int cs, const uint8_t* key, size_t len);
|
||||
bool SetKey(int type, int cs, const uint8_t* key, size_t len);
|
||||
bool UpdateKey(int type, int cs, const uint8_t* key, size_t len);
|
||||
bool SetEncryptedHeaderExtensionIds(int type,
|
||||
const std::vector<int>& encrypted_header_extension_ids);
|
||||
// Returns send stream current packet index from srtp db.
|
||||
bool GetSendStreamPacketIndex(void* data, int in_len, int64_t* index);
|
||||
|
||||
@ -246,6 +268,7 @@ class SrtpSession {
|
||||
int last_send_seq_num_ = -1;
|
||||
bool external_auth_active_ = false;
|
||||
bool external_auth_enabled_ = false;
|
||||
std::vector<int> encrypted_header_extension_ids_;
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(SrtpSession);
|
||||
};
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "webrtc/pc/srtpfilter.h"
|
||||
|
||||
#include "third_party/libsrtp/include/srtp.h"
|
||||
@ -193,6 +195,53 @@ class SrtpFilterTest : public testing::Test {
|
||||
EXPECT_EQ(rtcp_len, out_len);
|
||||
EXPECT_EQ(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len));
|
||||
}
|
||||
void TestProtectUnprotectHeaderEncryption(const std::string& cs1,
|
||||
const std::string& cs2,
|
||||
const std::vector<int>& encrypted_header_ids) {
|
||||
rtc::Buffer rtp_buffer(sizeof(kPcmuFrameWithExtensions) +
|
||||
rtp_auth_tag_len(cs1));
|
||||
char* rtp_packet = rtp_buffer.data<char>();
|
||||
size_t rtp_packet_size = rtp_buffer.size();
|
||||
char original_rtp_packet[sizeof(kPcmuFrameWithExtensions)];
|
||||
size_t original_rtp_packet_size = sizeof(original_rtp_packet);
|
||||
int rtp_len = sizeof(kPcmuFrameWithExtensions), out_len;
|
||||
memcpy(rtp_packet, kPcmuFrameWithExtensions, rtp_len);
|
||||
// In order to be able to run this test function multiple times we can not
|
||||
// use the same sequence number twice. Increase the sequence number by one.
|
||||
rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet) + 2,
|
||||
++sequence_number_);
|
||||
memcpy(original_rtp_packet, rtp_packet, rtp_len);
|
||||
|
||||
EXPECT_TRUE(f1_.ProtectRtp(rtp_packet, rtp_len,
|
||||
static_cast<int>(rtp_buffer.size()),
|
||||
&out_len));
|
||||
EXPECT_EQ(out_len, rtp_len + rtp_auth_tag_len(cs1));
|
||||
EXPECT_NE(0, memcmp(rtp_packet, original_rtp_packet, rtp_len));
|
||||
CompareHeaderExtensions(rtp_packet, rtp_packet_size,
|
||||
original_rtp_packet, original_rtp_packet_size,
|
||||
encrypted_header_ids, false);
|
||||
EXPECT_TRUE(f2_.UnprotectRtp(rtp_packet, out_len, &out_len));
|
||||
EXPECT_EQ(rtp_len, out_len);
|
||||
EXPECT_EQ(0, memcmp(rtp_packet, original_rtp_packet, rtp_len));
|
||||
CompareHeaderExtensions(rtp_packet, rtp_packet_size,
|
||||
original_rtp_packet, original_rtp_packet_size,
|
||||
encrypted_header_ids, true);
|
||||
|
||||
EXPECT_TRUE(f2_.ProtectRtp(rtp_packet, rtp_len,
|
||||
static_cast<int>(rtp_buffer.size()),
|
||||
&out_len));
|
||||
EXPECT_EQ(out_len, rtp_len + rtp_auth_tag_len(cs2));
|
||||
EXPECT_NE(0, memcmp(rtp_packet, original_rtp_packet, rtp_len));
|
||||
CompareHeaderExtensions(rtp_packet, rtp_packet_size,
|
||||
original_rtp_packet, original_rtp_packet_size,
|
||||
encrypted_header_ids, false);
|
||||
EXPECT_TRUE(f1_.UnprotectRtp(rtp_packet, out_len, &out_len));
|
||||
EXPECT_EQ(rtp_len, out_len);
|
||||
EXPECT_EQ(0, memcmp(rtp_packet, original_rtp_packet, rtp_len));
|
||||
CompareHeaderExtensions(rtp_packet, rtp_packet_size,
|
||||
original_rtp_packet, original_rtp_packet_size,
|
||||
encrypted_header_ids, true);
|
||||
}
|
||||
void TestProtectSetParamsDirect(bool enable_external_auth, int cs,
|
||||
const uint8_t* key1, int key1_len, const uint8_t* key2, int key2_len,
|
||||
const std::string& cs_name) {
|
||||
@ -217,6 +266,27 @@ class SrtpFilterTest : public testing::Test {
|
||||
}
|
||||
TestProtectUnprotect(cs_name, cs_name);
|
||||
}
|
||||
void TestProtectSetParamsDirectHeaderEncryption(int cs,
|
||||
const uint8_t* key1, int key1_len, const uint8_t* key2, int key2_len,
|
||||
const std::string& cs_name) {
|
||||
std::vector<int> encrypted_headers;
|
||||
encrypted_headers.push_back(1);
|
||||
// Don't encrypt header ids 2 and 3.
|
||||
encrypted_headers.push_back(4);
|
||||
EXPECT_EQ(key1_len, key2_len);
|
||||
EXPECT_EQ(cs_name, rtc::SrtpCryptoSuiteToName(cs));
|
||||
f1_.SetEncryptedHeaderExtensionIds(CS_LOCAL, encrypted_headers);
|
||||
f1_.SetEncryptedHeaderExtensionIds(CS_REMOTE, encrypted_headers);
|
||||
f2_.SetEncryptedHeaderExtensionIds(CS_LOCAL, encrypted_headers);
|
||||
f2_.SetEncryptedHeaderExtensionIds(CS_REMOTE, encrypted_headers);
|
||||
EXPECT_TRUE(f1_.SetRtpParams(cs, key1, key1_len, cs, key2, key2_len));
|
||||
EXPECT_TRUE(f2_.SetRtpParams(cs, key2, key2_len, cs, key1, key1_len));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
EXPECT_FALSE(f1_.IsExternalAuthActive());
|
||||
EXPECT_FALSE(f2_.IsExternalAuthActive());
|
||||
TestProtectUnprotectHeaderEncryption(cs_name, cs_name, encrypted_headers);
|
||||
}
|
||||
cricket::SrtpFilter f1_;
|
||||
cricket::SrtpFilter f2_;
|
||||
int sequence_number_;
|
||||
@ -619,6 +689,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_AES_CM_128_HMAC_SHA1_80) {
|
||||
CS_AES_CM_128_HMAC_SHA1_80);
|
||||
}
|
||||
|
||||
TEST_F(SrtpFilterTest,
|
||||
TestProtectSetParamsDirectHeaderEncryption_AES_CM_128_HMAC_SHA1_80) {
|
||||
TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AES128_CM_SHA1_80,
|
||||
kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen,
|
||||
CS_AES_CM_128_HMAC_SHA1_80);
|
||||
}
|
||||
|
||||
// Test directly setting the params with AES_CM_128_HMAC_SHA1_32.
|
||||
TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_AES_CM_128_HMAC_SHA1_32) {
|
||||
bool enable_external_auth = GetParam();
|
||||
@ -627,6 +704,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_AES_CM_128_HMAC_SHA1_32) {
|
||||
CS_AES_CM_128_HMAC_SHA1_32);
|
||||
}
|
||||
|
||||
TEST_F(SrtpFilterTest,
|
||||
TestProtectSetParamsDirectHeaderEncryption_AES_CM_128_HMAC_SHA1_32) {
|
||||
TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AES128_CM_SHA1_32,
|
||||
kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen,
|
||||
CS_AES_CM_128_HMAC_SHA1_32);
|
||||
}
|
||||
|
||||
// Test directly setting the params with SRTP_AEAD_AES_128_GCM.
|
||||
TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_128_GCM) {
|
||||
bool enable_external_auth = GetParam();
|
||||
@ -635,6 +719,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_128_GCM) {
|
||||
CS_AEAD_AES_128_GCM);
|
||||
}
|
||||
|
||||
TEST_F(SrtpFilterTest,
|
||||
TestProtectSetParamsDirectHeaderEncryption_SRTP_AEAD_AES_128_GCM) {
|
||||
TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AEAD_AES_128_GCM,
|
||||
kTestKeyGcm128_1, kTestKeyGcm128Len, kTestKeyGcm128_2, kTestKeyGcm128Len,
|
||||
CS_AEAD_AES_128_GCM);
|
||||
}
|
||||
|
||||
// Test directly setting the params with SRTP_AEAD_AES_256_GCM.
|
||||
TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_256_GCM) {
|
||||
bool enable_external_auth = GetParam();
|
||||
@ -643,6 +734,13 @@ TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_SRTP_AEAD_AES_256_GCM) {
|
||||
CS_AEAD_AES_256_GCM);
|
||||
}
|
||||
|
||||
TEST_F(SrtpFilterTest,
|
||||
TestProtectSetParamsDirectHeaderEncryption_SRTP_AEAD_AES_256_GCM) {
|
||||
TestProtectSetParamsDirectHeaderEncryption(rtc::SRTP_AEAD_AES_256_GCM,
|
||||
kTestKeyGcm256_1, kTestKeyGcm256Len, kTestKeyGcm256_2, kTestKeyGcm256Len,
|
||||
CS_AEAD_AES_256_GCM);
|
||||
}
|
||||
|
||||
// Run all tests both with and without external auth enabled.
|
||||
INSTANTIATE_TEST_CASE_P(ExternalAuth,
|
||||
SrtpFilterProtectSetParamsDirectTest,
|
||||
|
||||
@ -1176,7 +1176,25 @@ bool ParseExtmap(const std::string& line,
|
||||
return false;
|
||||
}
|
||||
|
||||
*extmap = RtpExtension(uri, value);
|
||||
bool encrypted = false;
|
||||
if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
|
||||
// RFC 6904
|
||||
// a=extmap:<value["/"<direction>] urn:ietf:params:rtp-hdrext:encrypt \
|
||||
// <URI> <extensionattributes>
|
||||
const size_t expected_min_fields_encrypted = expected_min_fields + 1;
|
||||
if (fields.size() < expected_min_fields_encrypted) {
|
||||
return ParseFailedExpectMinFieldNum(line, expected_min_fields_encrypted,
|
||||
error);
|
||||
}
|
||||
|
||||
encrypted = true;
|
||||
uri = fields[2];
|
||||
if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
|
||||
return ParseFailed(line, "Recursive encrypted header.", error);
|
||||
}
|
||||
}
|
||||
|
||||
*extmap = RtpExtension(uri, value, encrypted);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1433,9 +1451,13 @@ void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
|
||||
// The definitions MUST be either all session level or all media level. This
|
||||
// implementation uses all media level.
|
||||
for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
|
||||
const RtpExtension& extension = media_desc->rtp_header_extensions()[i];
|
||||
InitAttrLine(kAttributeExtmap, &os);
|
||||
os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
|
||||
<< kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
|
||||
os << kSdpDelimiterColon << extension.id;
|
||||
if (extension.encrypt) {
|
||||
os << kSdpDelimiterSpace << RtpExtension::kEncryptHeaderExtensionsUri;
|
||||
}
|
||||
os << kSdpDelimiterSpace << extension.uri;
|
||||
AddLine(os.str(), message);
|
||||
}
|
||||
|
||||
|
||||
@ -94,6 +94,9 @@ static const char kExtmap[] =
|
||||
"a=extmap:1 http://example.com/082005/ext.htm#ttime\r\n";
|
||||
static const char kExtmapWithDirectionAndAttribute[] =
|
||||
"a=extmap:1/sendrecv http://example.com/082005/ext.htm#ttime a1 a2\r\n";
|
||||
static const char kExtmapWithDirectionAndAttributeEncrypted[] =
|
||||
"a=extmap:1/sendrecv urn:ietf:params:rtp-hdrext:encrypt "
|
||||
"http://example.com/082005/ext.htm#ttime a1 a2\r\n";
|
||||
|
||||
static const uint8_t kIdentityDigest[] = {
|
||||
0x4A, 0xAD, 0xB9, 0xB1, 0x3F, 0x82, 0x18, 0x3B, 0x54, 0x02,
|
||||
@ -1213,6 +1216,7 @@ class WebRtcSdpTest : public testing::Test {
|
||||
const RtpExtension ext2 = cd2->rtp_header_extensions().at(i);
|
||||
EXPECT_EQ(ext1.uri, ext2.uri);
|
||||
EXPECT_EQ(ext1.id, ext2.id);
|
||||
EXPECT_EQ(ext1.encrypt, ext2.encrypt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1434,13 +1438,15 @@ class WebRtcSdpTest : public testing::Test {
|
||||
cricket::CONNECTIONROLE_NONE, &fingerprint))));
|
||||
}
|
||||
|
||||
void AddExtmap() {
|
||||
void AddExtmap(bool encrypted) {
|
||||
audio_desc_ = static_cast<AudioContentDescription*>(
|
||||
audio_desc_->Copy());
|
||||
video_desc_ = static_cast<VideoContentDescription*>(
|
||||
video_desc_->Copy());
|
||||
audio_desc_->AddRtpHeaderExtension(RtpExtension(kExtmapUri, kExtmapId));
|
||||
video_desc_->AddRtpHeaderExtension(RtpExtension(kExtmapUri, kExtmapId));
|
||||
audio_desc_->AddRtpHeaderExtension(
|
||||
RtpExtension(kExtmapUri, kExtmapId, encrypted));
|
||||
video_desc_->AddRtpHeaderExtension(
|
||||
RtpExtension(kExtmapUri, kExtmapId, encrypted));
|
||||
desc_.RemoveContentByName(kAudioContentName);
|
||||
desc_.RemoveContentByName(kVideoContentName);
|
||||
desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
|
||||
@ -1576,8 +1582,9 @@ class WebRtcSdpTest : public testing::Test {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestDeserializeExtmap(bool session_level, bool media_level) {
|
||||
AddExtmap();
|
||||
void TestDeserializeExtmap(bool session_level, bool media_level,
|
||||
bool encrypted) {
|
||||
AddExtmap(encrypted);
|
||||
JsepSessionDescription new_jdesc("dummy");
|
||||
ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(),
|
||||
jdesc_.session_id(),
|
||||
@ -1585,13 +1592,19 @@ class WebRtcSdpTest : public testing::Test {
|
||||
JsepSessionDescription jdesc_with_extmap("dummy");
|
||||
std::string sdp_with_extmap = kSdpString;
|
||||
if (session_level) {
|
||||
InjectAfter(kSessionTime, kExtmapWithDirectionAndAttribute,
|
||||
InjectAfter(kSessionTime,
|
||||
encrypted ? kExtmapWithDirectionAndAttributeEncrypted
|
||||
: kExtmapWithDirectionAndAttribute,
|
||||
&sdp_with_extmap);
|
||||
}
|
||||
if (media_level) {
|
||||
InjectAfter(kAttributeIcePwdVoice, kExtmapWithDirectionAndAttribute,
|
||||
InjectAfter(kAttributeIcePwdVoice,
|
||||
encrypted ? kExtmapWithDirectionAndAttributeEncrypted
|
||||
: kExtmapWithDirectionAndAttribute,
|
||||
&sdp_with_extmap);
|
||||
InjectAfter(kAttributeIcePwdVideo, kExtmapWithDirectionAndAttribute,
|
||||
InjectAfter(kAttributeIcePwdVideo,
|
||||
encrypted ? kExtmapWithDirectionAndAttributeEncrypted
|
||||
: kExtmapWithDirectionAndAttribute,
|
||||
&sdp_with_extmap);
|
||||
}
|
||||
// The extmap can't be present at the same time in both session level and
|
||||
@ -2016,7 +2029,8 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
|
||||
AddExtmap();
|
||||
bool encrypted = false;
|
||||
AddExtmap(encrypted);
|
||||
JsepSessionDescription desc_with_extmap("dummy");
|
||||
MakeDescriptionWithoutCandidates(&desc_with_extmap);
|
||||
std::string message = webrtc::SdpSerialize(desc_with_extmap, false);
|
||||
@ -2030,6 +2044,15 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
|
||||
EXPECT_EQ(sdp_with_extmap, message);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmapEncrypted) {
|
||||
bool encrypted = true;
|
||||
AddExtmap(encrypted);
|
||||
JsepSessionDescription desc_with_extmap("dummy");
|
||||
ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(),
|
||||
kSessionId, kSessionVersion));
|
||||
TestSerialize(desc_with_extmap, false);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, SerializeCandidates) {
|
||||
std::string message = webrtc::SdpSerializeCandidate(*jcandidate_);
|
||||
EXPECT_EQ(std::string(kRawCandidate), message);
|
||||
@ -2675,18 +2698,32 @@ TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsAndBandwidth) {
|
||||
EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth));
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSessionLevelExtmap) {
|
||||
TestDeserializeExtmap(true, false);
|
||||
class WebRtcSdpExtmapTest
|
||||
: public WebRtcSdpTest, public testing::WithParamInterface<bool> {
|
||||
};
|
||||
|
||||
TEST_P(WebRtcSdpExtmapTest,
|
||||
DeserializeSessionDescriptionWithSessionLevelExtmap) {
|
||||
bool encrypted = GetParam();
|
||||
TestDeserializeExtmap(true, false, encrypted);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithMediaLevelExtmap) {
|
||||
TestDeserializeExtmap(false, true);
|
||||
TEST_P(WebRtcSdpExtmapTest,
|
||||
DeserializeSessionDescriptionWithMediaLevelExtmap) {
|
||||
bool encrypted = GetParam();
|
||||
TestDeserializeExtmap(false, true, encrypted);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInvalidExtmap) {
|
||||
TestDeserializeExtmap(true, true);
|
||||
TEST_P(WebRtcSdpExtmapTest,
|
||||
DeserializeSessionDescriptionWithInvalidExtmap) {
|
||||
bool encrypted = GetParam();
|
||||
TestDeserializeExtmap(true, true, encrypted);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Encrypted,
|
||||
WebRtcSdpExtmapTest,
|
||||
::testing::Values(false, true));
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) {
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
std::string sdp = kSdpFullString;
|
||||
|
||||
@ -625,6 +625,9 @@ bool WebRtcSession::Initialize(
|
||||
webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
|
||||
}
|
||||
|
||||
webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions(
|
||||
options.crypto_options.enable_encrypted_rtp_header_extensions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -106,6 +106,10 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler,
|
||||
void SetSdesPolicy(cricket::SecurePolicy secure_policy);
|
||||
cricket::SecurePolicy SdesPolicy() const;
|
||||
|
||||
void set_enable_encrypted_rtp_header_extensions(bool enable) {
|
||||
session_desc_factory_.set_enable_encrypted_rtp_header_extensions(enable);
|
||||
}
|
||||
|
||||
sigslot::signal1<const rtc::scoped_refptr<rtc::RTCCertificate>&>
|
||||
SignalCertificateReady;
|
||||
|
||||
|
||||
@ -78,6 +78,10 @@ struct CryptoOptions {
|
||||
// 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;
|
||||
|
||||
// If set to true, encrypted RTP header extensions as defined in RFC 6904
|
||||
// will be negotiated. They will only be used if both peers support them.
|
||||
bool enable_encrypted_rtp_header_extensions = false;
|
||||
};
|
||||
|
||||
// Returns supported crypto suites, given |crypto_options|.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user