Enable End-to-End Encrypted Audio Payloads.

This change integrates the FrameDecryptorInterface and the FrameEncryptorInterface into
the audio media path. If a FrameEncryptorInterface is set on an outgoing audio RTPSender
then each outgoing audio payload will first pass through the provided FrameEncryptor which
will have a chance to modify the payload contents for the purposes of encryption.

If a FrameDecryptorInterface is set on an incoming audio RtpReceiver then each incoming
audio payload will first pass through the provided FrameDecryptor which have a chance to
modify the payload contents for the purpose of decryption.

While AEAD is supported by the FrameDecryptor/FrameEncryptor interfaces this CL does not
use it and so it is left as null.

Bug: webrtc:9681
Change-Id: Ic383a9dce280528739f9d271357c2220e0a0dccf
Reviewed-on: https://webrtc-review.googlesource.com/c/101702
Commit-Queue: Benjamin Wright <benwright@webrtc.org>
Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Emad Omara <emadomara@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25001}
This commit is contained in:
Benjamin Wright
2018-10-04 14:22:34 -07:00
committed by Commit Bot
parent aa43b7bb2f
commit 84583f6183
25 changed files with 561 additions and 63 deletions

View File

@ -446,6 +446,23 @@ if (rtc_include_tests) {
]
}
rtc_source_set("fake_frame_crypto") {
testonly = true
sources = [
"test/fake_frame_decryptor.cc",
"test/fake_frame_decryptor.h",
"test/fake_frame_encryptor.cc",
"test/fake_frame_encryptor.h",
]
deps = [
":array_view",
":libjingle_peerconnection_api",
"..:webrtc_common",
"../rtc_base:checks",
"../rtc_base:rtc_base_approved",
]
}
rtc_source_set("mock_peerconnectioninterface") {
testonly = true
sources = [

View File

@ -0,0 +1,69 @@
/*
* Copyright 2018 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 "api/test/fake_frame_decryptor.h"
#include <vector>
#include "rtc_base/checks.h"
namespace webrtc {
FakeFrameDecryptor::FakeFrameDecryptor(uint8_t fake_key,
uint8_t expected_postfix_byte)
: fake_key_(fake_key), expected_postfix_byte_(expected_postfix_byte) {}
int FakeFrameDecryptor::Decrypt(cricket::MediaType media_type,
const std::vector<uint32_t>& csrcs,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> encrypted_frame,
rtc::ArrayView<uint8_t> frame,
size_t* bytes_written) {
if (fail_decryption_) {
return 1;
}
RTC_CHECK_EQ(frame.size() + 1, encrypted_frame.size());
for (size_t i = 0; i < frame.size(); i++) {
frame[i] ^= fake_key_;
}
if (encrypted_frame[frame.size()] != expected_postfix_byte_) {
return 1;
}
return 0;
}
size_t FakeFrameDecryptor::GetMaxPlaintextByteSize(
cricket::MediaType media_type,
size_t encrypted_frame_size) {
return encrypted_frame_size - 1;
}
void FakeFrameDecryptor::SetFakeKey(uint8_t fake_key) {
fake_key_ = fake_key;
}
uint8_t FakeFrameDecryptor::GetFakeKey() const {
return fake_key_;
}
void FakeFrameDecryptor::SetExpectedPostfixByte(uint8_t expected_postfix_byte) {
expected_postfix_byte_ = expected_postfix_byte;
}
uint8_t FakeFrameDecryptor::GetExpectedPostfixByte() const {
return expected_postfix_byte_;
}
void FakeFrameDecryptor::SetFailDecryption(bool fail_decryption) {
fail_decryption_ = fail_decryption;
}
} // namespace webrtc

View File

@ -0,0 +1,62 @@
/*
* Copyright 2018 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.
*/
#ifndef API_TEST_FAKE_FRAME_DECRYPTOR_H_
#define API_TEST_FAKE_FRAME_DECRYPTOR_H_
#include <vector>
#include "api/crypto/framedecryptorinterface.h"
#include "rtc_base/refcountedobject.h"
namespace webrtc {
// The FakeFrameDecryptor is a TEST ONLY fake implementation of the
// FrameDecryptorInterface. It is constructed with a simple single digit key and
// a fixed postfix byte. This is just to validate that the core code works
// as expected.
class FakeFrameDecryptor
: public rtc::RefCountedObject<FrameDecryptorInterface> {
public:
// Provide a key (0,255) and some postfix byte (0,255) this should match the
// byte you expect from the FakeFrameEncryptor.
explicit FakeFrameDecryptor(uint8_t fake_key = 1,
uint8_t expected_postfix_byte = 255);
// FrameDecryptorInterface implementation
int Decrypt(cricket::MediaType media_type,
const std::vector<uint32_t>& csrcs,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> encrypted_frame,
rtc::ArrayView<uint8_t> frame,
size_t* bytes_written) override;
size_t GetMaxPlaintextByteSize(cricket::MediaType media_type,
size_t encrypted_frame_size) override;
void SetFakeKey(uint8_t fake_key);
uint8_t GetFakeKey() const;
void SetExpectedPostfixByte(uint8_t expected_postfix_byte);
uint8_t GetExpectedPostfixByte() const;
void SetFailDecryption(bool fail_decryption);
private:
uint8_t fake_key_ = 0;
uint8_t expected_postfix_byte_ = 0;
bool fail_decryption_ = false;
};
} // namespace webrtc
#endif // API_TEST_FAKE_FRAME_DECRYPTOR_H_

View File

@ -0,0 +1,65 @@
/*
* Copyright 2018 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 "api/test/fake_frame_encryptor.h"
#include "rtc_base/checks.h"
namespace webrtc {
FakeFrameEncryptor::FakeFrameEncryptor(uint8_t fake_key, uint8_t postfix_byte)
: fake_key_(fake_key), postfix_byte_(postfix_byte) {}
// FrameEncryptorInterface implementation
int FakeFrameEncryptor::Encrypt(cricket::MediaType media_type,
uint32_t ssrc,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> frame,
rtc::ArrayView<uint8_t> encrypted_frame,
size_t* bytes_written) {
// Useful if you want to test failure cases.
if (fail_encryption_) {
return 1;
}
RTC_CHECK_EQ(frame.size() + 1, encrypted_frame.size());
for (size_t i = 0; i < frame.size(); i++) {
encrypted_frame[i] ^= fake_key_;
}
encrypted_frame[frame.size()] = postfix_byte_;
*bytes_written = encrypted_frame.size();
return 0;
}
size_t FakeFrameEncryptor::GetMaxCiphertextByteSize(
cricket::MediaType media_type,
size_t frame_size) {
return frame_size + 1;
}
void FakeFrameEncryptor::SetFakeKey(uint8_t fake_key) {
fake_key_ = fake_key;
}
uint8_t FakeFrameEncryptor::GetFakeKey() const {
return fake_key_;
}
void FakeFrameEncryptor::SetPostfixByte(uint8_t postfix_byte) {
postfix_byte_ = postfix_byte;
}
uint8_t FakeFrameEncryptor::GetPostfixByte() const {
return postfix_byte_;
}
void FakeFrameEncryptor::SetFailEncryption(bool fail_encryption) {
fail_encryption_ = fail_encryption;
}
} // namespace webrtc

View File

@ -0,0 +1,58 @@
/*
* Copyright 2018 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.
*/
#ifndef API_TEST_FAKE_FRAME_ENCRYPTOR_H_
#define API_TEST_FAKE_FRAME_ENCRYPTOR_H_
#include "api/crypto/frameencryptorinterface.h"
#include "rtc_base/refcountedobject.h"
namespace webrtc {
// The FakeFrameEncryptor is a TEST ONLY fake implementation of the
// FrameEncryptorInterface. It is constructed with a simple single digit key and
// a fixed postfix byte. This is just to validate that the core code works
// as expected.
class FakeFrameEncryptor
: public rtc::RefCountedObject<FrameEncryptorInterface> {
public:
// Provide a key (0,255) and some postfix byte (0,255).
explicit FakeFrameEncryptor(uint8_t fake_key = 1, uint8_t postfix_byte = 255);
// FrameEncryptorInterface implementation
int Encrypt(cricket::MediaType media_type,
uint32_t ssrc,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> frame,
rtc::ArrayView<uint8_t> encrypted_frame,
size_t* bytes_written) override;
size_t GetMaxCiphertextByteSize(cricket::MediaType media_type,
size_t frame_size) override;
void SetFakeKey(uint8_t fake_key);
uint8_t GetFakeKey() const;
void SetPostfixByte(uint8_t expected_postfix_byte);
uint8_t GetPostfixByte() const;
void SetFailEncryption(bool fail_encryption);
private:
uint8_t fake_key_ = 0;
uint8_t postfix_byte_ = 0;
bool fail_encryption_ = false;
};
} // namespace webrtc
#endif // API_TEST_FAKE_FRAME_ENCRYPTOR_H_

View File

@ -76,7 +76,7 @@ std::unique_ptr<voe::ChannelReceiveProxy> CreateChannelAndProxy(
nullptr /* RtcpRttStats */, event_log, config.rtp.remote_ssrc,
config.jitter_buffer_max_packets,
config.jitter_buffer_fast_accelerate, config.decoder_factory,
config.codec_pair_id));
config.codec_pair_id, config.frame_decryptor));
}
} // namespace

View File

@ -50,10 +50,12 @@ std::unique_ptr<voe::ChannelSendProxy> CreateChannelAndProxy(
rtc::TaskQueue* worker_queue,
ProcessThread* module_process_thread,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* event_log) {
RtcEventLog* event_log,
FrameEncryptorInterface* frame_encryptor) {
return absl::make_unique<voe::ChannelSendProxy>(
absl::make_unique<voe::ChannelSend>(worker_queue, module_process_thread,
rtcp_rtt_stats, event_log));
rtcp_rtt_stats, event_log,
frame_encryptor));
}
} // namespace
@ -103,7 +105,8 @@ AudioSendStream::AudioSendStream(
CreateChannelAndProxy(worker_queue,
module_process_thread,
rtcp_rtt_stats,
event_log)) {}
event_log,
config.frame_encryptor)) {}
AudioSendStream::AudioSendStream(
const webrtc::AudioSendStream::Config& config,
@ -227,6 +230,11 @@ void AudioSendStream::ConfigureStream(
stream->timed_send_transport_adapter_.get());
}
// Enable the frame encryptor if a new frame encryptor has been provided.
if (first_time || new_config.frame_encryptor != old_config.frame_encryptor) {
channel_proxy->SetFrameEncryptor(new_config.frame_encryptor);
}
const ExtensionIds old_ids = FindExtensionIds(old_config.rtp.extensions);
const ExtensionIds new_ids = FindExtensionIds(new_config.rtp.extensions);
// Audio level indication

View File

@ -196,6 +196,7 @@ struct ConfigHelper {
EXPECT_CALL(*channel_proxy_, SetLocalSSRC(kSsrc)).Times(1);
EXPECT_CALL(*channel_proxy_, SetRTCP_CNAME(StrEq(kCName))).Times(1);
EXPECT_CALL(*channel_proxy_, SetNACKStatus(true, 10)).Times(1);
EXPECT_CALL(*channel_proxy_, SetFrameEncryptor(nullptr)).Times(1);
EXPECT_CALL(*channel_proxy_,
SetSendAudioLevelIndicationStatus(true, kAudioLevelId))
.Times(1);

View File

@ -229,7 +229,8 @@ ChannelReceive::ChannelReceive(
size_t jitter_buffer_max_packets,
bool jitter_buffer_fast_playout,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
absl::optional<AudioCodecPairId> codec_pair_id)
absl::optional<AudioCodecPairId> codec_pair_id,
FrameDecryptorInterface* frame_decryptor)
: event_log_(rtc_event_log),
rtp_receive_statistics_(
ReceiveStatistics::Create(Clock::GetRealTimeClock())),
@ -245,7 +246,8 @@ ChannelReceive::ChannelReceive(
_audioDeviceModulePtr(audio_device_module),
_transportPtr(NULL),
_outputGain(1.0f),
associated_send_channel_(nullptr) {
associated_send_channel_(nullptr),
frame_decryptor_(frame_decryptor) {
RTC_DCHECK(module_process_thread);
RTC_DCHECK(audio_device_module);
AudioCodingModule::Config acm_config;
@ -425,7 +427,38 @@ bool ChannelReceive::ReceivePacket(const uint8_t* packet,
WebRtcRTPHeader webrtc_rtp_header = {};
webrtc_rtp_header.header = header;
const size_t payload_data_length = payload_length - header.paddingLength;
size_t payload_data_length = payload_length - header.paddingLength;
// E2EE Custom Audio Frame Decryption (This is optional).
// Keep this buffer around for the lifetime of the OnReceivedPayloadData call.
rtc::Buffer decrypted_audio_payload;
if (frame_decryptor_ != nullptr) {
size_t max_plaintext_size = frame_decryptor_->GetMaxPlaintextByteSize(
cricket::MEDIA_TYPE_AUDIO, payload_length);
decrypted_audio_payload.SetSize(max_plaintext_size);
size_t bytes_written = 0;
std::vector<uint32_t> csrcs(header.arrOfCSRCs,
header.arrOfCSRCs + header.numCSRCs);
int decrypt_status = frame_decryptor_->Decrypt(
cricket::MEDIA_TYPE_AUDIO, csrcs,
/*additional_data=*/nullptr,
rtc::ArrayView<const uint8_t>(payload, payload_data_length),
decrypted_audio_payload, &bytes_written);
// In this case just interpret the failure as a silent frame.
if (decrypt_status != 0) {
bytes_written = 0;
}
// Resize the decrypted audio payload to the number of bytes actually
// written.
decrypted_audio_payload.SetSize(bytes_written);
// Update the final payload.
payload = decrypted_audio_payload.data();
payload_data_length = decrypted_audio_payload.size();
}
if (payload_data_length == 0) {
webrtc_rtp_header.frameType = kEmptyFrame;
return OnReceivedPayloadData(nullptr, 0, &webrtc_rtp_header);

View File

@ -42,6 +42,7 @@ class TimestampWrapAroundHandler;
namespace webrtc {
class AudioDeviceModule;
class FrameDecryptorInterface;
class PacketRouter;
class ProcessThread;
class RateLimiter;
@ -112,7 +113,8 @@ class ChannelReceive : public RtpData, public Transport {
size_t jitter_buffer_max_packets,
bool jitter_buffer_fast_playout,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
absl::optional<AudioCodecPairId> codec_pair_id);
absl::optional<AudioCodecPairId> codec_pair_id,
FrameDecryptorInterface* frame_decryptor);
virtual ~ChannelReceive();
void SetSink(AudioSinkInterface* sink);
@ -263,6 +265,9 @@ class ChannelReceive : public RtpData, public Transport {
PacketRouter* packet_router_ = nullptr;
rtc::ThreadChecker construction_thread_;
// E2EE Audio Frame Decryption
FrameDecryptorInterface* frame_decryptor_ = nullptr;
};
} // namespace voe

View File

@ -19,6 +19,7 @@
#include "absl/memory/memory.h"
#include "api/array_view.h"
#include "api/crypto/frameencryptorinterface.h"
#include "audio/utility/audio_frame_operations.h"
#include "call/rtp_transport_controller_send_interface.h"
#include "logging/rtc_event_log/events/rtc_event_audio_playout.h"
@ -260,6 +261,35 @@ int32_t ChannelSend::SendData(FrameType frameType,
_rtpRtcpModule->SetAudioLevel(rms_level_.Average());
}
// E2EE Custom Audio Frame Encryption (This is optional).
// Keep this buffer around for the lifetime of the send call.
rtc::Buffer encrypted_audio_payload;
if (frame_encryptor_ != nullptr) {
// TODO(benwright@webrtc.org) - Allocate enough to always encrypt inline.
// Allocate a buffer to hold the maximum possible encrypted payload.
size_t max_ciphertext_size = frame_encryptor_->GetMaxCiphertextByteSize(
cricket::MEDIA_TYPE_AUDIO, payloadSize);
encrypted_audio_payload.SetSize(max_ciphertext_size);
// Encrypt the audio payload into the buffer.
size_t bytes_written = 0;
int encrypt_status = frame_encryptor_->Encrypt(
cricket::MEDIA_TYPE_AUDIO, _rtpRtcpModule->SSRC(),
/*additional_data=*/nullptr,
rtc::ArrayView<const uint8_t>(payloadData, payloadSize),
encrypted_audio_payload, &bytes_written);
if (encrypt_status != 0) {
RTC_DLOG(LS_ERROR) << "Channel::SendData() failed encrypt audio payload: "
<< encrypt_status;
return -1;
}
// Resize the buffer to the exact number of bytes actually used.
encrypted_audio_payload.SetSize(bytes_written);
// Rewrite the payloadData and size to the new encrypted payload.
payloadData = encrypted_audio_payload.data();
payloadSize = encrypted_audio_payload.size();
}
// Push data from ACM to RTP/RTCP-module to deliver audio frame for
// packetization.
// This call will trigger Transport::SendPacket() from the RTP/RTCP module.
@ -322,7 +352,8 @@ int ChannelSend::PreferredSampleRate() const {
ChannelSend::ChannelSend(rtc::TaskQueue* encoder_queue,
ProcessThread* module_process_thread,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* rtc_event_log)
RtcEventLog* rtc_event_log,
FrameEncryptorInterface* frame_encryptor)
: event_log_(rtc_event_log),
_timeStamp(0), // This is just an offset, RTP module will add it's own
// random offset
@ -342,7 +373,8 @@ ChannelSend::ChannelSend(rtc::TaskQueue* encoder_queue,
kMaxRetransmissionWindowMs)),
use_twcc_plr_for_ana_(
webrtc::field_trial::FindFullName("UseTwccPlrForAna") == "Enabled"),
encoder_queue_(encoder_queue) {
encoder_queue_(encoder_queue),
frame_encryptor_(frame_encryptor) {
RTC_DCHECK(module_process_thread);
RTC_DCHECK(encoder_queue);
audio_coding_.reset(AudioCodingModule::Create(AudioCodingModule::Config()));
@ -949,5 +981,16 @@ int64_t ChannelSend::GetRTT() const {
return rtt;
}
void ChannelSend::SetFrameEncryptor(FrameEncryptorInterface* frame_encryptor) {
rtc::CritScope cs(&encoder_queue_lock_);
if (encoder_queue_is_active_) {
encoder_queue_->PostTask([this, frame_encryptor]() {
this->frame_encryptor_ = frame_encryptor;
});
} else {
frame_encryptor_ = frame_encryptor;
}
}
} // namespace voe
} // namespace webrtc

View File

@ -37,6 +37,7 @@ class TimestampWrapAroundHandler;
namespace webrtc {
class FrameEncryptorInterface;
class PacketRouter;
class ProcessThread;
class RateLimiter;
@ -118,7 +119,8 @@ class ChannelSend
ChannelSend(rtc::TaskQueue* encoder_queue,
ProcessThread* module_process_thread,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* rtc_event_log);
RtcEventLog* rtc_event_log,
FrameEncryptorInterface* frame_encryptor);
virtual ~ChannelSend();
@ -222,6 +224,9 @@ class ChannelSend
int64_t GetRTT() const;
// E2EE Custom Audio Frame Encryption
void SetFrameEncryptor(FrameEncryptorInterface* frame_encryptor);
private:
class ProcessAndEncodeAudioTask;
@ -290,6 +295,9 @@ class ChannelSend
rtc::CriticalSection encoder_queue_lock_;
bool encoder_queue_is_active_ RTC_GUARDED_BY(encoder_queue_lock_) = false;
rtc::TaskQueue* encoder_queue_ = nullptr;
// E2EE Audio Frame Encryption
FrameEncryptorInterface* frame_encryptor_ = nullptr;
};
} // namespace voe

View File

@ -197,5 +197,11 @@ ChannelSend* ChannelSendProxy::GetChannel() const {
return channel_.get();
}
void ChannelSendProxy::SetFrameEncryptor(
FrameEncryptorInterface* frame_encryptor) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
channel_->SetFrameEncryptor(frame_encryptor);
}
} // namespace voe
} // namespace webrtc

View File

@ -23,6 +23,7 @@
namespace webrtc {
class FrameEncryptorInterface;
class RtcpBandwidthObserver;
class RtpRtcp;
class RtpTransportControllerSendInterface;
@ -84,6 +85,9 @@ class ChannelSendProxy {
// Needed by ChannelReceiveProxy::AssociateSendChannel.
virtual ChannelSend* GetChannel() const;
// E2EE Custom Audio Frame Encryption (Optional)
virtual void SetFrameEncryptor(FrameEncryptorInterface* frame_encryptor);
private:
// Thread checkers document and lock usage of some methods on voe::Channel to
// specific threads we know about. The goal is to eventually split up

View File

@ -105,6 +105,8 @@ class MockChannelSendProxy : public voe::ChannelSendProxy {
void(float recoverable_packet_loss_rate));
MOCK_METHOD0(StartSend, void());
MOCK_METHOD0(StopSend, void());
MOCK_METHOD1(SetFrameEncryptor,
void(FrameEncryptorInterface* frame_encryptor));
};
} // namespace test
} // namespace webrtc

View File

@ -27,6 +27,7 @@
namespace webrtc {
class AudioSinkInterface;
class FrameDecryptorInterface;
class AudioReceiveStream {
public:
@ -120,6 +121,11 @@ class AudioReceiveStream {
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory;
absl::optional<AudioCodecPairId> codec_pair_id;
// An optional custom frame decryptor that allows the entire frame to be
// decrypted in whatever way the caller choses. This is not required by
// default.
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor;
};
// Reconfigure the stream according to the Configuration.

View File

@ -21,6 +21,7 @@
#include "api/audio_codecs/audio_encoder_factory.h"
#include "api/audio_codecs/audio_format.h"
#include "api/call/transport.h"
#include "api/crypto/frameencryptorinterface.h"
#include "api/rtpparameters.h"
#include "call/rtp_config.h"
#include "modules/audio_processing/include/audio_processing_statistics.h"
@ -128,6 +129,11 @@ class AudioSendStream {
// Track ID as specified during track creation.
std::string track_id;
// An optional custom frame encryptor that allows the entire frame to be
// encryptor in whatever way the caller choses. This is not required by
// default.
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor;
};
virtual ~AudioSendStream() = default;

View File

@ -15,6 +15,13 @@ namespace cricket {
VideoOptions::VideoOptions() = default;
VideoOptions::~VideoOptions() = default;
MediaChannel::MediaChannel(const MediaConfig& config)
: enable_dscp_(config.enable_dscp), network_interface_(NULL) {}
MediaChannel::MediaChannel() : enable_dscp_(false), network_interface_(NULL) {}
MediaChannel::~MediaChannel() {}
void MediaChannel::SetInterface(NetworkInterface* iface) {
rtc::CritScope cs(&network_interface_crit_);
network_interface_ = iface;
@ -30,13 +37,15 @@ rtc::DiffServCodePoint MediaChannel::PreferredDscp() const {
}
void MediaChannel::SetFrameEncryptor(
webrtc::FrameEncryptorInterface* frame_encryptor) {
frame_encryptor_ = frame_encryptor;
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
// Placeholder should be pure virtual once internal supports it.
}
void MediaChannel::SetFrameDecryptor(
webrtc::FrameDecryptorInterface* frame_decryptor) {
frame_decryptor_ = frame_decryptor;
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
// Placeholder should be pure virtual once internal supports it.
}
MediaSenderInfo::MediaSenderInfo() = default;

View File

@ -179,10 +179,9 @@ class MediaChannel : public sigslot::has_slots<> {
virtual ~NetworkInterface() {}
};
explicit MediaChannel(const MediaConfig& config)
: enable_dscp_(config.enable_dscp), network_interface_(NULL) {}
MediaChannel() : enable_dscp_(false), network_interface_(NULL) {}
~MediaChannel() override {}
explicit MediaChannel(const MediaConfig& config);
MediaChannel();
~MediaChannel() override;
// Sets the abstract interface class for sending RTP/RTCP data.
virtual void SetInterface(NetworkInterface* iface);
@ -219,13 +218,17 @@ class MediaChannel : public sigslot::has_slots<> {
// Set the frame encryptor to use on all outgoing frames. This is optional.
// This pointers lifetime is managed by the set of RtpSender it is attached
// to.
// TODO(benwright) make pure virtual once internal supports it.
virtual void SetFrameEncryptor(
webrtc::FrameEncryptorInterface* frame_encryptor);
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor);
// Set the frame decryptor to use on all incoming frames. This is optional.
// This pointers lifetimes is managed by the set of RtpReceivers it is
// attached to.
// TODO(benwright) make pure virtual once internal supports it.
virtual void SetFrameDecryptor(
webrtc::FrameDecryptorInterface* frame_decryptor);
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor);
// Base method to send packet using NetworkInterface.
bool SendPacket(rtc::CopyOnWriteBuffer* packet,
@ -281,10 +284,6 @@ class MediaChannel : public sigslot::has_slots<> {
// of network_interface_ object.
rtc::CriticalSection network_interface_crit_;
NetworkInterface* network_interface_;
protected:
webrtc::FrameEncryptorInterface* frame_encryptor_ = nullptr;
webrtc::FrameDecryptorInterface* frame_decryptor_ = nullptr;
};
// The stats information is structured as follows:

View File

@ -719,7 +719,8 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
webrtc::Call* call,
webrtc::Transport* send_transport,
const rtc::scoped_refptr<webrtc::AudioEncoderFactory>& encoder_factory,
const absl::optional<webrtc::AudioCodecPairId> codec_pair_id)
const absl::optional<webrtc::AudioCodecPairId> codec_pair_id,
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor)
: call_(call),
config_(send_transport),
send_side_bwe_with_overhead_(
@ -736,6 +737,7 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
config_.encoder_factory = encoder_factory;
config_.codec_pair_id = codec_pair_id;
config_.track_id = track_id;
config_.frame_encryptor = frame_encryptor;
rtp_parameters_.encodings[0].ssrc = ssrc;
rtp_parameters_.rtcp.cname = c_name;
rtp_parameters_.header_extensions = extensions;
@ -775,6 +777,13 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
ReconfigureAudioSendStream();
}
void SetFrameEncryptor(
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
config_.frame_encryptor = frame_encryptor;
ReconfigureAudioSendStream();
}
void SetAudioNetworkAdaptorConfig(
const absl::optional<std::string>& audio_network_adaptor_config) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
@ -1072,7 +1081,8 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream {
const std::map<int, webrtc::SdpAudioFormat>& decoder_map,
absl::optional<webrtc::AudioCodecPairId> codec_pair_id,
size_t jitter_buffer_max_packets,
bool jitter_buffer_fast_accelerate)
bool jitter_buffer_fast_accelerate,
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor)
: call_(call), config_() {
RTC_DCHECK(call);
config_.rtp.remote_ssrc = remote_ssrc;
@ -1089,6 +1099,7 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream {
config_.decoder_factory = decoder_factory;
config_.decoder_map = decoder_map;
config_.codec_pair_id = codec_pair_id;
config_.frame_decryptor = frame_decryptor;
RecreateAudioReceiveStream();
}
@ -1097,6 +1108,13 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream {
call_->DestroyAudioReceiveStream(stream_);
}
void SetFrameDecryptor(
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
config_.frame_decryptor = frame_decryptor;
RecreateAudioReceiveStream();
}
void SetLocalSsrc(uint32_t local_ssrc) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
config_.rtp.local_ssrc = local_ssrc;
@ -1745,7 +1763,7 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
ssrc, mid_, sp.cname, sp.id, send_codec_spec_, send_rtp_extensions_,
max_send_bitrate_bps_, audio_network_adaptor_config, call_, this,
engine()->encoder_factory_, codec_pair_id_);
engine()->encoder_factory_, codec_pair_id_, nullptr);
send_streams_.insert(std::make_pair(ssrc, stream));
// At this point the stream's local SSRC has been updated. If it is the first
@ -1831,7 +1849,8 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
recv_nack_enabled_, sp.stream_ids(), recv_rtp_extensions_,
call_, this, engine()->decoder_factory_, decoder_map_,
codec_pair_id_, engine()->audio_jitter_buffer_max_packets_,
engine()->audio_jitter_buffer_fast_accelerate_)));
engine()->audio_jitter_buffer_fast_accelerate_,
unsignaled_frame_decryptor_)));
recv_streams_[ssrc]->SetPlayout(playout_);
return true;
@ -1912,6 +1931,30 @@ bool WebRtcVoiceMediaChannel::CanInsertDtmf() {
return dtmf_payload_type_.has_value() && send_;
}
void WebRtcVoiceMediaChannel::SetFrameDecryptor(
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
auto matching_stream = recv_streams_.find(ssrc);
if (matching_stream != recv_streams_.end()) {
matching_stream->second->SetFrameDecryptor(frame_decryptor);
}
// Handle unsignaled frame decryptors.
if (ssrc == 0) {
unsignaled_frame_decryptor_ = frame_decryptor;
}
}
void WebRtcVoiceMediaChannel::SetFrameEncryptor(
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
auto matching_stream = send_streams_.find(ssrc);
if (matching_stream != send_streams_.end()) {
matching_stream->second->SetFrameEncryptor(frame_encryptor);
}
}
bool WebRtcVoiceMediaChannel::InsertDtmf(uint32_t ssrc,
int event,
int duration) {

View File

@ -170,6 +170,21 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
bool RemoveSendStream(uint32_t ssrc) override;
bool AddRecvStream(const StreamParams& sp) override;
bool RemoveRecvStream(uint32_t ssrc) override;
// E2EE Frame API
// Set a frame decryptor to a particular ssrc that will intercept all
// incoming audio payloads and attempt to decrypt them before forwarding the
// result.
void SetFrameDecryptor(uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
frame_decryptor) override;
// Set a frame encryptor to a particular ssrc that will intercept all
// outgoing audio payloads frames and attempt to encrypt them and forward the
// result to the packetizer.
void SetFrameEncryptor(uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
frame_encryptor) override;
// SSRC=0 will apply the new volume to current and future unsignaled streams.
bool SetOutputVolume(uint32_t ssrc, double volume) override;
@ -287,6 +302,10 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
const webrtc::AudioCodecPairId codec_pair_id_ =
webrtc::AudioCodecPairId::Create();
// Unsignaled streams have an option to have a frame decryptor set on them.
rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
unsignaled_frame_decryptor_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WebRtcVoiceMediaChannel);
};
} // namespace cricket

View File

@ -492,6 +492,7 @@ if (rtc_include_tests) {
deps = [
":peerconnection",
":rtc_pc_base",
"../api:fake_frame_crypto",
"../api:libjingle_peerconnection_api",
"../api:mock_rtp",
"../api/units:time_delta",

View File

@ -43,13 +43,17 @@ std::vector<rtc::scoped_refptr<MediaStreamInterface>> CreateStreamsFromIds(
return streams;
}
void AttachFrameDecryptorToMediaChannel(
// Attempt to attach the frame decryptor to the current media channel on the
// correct worker thread only if both the media channel exists and a ssrc has
// been allocated to the stream.
void MaybeAttachFrameDecryptorToMediaChannel(
const absl::optional<uint32_t>& ssrc,
rtc::Thread* worker_thread,
webrtc::FrameDecryptorInterface* frame_decryptor,
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor,
cricket::MediaChannel* media_channel) {
if (media_channel) {
if (media_channel && ssrc.has_value()) {
return worker_thread->Invoke<void>(RTC_FROM_HERE, [&] {
media_channel->SetFrameDecryptor(frame_decryptor);
media_channel->SetFrameDecryptor(*ssrc, frame_decryptor);
});
}
}
@ -152,8 +156,9 @@ bool AudioRtpReceiver::SetParameters(const RtpParameters& parameters) {
void AudioRtpReceiver::SetFrameDecryptor(
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
frame_decryptor_ = std::move(frame_decryptor);
AttachFrameDecryptorToMediaChannel(worker_thread_, frame_decryptor_.get(),
media_channel_);
// Attach the frame decryptor to the media channel if it exists.
MaybeAttachFrameDecryptorToMediaChannel(ssrc_, worker_thread_,
frame_decryptor_, media_channel_);
}
rtc::scoped_refptr<FrameDecryptorInterface>
@ -246,6 +251,9 @@ void AudioRtpReceiver::Reconfigure() {
if (!SetOutputVolume(track_->enabled() ? cached_volume_ : 0)) {
RTC_NOTREACHED();
}
// Reattach the frame decryptor if we were reconfigured.
MaybeAttachFrameDecryptorToMediaChannel(ssrc_, worker_thread_,
frame_decryptor_, media_channel_);
}
void AudioRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
@ -259,8 +267,6 @@ void AudioRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
void AudioRtpReceiver::SetVoiceMediaChannel(
cricket::VoiceMediaChannel* voice_media_channel) {
media_channel_ = voice_media_channel;
AttachFrameDecryptorToMediaChannel(worker_thread_, frame_decryptor_.get(),
media_channel_);
}
void AudioRtpReceiver::NotifyFirstPacketReceived() {
@ -341,8 +347,9 @@ bool VideoRtpReceiver::SetParameters(const RtpParameters& parameters) {
void VideoRtpReceiver::SetFrameDecryptor(
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
frame_decryptor_ = std::move(frame_decryptor);
AttachFrameDecryptorToMediaChannel(worker_thread_, frame_decryptor_.get(),
media_channel_);
// Attach the new frame decryptor the media channel if it exists yet.
MaybeAttachFrameDecryptorToMediaChannel(ssrc_, worker_thread_,
frame_decryptor_, media_channel_);
}
rtc::scoped_refptr<FrameDecryptorInterface>
@ -379,6 +386,9 @@ void VideoRtpReceiver::SetupMediaChannel(uint32_t ssrc) {
}
ssrc_ = ssrc;
SetSink(source_->sink());
// Attach any existing frame decryptor to the media channel.
MaybeAttachFrameDecryptorToMediaChannel(ssrc_, worker_thread_,
frame_decryptor_, media_channel_);
}
void VideoRtpReceiver::set_stream_ids(std::vector<std::string> stream_ids) {
@ -429,8 +439,6 @@ void VideoRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) {
void VideoRtpReceiver::SetVideoMediaChannel(
cricket::VideoMediaChannel* video_media_channel) {
media_channel_ = video_media_channel;
AttachFrameDecryptorToMediaChannel(worker_thread_, frame_decryptor_.get(),
media_channel_);
}
void VideoRtpReceiver::NotifyFirstPacketReceived() {

View File

@ -31,20 +31,6 @@ int GenerateUniqueId() {
return ++g_unique_id;
}
// Attaches the frame encryptor to the media channel through an invoke on a
// worker thread. This set must be done on the corresponding worker thread that
// the media channel was created on.
void AttachFrameEncryptorToMediaChannel(
rtc::Thread* worker_thread,
webrtc::FrameEncryptorInterface* frame_encryptor,
cricket::MediaChannel* media_channel) {
if (media_channel) {
return worker_thread->Invoke<void>(RTC_FROM_HERE, [&] {
media_channel->SetFrameEncryptor(frame_encryptor);
});
}
}
// Returns an true if any RtpEncodingParameters member that isn't implemented
// contains a value.
bool UnimplementedRtpEncodingParameterHasValue(
@ -78,6 +64,21 @@ bool PerSenderRtpEncodingParameterHasValue(
return false;
}
// Attempt to attach the frame decryptor to the current media channel on the
// correct worker thread only if both the media channel exists and a ssrc has
// been allocated to the stream.
void MaybeAttachFrameEncryptorToMediaChannel(
const absl::optional<uint32_t> ssrc,
rtc::Thread* worker_thread,
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor,
cricket::MediaChannel* media_channel) {
if (media_channel && ssrc.has_value()) {
return worker_thread->Invoke<void>(RTC_FROM_HERE, [&] {
media_channel->SetFrameEncryptor(*ssrc, frame_encryptor);
});
}
}
} // namespace
// Returns true if any RtpParameters member that isn't implemented contains a
@ -304,8 +305,8 @@ rtc::scoped_refptr<DtmfSenderInterface> AudioRtpSender::GetDtmfSender() const {
void AudioRtpSender::SetFrameEncryptor(
rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) {
frame_encryptor_ = std::move(frame_encryptor);
AttachFrameEncryptorToMediaChannel(worker_thread_, frame_encryptor_.get(),
media_channel_);
MaybeAttachFrameEncryptorToMediaChannel(ssrc_, worker_thread_,
frame_encryptor_, media_channel_);
}
rtc::scoped_refptr<FrameEncryptorInterface> AudioRtpSender::GetFrameEncryptor()
@ -354,6 +355,9 @@ void AudioRtpSender::SetSsrc(uint32_t ssrc) {
init_parameters_.encodings.clear();
});
}
// Each time there is an ssrc update.
MaybeAttachFrameEncryptorToMediaChannel(ssrc_, worker_thread_,
frame_encryptor_, media_channel_);
}
void AudioRtpSender::Stop() {
@ -379,8 +383,6 @@ void AudioRtpSender::Stop() {
void AudioRtpSender::SetVoiceMediaChannel(
cricket::VoiceMediaChannel* voice_media_channel) {
media_channel_ = voice_media_channel;
AttachFrameEncryptorToMediaChannel(worker_thread_, frame_encryptor_.get(),
media_channel_);
}
void AudioRtpSender::SetAudioSend() {
@ -555,8 +557,8 @@ rtc::scoped_refptr<DtmfSenderInterface> VideoRtpSender::GetDtmfSender() const {
void VideoRtpSender::SetFrameEncryptor(
rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) {
frame_encryptor_ = std::move(frame_encryptor);
AttachFrameEncryptorToMediaChannel(worker_thread_, frame_encryptor_.get(),
media_channel_);
MaybeAttachFrameEncryptorToMediaChannel(ssrc_, worker_thread_,
frame_encryptor_, media_channel_);
}
rtc::scoped_refptr<FrameEncryptorInterface> VideoRtpSender::GetFrameEncryptor()
@ -599,6 +601,8 @@ void VideoRtpSender::SetSsrc(uint32_t ssrc) {
init_parameters_.encodings.clear();
});
}
MaybeAttachFrameEncryptorToMediaChannel(ssrc_, worker_thread_,
frame_encryptor_, media_channel_);
}
void VideoRtpSender::Stop() {
@ -620,8 +624,6 @@ void VideoRtpSender::Stop() {
void VideoRtpSender::SetVideoMediaChannel(
cricket::VideoMediaChannel* video_media_channel) {
media_channel_ = video_media_channel;
AttachFrameEncryptorToMediaChannel(worker_thread_, frame_encryptor_.get(),
media_channel_);
}
void VideoRtpSender::SetVideoSend() {

View File

@ -13,6 +13,8 @@
#include <utility>
#include "api/rtpparameters.h"
#include "api/test/fake_frame_decryptor.h"
#include "api/test/fake_frame_encryptor.h"
#include "media/base/fakemediaengine.h"
#include "media/base/rtpdataengine.h"
#include "media/base/testutils.h"
@ -1411,4 +1413,26 @@ TEST_F(RtpSenderReceiverTest, TestOnDestroyedSignal) {
EXPECT_TRUE(audio_sender_destroyed_signal_fired_);
}
// Validate that the default FrameEncryptor setting is nullptr.
TEST_F(RtpSenderReceiverTest, AudioSenderCanSetFrameEncryptor) {
CreateAudioRtpSender();
rtc::scoped_refptr<FrameEncryptorInterface> fake_frame_encryptor(
new FakeFrameEncryptor());
EXPECT_EQ(nullptr, audio_rtp_sender_->GetFrameEncryptor());
audio_rtp_sender_->SetFrameEncryptor(fake_frame_encryptor);
EXPECT_EQ(fake_frame_encryptor.get(),
audio_rtp_sender_->GetFrameEncryptor().get());
}
// Validate that the default FrameEncryptor setting is nullptr.
TEST_F(RtpSenderReceiverTest, AudioReceiverCanSetFrameDecryptor) {
CreateAudioRtpReceiver();
rtc::scoped_refptr<FrameDecryptorInterface> fake_frame_decryptor(
new FakeFrameDecryptor());
EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor());
audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor);
EXPECT_EQ(fake_frame_decryptor.get(),
audio_rtp_receiver_->GetFrameDecryptor().get());
}
} // namespace webrtc