Revert of Completed the functionalities of SrtpTransport. (patchset 7 id:320001 of https://codereview.webrtc.org/2997983002/ )
Reason for revert:
This seems to be causing some video freezes. See https://bugs.chromium.org/p/webrtc/issues/detail?id=8251
Original issue's description:
> Completed the functionalities of SrtpTransport.
>
> The SrtpTransport takes the SRTP responsibilities from the BaseChannel
> and SrtpFilter. SrtpTransport is now responsible for setting the crypto
> keys, protecting and unprotecting the packets. SrtpTransport doesn't know
> if the keys are from SDES or DTLS handshake.
>
> BaseChannel is now only responsible setting the offer/answer for SDES
> or extracting the key from DtlsTransport and configuring the
> SrtpTransport.
>
> SrtpFilter is used by BaseChannel as a helper for SDES negotiation.
>
> BUG=webrtc:7013
>
> Review-Url: https://codereview.webrtc.org/2997983002
> Cr-Commit-Position: refs/heads/master@{#19636}
> Committed: https://chromium.googlesource.com/external/webrtc/+/e683c6871fef24d3ff64f085d6bc0e965f17fcf7
TBR=deadbeef@webrtc.org,pthatcher@google.com,zhihuang@webrtc.org
Not skipping CQ checks because original CL landed more than 1 days ago.
BUG=webrtc:7013
Review-Url: https://codereview.webrtc.org/3018513002
Cr-Commit-Position: refs/heads/master@{#19895}
diff --git a/p2p/base/fakepackettransport.h b/p2p/base/fakepackettransport.h
index 2af14c1..654d05b 100644
--- a/p2p/base/fakepackettransport.h
+++ b/p2p/base/fakepackettransport.h
@@ -86,8 +86,6 @@
bool GetOption(Socket::Option opt, int* value) override { return true; }
int GetError() override { return 0; }
- const CopyOnWriteBuffer* last_sent_packet() { return &last_sent_packet_; }
-
private:
void set_writable(bool writable) {
if (writable_ == writable) {
@@ -109,14 +107,12 @@
}
void SendPacketInternal(const CopyOnWriteBuffer& packet) {
- last_sent_packet_ = packet;
if (dest_) {
dest_->SignalReadPacket(dest_, packet.data<char>(), packet.size(),
CreatePacketTime(0), 0);
}
}
- CopyOnWriteBuffer last_sent_packet_;
AsyncInvoker invoker_;
std::string debug_name_;
FakePacketTransport* dest_ = nullptr;
diff --git a/pc/channel.cc b/pc/channel.cc
index 7ddf5de..3ab8ca0 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -151,22 +151,18 @@
signaling_thread_(signaling_thread),
content_name_(content_name),
rtcp_mux_required_(rtcp_mux_required),
+ rtp_transport_(
+ srtp_required
+ ? rtc::WrapUnique<webrtc::RtpTransportInternal>(
+ new webrtc::SrtpTransport(rtcp_mux_required, content_name))
+ : rtc::MakeUnique<webrtc::RtpTransport>(rtcp_mux_required)),
srtp_required_(srtp_required),
media_channel_(media_channel),
selected_candidate_pair_(nullptr) {
RTC_DCHECK(worker_thread_ == rtc::Thread::Current());
- if (srtp_required) {
- auto transport =
- rtc::MakeUnique<webrtc::SrtpTransport>(rtcp_mux_required, content_name);
- srtp_transport_ = transport.get();
- rtp_transport_ = std::move(transport);
#if defined(ENABLE_EXTERNAL_AUTH)
- srtp_transport_->EnableExternalAuth();
+ srtp_filter_.EnableExternalAuth();
#endif
- } else {
- rtp_transport_ = rtc::MakeUnique<webrtc::RtpTransport>(rtcp_mux_required);
- srtp_transport_ = nullptr;
- }
rtp_transport_->SignalReadyToSend.connect(
this, &BaseChannel::OnTransportReadyToSend);
// TODO(zstein): RtpTransport::SignalPacketReceived will probably be replaced
@@ -311,17 +307,14 @@
return;
}
- // When using DTLS-SRTP, we must reset the SrtpTransport every time the
- // DtlsTransport changes and wait until the DTLS handshake is complete to set
- // the newly negotiated parameters.
+ // When using DTLS-SRTP, we must reset the SrtpFilter every time the transport
+ // changes and wait until the DTLS handshake is complete to set the newly
+ // negotiated parameters.
if (ShouldSetupDtlsSrtp_n()) {
// Set |writable_| to false such that UpdateWritableState_w can set up
// DTLS-SRTP when |writable_| becomes true again.
writable_ = false;
- dtls_active_ = false;
- if (srtp_transport_) {
- srtp_transport_->ResetParams();
- }
+ srtp_filter_.ResetParams();
}
// If this BaseChannel doesn't require RTCP mux and we haven't fully
@@ -377,8 +370,8 @@
}
if (rtcp && new_dtls_transport) {
- RTC_CHECK(!(ShouldSetupDtlsSrtp_n() && srtp_active()))
- << "Setting RTCP for DTLS/SRTP after the DTLS is active "
+ RTC_CHECK(!(ShouldSetupDtlsSrtp_n() && srtp_filter_.IsActive()))
+ << "Setting RTCP for DTLS/SRTP after SrtpFilter is active "
<< "should never happen.";
}
@@ -529,7 +522,8 @@
// and we have had some form of connectivity.
return enabled() && IsReceiveContentDirection(remote_content_direction_) &&
IsSendContentDirection(local_content_direction_) &&
- was_ever_writable() && (srtp_active() || !ShouldSetupDtlsSrtp_n());
+ was_ever_writable() &&
+ (srtp_filter_.IsActive() || !ShouldSetupDtlsSrtp_n());
}
bool BaseChannel::SendPacket(rtc::CopyOnWriteBuffer* packet,
@@ -581,16 +575,13 @@
return;
}
- // Reset the SrtpTransport if it's not the CONNECTED state. For the CONNECTED
+ // Reset the srtp filter if it's not the CONNECTED state. For the CONNECTED
// state, setting up DTLS-SRTP context is deferred to ChannelWritable_w to
// cover other scenarios like the whole transport is writable (not just this
// TransportChannel) or when TransportChannel is attached after DTLS is
// negotiated.
if (state != DTLS_TRANSPORT_CONNECTED) {
- dtls_active_ = false;
- if (srtp_transport_) {
- srtp_transport_->ResetParams();
- }
+ srtp_filter_.ResetParams();
}
}
@@ -664,30 +655,90 @@
return false;
}
- if (!srtp_active()) {
- if (srtp_required_) {
- // The audio/video engines may attempt to send RTCP packets as soon as the
- // streams are created, so don't treat this as an error for RTCP.
- // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=6809
- if (rtcp) {
+ rtc::PacketOptions updated_options;
+ updated_options = options;
+ // Protect if needed.
+ if (srtp_filter_.IsActive()) {
+ TRACE_EVENT0("webrtc", "SRTP Encode");
+ bool res;
+ uint8_t* data = packet->data();
+ int len = static_cast<int>(packet->size());
+ if (!rtcp) {
+// If ENABLE_EXTERNAL_AUTH flag is on then packet authentication is not done
+// inside libsrtp for a RTP packet. A external HMAC module will be writing
+// a fake HMAC value. This is ONLY done for a RTP packet.
+// Socket layer will update rtp sendtime extension header if present in
+// packet with current time before updating the HMAC.
+#if !defined(ENABLE_EXTERNAL_AUTH)
+ res = srtp_filter_.ProtectRtp(data, len,
+ static_cast<int>(packet->capacity()), &len);
+#else
+ if (!srtp_filter_.IsExternalAuthActive()) {
+ res = srtp_filter_.ProtectRtp(
+ data, len, static_cast<int>(packet->capacity()), &len);
+ } else {
+ updated_options.packet_time_params.rtp_sendtime_extension_id =
+ rtp_abs_sendtime_extn_id_;
+ res = srtp_filter_.ProtectRtp(
+ data, len, static_cast<int>(packet->capacity()), &len,
+ &updated_options.packet_time_params.srtp_packet_index);
+ // If protection succeeds, let's get auth params from srtp.
+ if (res) {
+ uint8_t* auth_key = NULL;
+ int key_len;
+ res = srtp_filter_.GetRtpAuthParams(
+ &auth_key, &key_len,
+ &updated_options.packet_time_params.srtp_auth_tag_len);
+ if (res) {
+ updated_options.packet_time_params.srtp_auth_key.resize(key_len);
+ updated_options.packet_time_params.srtp_auth_key.assign(
+ auth_key, auth_key + key_len);
+ }
+ }
+ }
+#endif
+ if (!res) {
+ int seq_num = -1;
+ uint32_t ssrc = 0;
+ GetRtpSeqNum(data, len, &seq_num);
+ GetRtpSsrc(data, len, &ssrc);
+ LOG(LS_ERROR) << "Failed to protect " << content_name_
+ << " RTP packet: size=" << len << ", seqnum=" << seq_num
+ << ", SSRC=" << ssrc;
return false;
}
- // However, there shouldn't be any RTP packets sent before SRTP is set up
- // (and SetSend(true) is called).
- LOG(LS_ERROR) << "Can't send outgoing RTP packet when SRTP is inactive"
- << " and crypto is required";
- RTC_NOTREACHED();
+ } else {
+ res = srtp_filter_.ProtectRtcp(
+ data, len, static_cast<int>(packet->capacity()), &len);
+ if (!res) {
+ int type = -1;
+ GetRtcpType(data, len, &type);
+ LOG(LS_ERROR) << "Failed to protect " << content_name_
+ << " RTCP packet: size=" << len << ", type=" << type;
+ return false;
+ }
+ }
+
+ // Update the length of the packet now that we've added the auth tag.
+ packet->SetSize(len);
+ } else if (srtp_required_) {
+ // The audio/video engines may attempt to send RTCP packets as soon as the
+ // streams are created, so don't treat this as an error for RTCP.
+ // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=6809
+ if (rtcp) {
return false;
}
- // Bon voyage.
- return rtcp ? rtp_transport_->SendRtcpPacket(packet, options, PF_NORMAL)
- : rtp_transport_->SendRtpPacket(packet, options, PF_NORMAL);
+ // However, there shouldn't be any RTP packets sent before SRTP is set up
+ // (and SetSend(true) is called).
+ LOG(LS_ERROR) << "Can't send outgoing RTP packet when SRTP is inactive"
+ << " and crypto is required";
+ RTC_NOTREACHED();
+ return false;
}
- RTC_DCHECK(srtp_transport_);
- RTC_DCHECK(srtp_transport_->IsActive());
+
// Bon voyage.
- return rtcp ? srtp_transport_->SendRtcpPacket(packet, options, PF_SRTP_BYPASS)
- : srtp_transport_->SendRtpPacket(packet, options, PF_SRTP_BYPASS);
+ int flags = (secure() && secure_dtls()) ? PF_SRTP_BYPASS : PF_NORMAL;
+ return rtp_transport_->SendPacket(rtcp, packet, updated_options, flags);
}
bool BaseChannel::HandlesPayloadType(int packet_type) const {
@@ -702,7 +753,37 @@
signaling_thread()->Post(RTC_FROM_HERE, this, MSG_FIRSTPACKETRECEIVED);
}
- if (!srtp_active() && srtp_required_) {
+ // Unprotect the packet, if needed.
+ if (srtp_filter_.IsActive()) {
+ TRACE_EVENT0("webrtc", "SRTP Decode");
+ char* data = packet->data<char>();
+ int len = static_cast<int>(packet->size());
+ bool res;
+ if (!rtcp) {
+ res = srtp_filter_.UnprotectRtp(data, len, &len);
+ if (!res) {
+ int seq_num = -1;
+ uint32_t ssrc = 0;
+ GetRtpSeqNum(data, len, &seq_num);
+ GetRtpSsrc(data, len, &ssrc);
+ LOG(LS_ERROR) << "Failed to unprotect " << content_name_
+ << " RTP packet: size=" << len << ", seqnum=" << seq_num
+ << ", SSRC=" << ssrc;
+ return;
+ }
+ } else {
+ res = srtp_filter_.UnprotectRtcp(data, len, &len);
+ if (!res) {
+ int type = -1;
+ GetRtcpType(data, len, &type);
+ LOG(LS_ERROR) << "Failed to unprotect " << content_name_
+ << " RTCP packet: size=" << len << ", type=" << type;
+ return;
+ }
+ }
+
+ packet->SetSize(len);
+ } else if (srtp_required_) {
// Our session description indicates that SRTP is required, but we got a
// packet before our SRTP filter is active. This means either that
// a) we got SRTP packets before we received the SDES keys, in which case
@@ -878,37 +959,42 @@
recv_key = &server_write_key;
}
- if (rtcp) {
- if (!dtls_active()) {
- RTC_DCHECK(srtp_transport_);
- ret = srtp_transport_->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 {
- // RTCP doesn't need to call SetRtpParam because it is only used
- // to make the updated encrypted RTP header extension IDs take effect.
- ret = true;
+ 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 {
- RTC_DCHECK(srtp_transport_);
- ret = srtp_transport_->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()));
- dtls_active_ = ret;
+ 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) {
LOG(LS_WARNING) << "DTLS-SRTP key installation failed";
} else {
+ dtls_keyed_ = true;
UpdateTransportOverhead();
}
return ret;
}
void BaseChannel::MaybeSetupDtlsSrtp_n() {
- if (dtls_active()) {
+ if (srtp_filter_.IsActive()) {
return;
}
@@ -916,10 +1002,6 @@
return;
}
- if (!srtp_transport_) {
- EnableSrtpTransport_n();
- }
-
if (!SetupDtlsSrtp_n(false)) {
SignalDtlsSrtpSetupFailure_n(false);
return;
@@ -1003,24 +1085,6 @@
return true;
}
-void BaseChannel::EnableSrtpTransport_n() {
- if (srtp_transport_ == nullptr) {
- rtp_transport_->SignalReadyToSend.disconnect(this);
- rtp_transport_->SignalPacketReceived.disconnect(this);
-
- auto transport = rtc::MakeUnique<webrtc::SrtpTransport>(
- std::move(rtp_transport_), content_name_);
- srtp_transport_ = transport.get();
- rtp_transport_ = std::move(transport);
-
- rtp_transport_->SignalReadyToSend.connect(
- this, &BaseChannel::OnTransportReadyToSend);
- rtp_transport_->SignalPacketReceived.connect(
- this, &BaseChannel::OnPacketReceived);
- LOG(LS_INFO) << "Wrapping RtpTransport in SrtpTransport.";
- }
-}
-
bool BaseChannel::SetSrtp_n(const std::vector<CryptoParams>& cryptos,
ContentAction action,
ContentSource src,
@@ -1037,69 +1101,36 @@
if (!ret) {
return false;
}
-
- // If SRTP was not required, but we're setting a description that uses SDES,
- // we need to upgrade to an SrtpTransport.
- if (!srtp_transport_ && !dtls && !cryptos.empty()) {
- EnableSrtpTransport_n();
- }
- if (srtp_transport_) {
- srtp_transport_->SetEncryptedHeaderExtensionIds(src,
- encrypted_extension_ids);
- }
+ srtp_filter_.SetEncryptedHeaderExtensionIds(src, encrypted_extension_ids);
switch (action) {
case CA_OFFER:
// If DTLS is already active on the channel, we could be renegotiating
// here. We don't update the srtp filter.
if (!dtls) {
- ret = sdes_negotiator_.SetOffer(cryptos, src);
+ ret = srtp_filter_.SetOffer(cryptos, src);
}
break;
case CA_PRANSWER:
// If we're doing DTLS-SRTP, we don't want to update the filter
// with an answer, because we already have SRTP parameters.
if (!dtls) {
- ret = sdes_negotiator_.SetProvisionalAnswer(cryptos, src);
+ ret = srtp_filter_.SetProvisionalAnswer(cryptos, src);
}
break;
case CA_ANSWER:
// If we're doing DTLS-SRTP, we don't want to update the filter
// with an answer, because we already have SRTP parameters.
if (!dtls) {
- ret = sdes_negotiator_.SetAnswer(cryptos, src);
+ ret = srtp_filter_.SetAnswer(cryptos, src);
}
break;
default:
break;
}
-
- // If setting an SDES answer succeeded, apply the negotiated parameters
- // to the SRTP transport.
- if ((action == CA_PRANSWER || action == CA_ANSWER) && !dtls && ret) {
- if (sdes_negotiator_.send_cipher_suite() &&
- sdes_negotiator_.recv_cipher_suite()) {
- ret = srtp_transport_->SetRtpParams(
- *(sdes_negotiator_.send_cipher_suite()),
- sdes_negotiator_.send_key().data(),
- static_cast<int>(sdes_negotiator_.send_key().size()),
- *(sdes_negotiator_.recv_cipher_suite()),
- sdes_negotiator_.recv_key().data(),
- static_cast<int>(sdes_negotiator_.recv_key().size()));
- } else {
- LOG(LS_INFO) << "No crypto keys are provided for SDES.";
- if (action == CA_ANSWER && srtp_transport_) {
- // Explicitly reset the |srtp_transport_| if no crypto param is
- // provided in the answer. No need to call |ResetParams()| for
- // |sdes_negotiator_| because it resets the params inside |SetAnswer|.
- srtp_transport_->ResetParams();
- }
- }
- }
-
// 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_active() && rtp_dtls_transport_ &&
+ if (ret && dtls_keyed_ && rtp_dtls_transport_ &&
rtp_dtls_transport_->dtls_state() == DTLS_TRANSPORT_CONNECTED) {
bool rtcp = false;
ret = SetupDtlsSrtp_n(rtcp);
@@ -1143,6 +1174,7 @@
transport_name_.empty()
? rtp_transport_->rtp_packet_transport()->debug_name()
: transport_name_;
+ ;
LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name()
<< "; no longer need RTCP transport for " << debug_name;
if (rtp_transport_->rtcp_packet_transport()) {
@@ -1371,13 +1403,7 @@
void BaseChannel::CacheRtpAbsSendTimeHeaderExtension_n(
int rtp_abs_sendtime_extn_id) {
- if (srtp_transport_) {
- srtp_transport_->CacheRtpAbsSendTimeHeaderExtension(
- rtp_abs_sendtime_extn_id);
- } else {
- LOG(LS_WARNING) << "Trying to cache the Absolute Send Time extension id "
- "but the SRTP is not active.";
- }
+ rtp_abs_sendtime_extn_id_ = rtp_abs_sendtime_extn_id;
}
void BaseChannel::OnMessage(rtc::Message *pmsg) {
@@ -1661,9 +1687,9 @@
? kTcpOverhaed
: kUdpOverhaed;
- if (sdes_active()) {
+ if (secure()) {
int srtp_overhead = 0;
- if (srtp_transport_->GetSrtpOverhead(&srtp_overhead))
+ if (srtp_filter_.GetSrtpOverhead(&srtp_overhead))
transport_overhead_per_packet += srtp_overhead;
}
diff --git a/pc/channel.h b/pc/channel.h
index 613597b..e7871f9 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -33,6 +33,7 @@
#include "pc/mediamonitor.h"
#include "pc/mediasession.h"
#include "pc/rtcpmuxfilter.h"
+#include "pc/rtptransportinternal.h"
#include "pc/srtpfilter.h"
#include "rtc_base/asyncinvoker.h"
#include "rtc_base/asyncudpsocket.h"
@@ -43,8 +44,6 @@
namespace webrtc {
class AudioSinkInterface;
-class RtpTransportInternal;
-class SrtpTransport;
} // namespace webrtc
namespace cricket {
@@ -100,12 +99,12 @@
const std::string& transport_name() const { return transport_name_; }
bool enabled() const { return enabled_; }
- // This function returns true if we are using SDES.
- bool sdes_active() const { return sdes_negotiator_.IsActive(); }
- // The following function returns true if we are using DTLS-based keying.
- bool dtls_active() const { return dtls_active_; }
- // This function returns true if using SRTP (DTLS-based keying or SDES).
- bool srtp_active() const { return sdes_active() || dtls_active(); }
+ // This function returns true if we are using SRTP.
+ bool secure() const { return srtp_filter_.IsActive(); }
+ // The following function returns true if we are using
+ // DTLS-based keying. If you turned off SRTP later, however
+ // you could have secure() == false and dtls_secure() == true.
+ bool secure_dtls() const { return dtls_keyed_; }
bool writable() const { return writable_; }
@@ -183,6 +182,8 @@
override;
int SetOption_n(SocketType type, rtc::Socket::Option o, int val);
+ SrtpFilter* srtp_filter() { return &srtp_filter_; }
+
virtual cricket::MediaType media_type() = 0;
// This function returns true if we require SRTP for call setup.
@@ -368,8 +369,6 @@
void CacheRtpAbsSendTimeHeaderExtension_n(int rtp_abs_sendtime_extn_id);
int GetTransportOverheadPerPacket() const;
void UpdateTransportOverhead();
- // Wraps the existing RtpTransport in an SrtpTransport.
- void EnableSrtpTransport_n();
rtc::Thread* const worker_thread_;
rtc::Thread* const network_thread_;
@@ -390,16 +389,16 @@
DtlsTransportInternal* rtp_dtls_transport_ = nullptr;
DtlsTransportInternal* rtcp_dtls_transport_ = nullptr;
std::unique_ptr<webrtc::RtpTransportInternal> rtp_transport_;
- webrtc::SrtpTransport* srtp_transport_ = nullptr;
std::vector<std::pair<rtc::Socket::Option, int> > socket_options_;
std::vector<std::pair<rtc::Socket::Option, int> > rtcp_socket_options_;
- SrtpFilter sdes_negotiator_;
+ SrtpFilter srtp_filter_;
RtcpMuxFilter rtcp_mux_filter_;
bool writable_ = false;
bool was_ever_writable_ = false;
bool has_received_packet_ = false;
- bool dtls_active_ = false;
+ bool dtls_keyed_ = false;
const bool srtp_required_ = true;
+ int rtp_abs_sendtime_extn_id_ = -1;
// MediaChannel related members that should be accessed from the worker
// thread.
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index b5517da..26da453 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -577,7 +577,7 @@
// Basic sanity check.
void TestInit() {
CreateChannels(0, 0);
- EXPECT_FALSE(channel1_->srtp_active());
+ EXPECT_FALSE(channel1_->secure());
EXPECT_FALSE(media_channel1_->sending());
if (verify_playout_) {
EXPECT_FALSE(media_channel1_->playout());
@@ -892,8 +892,8 @@
EXPECT_TRUE(channel2_->SetRemoteContent(&content4, CA_ANSWER, NULL));
EXPECT_EQ(0u, media_channel2_->recv_streams().size());
- EXPECT_TRUE(channel1_->srtp_active());
- EXPECT_TRUE(channel2_->srtp_active());
+ EXPECT_TRUE(channel1_->secure());
+ EXPECT_TRUE(channel2_->secure());
SendCustomRtp2(kSsrc2, 0);
WaitForThreads();
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, 0));
@@ -1249,14 +1249,14 @@
// Test setting up a call.
void TestCallSetup() {
CreateChannels(0, 0);
- EXPECT_FALSE(channel1_->srtp_active());
+ EXPECT_FALSE(channel1_->secure());
EXPECT_TRUE(SendInitiate());
if (verify_playout_) {
EXPECT_TRUE(media_channel1_->playout());
}
EXPECT_FALSE(media_channel1_->sending());
EXPECT_TRUE(SendAccept());
- EXPECT_FALSE(channel1_->srtp_active());
+ EXPECT_FALSE(channel1_->secure());
EXPECT_TRUE(media_channel1_->sending());
EXPECT_EQ(1U, media_channel1_->codecs().size());
if (verify_playout_) {
@@ -1531,17 +1531,17 @@
bool dtls1 = !!(flags1_in & DTLS);
bool dtls2 = !!(flags2_in & DTLS);
CreateChannels(flags1, flags2);
- EXPECT_FALSE(channel1_->srtp_active());
- EXPECT_FALSE(channel2_->srtp_active());
+ EXPECT_FALSE(channel1_->secure());
+ EXPECT_FALSE(channel2_->secure());
EXPECT_TRUE(SendInitiate());
WaitForThreads();
EXPECT_TRUE(channel1_->writable());
EXPECT_TRUE(channel2_->writable());
EXPECT_TRUE(SendAccept());
- EXPECT_TRUE(channel1_->srtp_active());
- EXPECT_TRUE(channel2_->srtp_active());
- EXPECT_EQ(dtls1 && dtls2, channel1_->dtls_active());
- EXPECT_EQ(dtls1 && dtls2, channel2_->dtls_active());
+ EXPECT_TRUE(channel1_->secure());
+ EXPECT_TRUE(channel2_->secure());
+ EXPECT_EQ(dtls1 && dtls2, channel1_->secure_dtls());
+ EXPECT_EQ(dtls1 && dtls2, channel2_->secure_dtls());
SendRtp1();
SendRtp2();
SendRtcp1();
@@ -1560,12 +1560,12 @@
// Test that we properly handling SRTP negotiating down to RTP.
void SendSrtpToRtp() {
CreateChannels(SECURE, 0);
- EXPECT_FALSE(channel1_->srtp_active());
- EXPECT_FALSE(channel2_->srtp_active());
+ EXPECT_FALSE(channel1_->secure());
+ EXPECT_FALSE(channel2_->secure());
EXPECT_TRUE(SendInitiate());
EXPECT_TRUE(SendAccept());
- EXPECT_FALSE(channel1_->srtp_active());
- EXPECT_FALSE(channel2_->srtp_active());
+ EXPECT_FALSE(channel1_->secure());
+ EXPECT_FALSE(channel2_->secure());
SendRtp1();
SendRtp2();
SendRtcp1();
@@ -1590,8 +1590,8 @@
SSRC_MUX | RTCP_MUX | SECURE);
EXPECT_TRUE(SendOffer());
EXPECT_TRUE(SendProvisionalAnswer());
- EXPECT_TRUE(channel1_->srtp_active());
- EXPECT_TRUE(channel2_->srtp_active());
+ EXPECT_TRUE(channel1_->secure());
+ EXPECT_TRUE(channel2_->secure());
EXPECT_TRUE(channel1_->NeedsRtcpTransport());
EXPECT_TRUE(channel2_->NeedsRtcpTransport());
WaitForThreads(); // Wait for 'sending' flag go through network thread.
@@ -1616,8 +1616,8 @@
EXPECT_FALSE(channel2_->NeedsRtcpTransport());
EXPECT_EQ(1, rtcp_mux_activated_callbacks1_);
EXPECT_EQ(1, rtcp_mux_activated_callbacks2_);
- EXPECT_TRUE(channel1_->srtp_active());
- EXPECT_TRUE(channel2_->srtp_active());
+ EXPECT_TRUE(channel1_->secure());
+ EXPECT_TRUE(channel2_->secure());
SendCustomRtcp1(kSsrc1);
SendCustomRtp1(kSsrc1, ++sequence_number1_1);
SendCustomRtcp2(kSsrc2);
diff --git a/pc/rtptransport.cc b/pc/rtptransport.cc
index 78a2af5..2917911 100644
--- a/pc/rtptransport.cc
+++ b/pc/rtptransport.cc
@@ -76,18 +76,6 @@
return transport && transport->writable();
}
-bool RtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) {
- return SendPacket(false, packet, options, flags);
-}
-
-bool RtpTransport::SendRtcpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) {
- return SendPacket(true, packet, options, flags);
-}
-
bool RtpTransport::SendPacket(bool rtcp,
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
diff --git a/pc/rtptransport.h b/pc/rtptransport.h
index 5e1aa20..373eaa7 100644
--- a/pc/rtptransport.h
+++ b/pc/rtptransport.h
@@ -56,13 +56,10 @@
bool IsWritable(bool rtcp) const override;
- bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) override;
-
- bool SendRtcpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) override;
+ bool SendPacket(bool rtcp,
+ rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options,
+ int flags) override;
bool HandlesPayloadType(int payload_type) const override;
@@ -83,11 +80,6 @@
void MaybeSignalReadyToSend();
- bool SendPacket(bool rtcp,
- rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags);
-
void OnReadPacket(rtc::PacketTransportInternal* transport,
const char* data,
size_t len,
diff --git a/pc/rtptransportinternal.h b/pc/rtptransportinternal.h
index e589619..cd88b97 100644
--- a/pc/rtptransportinternal.h
+++ b/pc/rtptransportinternal.h
@@ -54,13 +54,10 @@
virtual bool IsWritable(bool rtcp) const = 0;
- virtual bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) = 0;
-
- virtual bool SendRtcpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) = 0;
+ virtual bool SendPacket(bool rtcp,
+ rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options,
+ int flags) = 0;
virtual bool HandlesPayloadType(int payload_type) const = 0;
diff --git a/pc/srtpfilter.cc b/pc/srtpfilter.cc
index fbea08f..c01f29b 100644
--- a/pc/srtpfilter.cc
+++ b/pc/srtpfilter.cc
@@ -17,6 +17,7 @@
#include "media/base/rtputils.h"
#include "pc/srtpsession.h"
#include "rtc_base/base64.h"
+#include "rtc_base/buffer.h"
#include "rtc_base/byteorder.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
@@ -62,6 +63,210 @@
return DoSetAnswer(answer_params, source, false);
}
+bool SrtpFilter::SetRtpParams(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 set SRTP Params when filter already active";
+ return false;
+ }
+ CreateSrtpSessions();
+ send_session_->SetEncryptedHeaderExtensionIds(
+ send_encrypted_header_extension_ids_);
+ if (!send_session_->SetSend(send_cs, send_key, send_key_len)) {
+ return false;
+ }
+
+ recv_session_->SetEncryptedHeaderExtensionIds(
+ recv_encrypted_header_extension_ids_);
+ if (!recv_session_->SetRecv(recv_cs, recv_key, recv_key_len)) {
+ return false;
+ }
+
+ state_ = ST_ACTIVE;
+
+ LOG(LS_INFO) << "SRTP activated with negotiated parameters:"
+ << " send cipher_suite " << send_cs << " recv cipher_suite "
+ << recv_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.
+//
+// - In the non-muxed case, RTP and RTCP are keyed with different
+// keys (from different DTLS handshakes), and so we need a new
+// SrtpSession.
+// - In the muxed case, they are keyed with the same keys, so
+// this function is not needed
+bool SrtpFilter::SetRtcpParams(int send_cs,
+ const uint8_t* send_key,
+ int send_key_len,
+ int recv_cs,
+ const uint8_t* recv_key,
+ int recv_key_len) {
+ // This can only be called once, but can be safely called after
+ // SetRtpParams
+ if (send_rtcp_session_ || recv_rtcp_session_) {
+ LOG(LS_ERROR) << "Tried to set SRTCP Params when filter already active";
+ return false;
+ }
+
+ send_rtcp_session_.reset(new SrtpSession());
+ 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)) {
+ return false;
+ }
+
+ LOG(LS_INFO) << "SRTCP activated with negotiated parameters:"
+ << " send cipher_suite " << send_cs << " recv cipher_suite "
+ << recv_cs;
+
+ return true;
+}
+
+bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
+ return false;
+ }
+ RTC_CHECK(send_session_);
+ return send_session_->ProtectRtp(p, in_len, max_len, out_len);
+}
+
+bool SrtpFilter::ProtectRtp(void* p,
+ int in_len,
+ int max_len,
+ int* out_len,
+ int64_t* index) {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
+ return false;
+ }
+ RTC_CHECK(send_session_);
+ return send_session_->ProtectRtp(p, in_len, max_len, out_len, index);
+}
+
+bool SrtpFilter::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active";
+ return false;
+ }
+ if (send_rtcp_session_) {
+ return send_rtcp_session_->ProtectRtcp(p, in_len, max_len, out_len);
+ } else {
+ RTC_CHECK(send_session_);
+ return send_session_->ProtectRtcp(p, in_len, max_len, out_len);
+ }
+}
+
+bool SrtpFilter::UnprotectRtp(void* p, int in_len, int* out_len) {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to UnprotectRtp: SRTP not active";
+ return false;
+ }
+ RTC_CHECK(recv_session_);
+ return recv_session_->UnprotectRtp(p, in_len, out_len);
+}
+
+bool SrtpFilter::UnprotectRtcp(void* p, int in_len, int* out_len) {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to UnprotectRtcp: SRTP not active";
+ return false;
+ }
+ if (recv_rtcp_session_) {
+ return recv_rtcp_session_->UnprotectRtcp(p, in_len, out_len);
+ } else {
+ RTC_CHECK(recv_session_);
+ return recv_session_->UnprotectRtcp(p, in_len, out_len);
+ }
+}
+
+bool SrtpFilter::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to GetRtpAuthParams: SRTP not active";
+ return false;
+ }
+
+ RTC_CHECK(send_session_);
+ return send_session_->GetRtpAuthParams(key, key_len, tag_len);
+}
+
+bool SrtpFilter::GetSrtpOverhead(int* srtp_overhead) const {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to GetSrtpOverhead: SRTP not active";
+ return false;
+ }
+
+ RTC_CHECK(send_session_);
+ *srtp_overhead = send_session_->GetSrtpOverhead();
+ return true;
+}
+
+void SrtpFilter::EnableExternalAuth() {
+ RTC_DCHECK(!IsActive());
+ external_auth_enabled_ = true;
+}
+
+bool SrtpFilter::IsExternalAuthEnabled() const {
+ return external_auth_enabled_;
+}
+
+bool SrtpFilter::IsExternalAuthActive() const {
+ if (!IsActive()) {
+ LOG(LS_WARNING) << "Failed to check IsExternalAuthActive: SRTP not active";
+ return false;
+ }
+
+ RTC_CHECK(send_session_);
+ 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) ||
@@ -119,16 +324,13 @@
CryptoParams selected_params;
if (!NegotiateParams(answer_params, &selected_params))
return false;
-
- const CryptoParams& new_send_params =
+ const CryptoParams& send_params =
(source == CS_REMOTE) ? selected_params : answer_params[0];
- const CryptoParams& new_recv_params =
+ const CryptoParams& recv_params =
(source == CS_REMOTE) ? answer_params[0] : selected_params;
- if (!ApplySendParams(new_send_params) || !ApplyRecvParams(new_recv_params)) {
+ if (!ApplyParams(send_params, recv_params)) {
return false;
}
- applied_send_params_ = new_send_params;
- applied_recv_params_ = new_recv_params;
if (final) {
offer_params_.clear();
@@ -140,6 +342,17 @@
return true;
}
+void SrtpFilter::CreateSrtpSessions() {
+ send_session_.reset(new SrtpSession());
+ applied_send_params_ = CryptoParams();
+ recv_session_.reset(new SrtpSession());
+ applied_recv_params_ = CryptoParams();
+
+ if (external_auth_enabled_) {
+ send_session_->EnableExternalAuth();
+ }
+}
+
bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params,
CryptoParams* selected_params) {
// We're processing an accept. We should have exactly one set of params,
@@ -167,76 +380,85 @@
return ret;
}
-bool SrtpFilter::ResetParams() {
- offer_params_.clear();
- applied_send_params_ = CryptoParams();
- applied_recv_params_ = CryptoParams();
- send_cipher_suite_ = rtc::Optional<int>();
- recv_cipher_suite_ = rtc::Optional<int>();
- send_key_.Clear();
- recv_key_.Clear();
- state_ = ST_INIT;
- return true;
-}
-
-bool SrtpFilter::ApplySendParams(const CryptoParams& send_params) {
+bool SrtpFilter::ApplyParams(const CryptoParams& send_params,
+ const CryptoParams& recv_params) {
+ // TODO(jiayl): Split this method to apply send and receive CryptoParams
+ // independently, so that we can skip one method when either send or receive
+ // CryptoParams is unchanged.
if (applied_send_params_.cipher_suite == send_params.cipher_suite &&
- applied_send_params_.key_params == send_params.key_params) {
- LOG(LS_INFO) << "Applying the same SRTP send parameters again. No-op.";
+ applied_send_params_.key_params == send_params.key_params &&
+ applied_recv_params_.cipher_suite == recv_params.cipher_suite &&
+ applied_recv_params_.key_params == recv_params.key_params) {
+ LOG(LS_INFO) << "Applying the same SRTP parameters again. No-op.";
// We do not want to reset the ROC if the keys are the same. So just return.
return true;
}
- send_cipher_suite_ = rtc::Optional<int>(
- rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite));
- if (send_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
+ int send_suite = rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite);
+ int recv_suite = rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite);
+ if (send_suite == rtc::SRTP_INVALID_CRYPTO_SUITE ||
+ recv_suite == rtc::SRTP_INVALID_CRYPTO_SUITE) {
LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
- << " send cipher_suite " << send_params.cipher_suite;
+ << " send cipher_suite " << send_params.cipher_suite
+ << " recv cipher_suite " << recv_params.cipher_suite;
return false;
}
int send_key_len, send_salt_len;
- if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
- &send_salt_len)) {
- LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
- << " send cipher_suite " << send_params.cipher_suite;
- return false;
- }
-
- send_key_ = rtc::Buffer(send_key_len + send_salt_len);
- return ParseKeyParams(send_params.key_params, send_key_.data(),
- send_key_.size());
-}
-
-bool SrtpFilter::ApplyRecvParams(const CryptoParams& recv_params) {
- if (applied_recv_params_.cipher_suite == recv_params.cipher_suite &&
- applied_recv_params_.key_params == recv_params.key_params) {
- LOG(LS_INFO) << "Applying the same SRTP recv parameters again. No-op.";
-
- // We do not want to reset the ROC if the keys are the same. So just return.
- return true;
- }
-
- recv_cipher_suite_ = rtc::Optional<int>(
- rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite));
- if (recv_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
- LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
- << " recv cipher_suite " << recv_params.cipher_suite;
- return false;
- }
-
int recv_key_len, recv_salt_len;
- if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
+ if (!rtc::GetSrtpKeyAndSaltLengths(send_suite, &send_key_len,
+ &send_salt_len) ||
+ !rtc::GetSrtpKeyAndSaltLengths(recv_suite, &recv_key_len,
&recv_salt_len)) {
LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
+ << " send cipher_suite " << send_params.cipher_suite
<< " recv cipher_suite " << recv_params.cipher_suite;
return false;
}
- recv_key_ = rtc::Buffer(recv_key_len + recv_salt_len);
- return ParseKeyParams(recv_params.key_params, recv_key_.data(),
- recv_key_.size());
+ // TODO(juberti): Zero these buffers after use.
+ bool ret;
+ rtc::Buffer send_key(send_key_len + send_salt_len);
+ rtc::Buffer recv_key(recv_key_len + recv_salt_len);
+ ret = (ParseKeyParams(send_params.key_params, send_key.data(),
+ send_key.size()) &&
+ ParseKeyParams(recv_params.key_params, recv_key.data(),
+ recv_key.size()));
+ if (ret) {
+ CreateSrtpSessions();
+ 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()) &&
+ recv_session_->SetRecv(
+ rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite),
+ recv_key.data(), recv_key.size()));
+ }
+ if (ret) {
+ LOG(LS_INFO) << "SRTP activated with negotiated parameters:"
+ << " send cipher_suite " << send_params.cipher_suite
+ << " recv cipher_suite " << recv_params.cipher_suite;
+ applied_send_params_ = send_params;
+ applied_recv_params_ = recv_params;
+ } else {
+ LOG(LS_WARNING) << "Failed to apply negotiated SRTP parameters";
+ }
+ return ret;
+}
+
+bool SrtpFilter::ResetParams() {
+ offer_params_.clear();
+ state_ = ST_INIT;
+ send_session_ = nullptr;
+ recv_session_ = nullptr;
+ send_rtcp_session_ = nullptr;
+ recv_rtcp_session_ = nullptr;
+ LOG(LS_INFO) << "SRTP reset to init state";
+ return true;
}
bool SrtpFilter::ParseKeyParams(const std::string& key_params,
diff --git a/pc/srtpfilter.h b/pc/srtpfilter.h
index ffdf637..7b1127e 100644
--- a/pc/srtpfilter.h
+++ b/pc/srtpfilter.h
@@ -17,11 +17,9 @@
#include <string>
#include <vector>
-#include "api/optional.h"
#include "media/base/cryptoparams.h"
#include "p2p/base/sessiondescription.h"
#include "rtc_base/basictypes.h"
-#include "rtc_base/buffer.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/criticalsection.h"
#include "rtc_base/sslstreamadapter.h"
@@ -33,10 +31,16 @@
namespace cricket {
+class SrtpSession;
+
void ShutdownSrtp();
-// A helper class used to negotiate SDES crypto params.
-// TODO(zhihuang): Find a better name for this class, like "SdesNegotiator".
+// Class to transform SRTP to/from RTP.
+// Initialize by calling SetSend with the local security params, then
+// call
+// SetRecv once the remote security params are received. At that point
+// Protect/UnprotectRt(c)p can be called to encrypt/decrypt data.
+// TODO: Figure out concurrency policy for SrtpFilter.
class SrtpFilter {
public:
enum Mode {
@@ -73,14 +77,66 @@
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,
+ const uint8_t* send_key,
+ int send_key_len,
+ 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,
+ int recv_cs,
+ const uint8_t* recv_key,
+ int recv_key_len);
+ // Encrypts/signs an individual RTP/RTCP packet, in-place.
+ // If an HMAC is used, this will increase the packet size.
+ bool ProtectRtp(void* data, int in_len, int max_len, int* out_len);
+ // Overloaded version, outputs packet index.
+ bool ProtectRtp(void* data,
+ int in_len,
+ int max_len,
+ int* out_len,
+ int64_t* index);
+ bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len);
+ // Decrypts/verifies an invidiual RTP/RTCP packet.
+ // If an HMAC is used, this will decrease the packet size.
+ bool UnprotectRtp(void* data, int in_len, int* out_len);
+ bool UnprotectRtcp(void* data, int in_len, int* out_len);
+ // Returns rtp auth params from srtp context.
+ bool GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len);
+ // Returns srtp overhead for rtp packets.
+ bool GetSrtpOverhead(int* srtp_overhead) const;
+ // If external auth is enabled, SRTP will write a dummy auth tag that
+ // then
+ // later must get replaced before the packet is sent out. Only
+ // supported for
+ // non-GCM cipher suites and can be checked through
+ // "IsExternalAuthActive"
+ // if it is actually used. This method is only valid before the RTP
+ // params
+ // have been set.
+ void EnableExternalAuth();
+ bool IsExternalAuthEnabled() const;
+ // A SRTP filter supports external creation of the auth tag if a non-GCM
+ // cipher is used. This method is only valid after the RTP params have
+ // been set.
+ bool IsExternalAuthActive() const;
+
bool ResetParams();
- rtc::Optional<int> send_cipher_suite() { return send_cipher_suite_; }
- rtc::Optional<int> recv_cipher_suite() { return recv_cipher_suite_; }
-
- const rtc::Buffer& send_key() { return send_key_; }
- const rtc::Buffer& recv_key() { return recv_key_; }
-
protected:
bool ExpectOffer(ContentSource source);
@@ -93,18 +149,17 @@
ContentSource source,
bool final);
+ void CreateSrtpSessions();
bool NegotiateParams(const std::vector<CryptoParams>& answer_params,
CryptoParams* selected_params);
- private:
- bool ApplySendParams(const CryptoParams& send_params);
-
- bool ApplyRecvParams(const CryptoParams& recv_params);
-
+ bool ApplyParams(const CryptoParams& send_params,
+ const CryptoParams& recv_params);
static bool ParseKeyParams(const std::string& params,
uint8_t* key,
size_t len);
+ private:
enum State {
ST_INIT, // SRTP filter unused.
ST_SENTOFFER, // Offer with SRTP parameters sent.
@@ -129,13 +184,16 @@
ST_RECEIVEDPRANSWER
};
State state_ = ST_INIT;
+ bool external_auth_enabled_ = false;
std::vector<CryptoParams> offer_params_;
+ std::unique_ptr<SrtpSession> send_session_;
+ std::unique_ptr<SrtpSession> recv_session_;
+ std::unique_ptr<SrtpSession> send_rtcp_session_;
+ std::unique_ptr<SrtpSession> recv_rtcp_session_;
CryptoParams applied_send_params_;
CryptoParams applied_recv_params_;
- rtc::Optional<int> send_cipher_suite_;
- rtc::Optional<int> recv_cipher_suite_;
- rtc::Buffer send_key_;
- rtc::Buffer recv_key_;
+ std::vector<int> send_encrypted_header_extension_ids_;
+ std::vector<int> recv_encrypted_header_extension_ids_;
};
} // namespace cricket
diff --git a/pc/srtpfilter_unittest.cc b/pc/srtpfilter_unittest.cc
index a71762c..75a4e39 100644
--- a/pc/srtpfilter_unittest.cc
+++ b/pc/srtpfilter_unittest.cc
@@ -13,7 +13,14 @@
#include "pc/srtpfilter.h"
#include "media/base/cryptoparams.h"
+#include "media/base/fakertp.h"
+#include "p2p/base/sessiondescription.h"
+#include "pc/srtptestutil.h"
+#include "rtc_base/buffer.h"
+#include "rtc_base/byteorder.h"
+#include "rtc_base/constructormagic.h"
#include "rtc_base/gunit.h"
+#include "rtc_base/thread.h"
using cricket::CryptoParams;
using cricket::CS_LOCAL;
@@ -21,6 +28,14 @@
namespace rtc {
+static const uint8_t kTestKeyGcm128_1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ12";
+static const uint8_t kTestKeyGcm128_2[] = "21ZYXWVUTSRQPONMLKJIHGFEDCBA";
+static const int kTestKeyGcm128Len = 28; // 128 bits key + 96 bits salt.
+static const uint8_t kTestKeyGcm256_1[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr";
+static const uint8_t kTestKeyGcm256_2[] =
+ "rqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";
+static const int kTestKeyGcm256Len = 44; // 256 bits key + 96 bits salt.
static const std::string kTestKeyParams1 =
"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz";
static const std::string kTestKeyParams2 =
@@ -52,13 +67,14 @@
class SrtpFilterTest : public testing::Test {
protected:
- SrtpFilterTest() {}
+ SrtpFilterTest()
+ // Need to initialize |sequence_number_|, the value does not matter.
+ : sequence_number_(1) {}
static std::vector<CryptoParams> MakeVector(const CryptoParams& params) {
std::vector<CryptoParams> vec;
vec.push_back(params);
return vec;
}
-
void TestSetParams(const std::vector<CryptoParams>& params1,
const std::vector<CryptoParams>& params2) {
EXPECT_TRUE(f1_.SetOffer(params1, CS_LOCAL));
@@ -70,16 +86,186 @@
EXPECT_TRUE(f1_.IsActive());
EXPECT_TRUE(f2_.IsActive());
}
+ void TestRtpAuthParams(cricket::SrtpFilter* filter, const std::string& cs) {
+ int overhead;
+ EXPECT_TRUE(filter->GetSrtpOverhead(&overhead));
+ switch (SrtpCryptoSuiteFromName(cs)) {
+ case SRTP_AES128_CM_SHA1_32:
+ EXPECT_EQ(32 / 8, overhead); // 32-bit tag.
+ break;
+ case SRTP_AES128_CM_SHA1_80:
+ EXPECT_EQ(80 / 8, overhead); // 80-bit tag.
+ break;
+ default:
+ RTC_NOTREACHED();
+ break;
+ }
- void VerifyCryptoParamsMatch(const std::string& cs1, const std::string& cs2) {
- EXPECT_EQ(rtc::SrtpCryptoSuiteFromName(cs1), f1_.send_cipher_suite());
- EXPECT_EQ(rtc::SrtpCryptoSuiteFromName(cs2), f2_.send_cipher_suite());
- EXPECT_TRUE(f1_.send_key() == f2_.recv_key());
- EXPECT_TRUE(f2_.send_key() == f1_.recv_key());
+ uint8_t* auth_key = nullptr;
+ int key_len = 0;
+ int tag_len = 0;
+ EXPECT_TRUE(filter->GetRtpAuthParams(&auth_key, &key_len, &tag_len));
+ EXPECT_NE(nullptr, auth_key);
+ EXPECT_EQ(160 / 8, key_len); // Length of SHA-1 is 160 bits.
+ EXPECT_EQ(overhead, tag_len);
}
+ void TestProtectUnprotect(const std::string& cs1, const std::string& cs2) {
+ Buffer rtp_buffer(sizeof(kPcmuFrame) + rtp_auth_tag_len(cs1));
+ char* rtp_packet = rtp_buffer.data<char>();
+ char original_rtp_packet[sizeof(kPcmuFrame)];
+ Buffer rtcp_buffer(sizeof(kRtcpReport) + 4 + rtcp_auth_tag_len(cs2));
+ char* rtcp_packet = rtcp_buffer.data<char>();
+ int rtp_len = sizeof(kPcmuFrame), rtcp_len = sizeof(kRtcpReport), out_len;
+ memcpy(rtp_packet, kPcmuFrame, 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.
+ SetBE16(reinterpret_cast<uint8_t*>(rtp_packet) + 2, ++sequence_number_);
+ memcpy(original_rtp_packet, rtp_packet, rtp_len);
+ memcpy(rtcp_packet, kRtcpReport, rtcp_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));
+ if (!f1_.IsExternalAuthActive()) {
+ 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));
+ } else {
+ // With external auth enabled, SRTP doesn't write the auth tag and
+ // unprotect would fail. Check accessing the information about the
+ // tag instead, similar to what the actual code would do that relies
+ // on external auth.
+ TestRtpAuthParams(&f1_, cs1);
+ }
+
+ 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));
+ if (!f2_.IsExternalAuthActive()) {
+ 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));
+ } else {
+ TestRtpAuthParams(&f2_, cs2);
+ }
+
+ EXPECT_TRUE(f1_.ProtectRtcp(
+ rtcp_packet, rtcp_len, static_cast<int>(rtcp_buffer.size()), &out_len));
+ EXPECT_EQ(out_len, rtcp_len + 4 + rtcp_auth_tag_len(cs1)); // NOLINT
+ EXPECT_NE(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len));
+ EXPECT_TRUE(f2_.UnprotectRtcp(rtcp_packet, out_len, &out_len));
+ EXPECT_EQ(rtcp_len, out_len);
+ EXPECT_EQ(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len));
+
+ EXPECT_TRUE(f2_.ProtectRtcp(
+ rtcp_packet, rtcp_len, static_cast<int>(rtcp_buffer.size()), &out_len));
+ EXPECT_EQ(out_len, rtcp_len + 4 + rtcp_auth_tag_len(cs2)); // NOLINT
+ EXPECT_NE(0, memcmp(rtcp_packet, kRtcpReport, rtcp_len));
+ EXPECT_TRUE(f1_.UnprotectRtcp(rtcp_packet, out_len, &out_len));
+ 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) {
+ 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.
+ 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) {
+ EXPECT_EQ(key1_len, key2_len);
+ EXPECT_EQ(cs_name, SrtpCryptoSuiteToName(cs));
+ if (enable_external_auth) {
+ f1_.EnableExternalAuth();
+ f2_.EnableExternalAuth();
+ }
+ 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_.SetRtcpParams(cs, key1, key1_len, cs, key2, key2_len));
+ EXPECT_TRUE(f2_.SetRtcpParams(cs, key2, key2_len, cs, key1, key1_len));
+ EXPECT_TRUE(f1_.IsActive());
+ EXPECT_TRUE(f2_.IsActive());
+ if (IsGcmCryptoSuite(cs)) {
+ EXPECT_FALSE(f1_.IsExternalAuthActive());
+ EXPECT_FALSE(f2_.IsExternalAuthActive());
+ } else if (enable_external_auth) {
+ EXPECT_TRUE(f1_.IsExternalAuthActive());
+ EXPECT_TRUE(f2_.IsExternalAuthActive());
+ }
+ 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, 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_;
};
// Test that we can set up the session and keys properly.
@@ -293,6 +479,21 @@
EXPECT_FALSE(f1_.IsActive());
}
+// Test that we can encrypt/decrypt after setting the same CryptoParams again on
+// one side.
+TEST_F(SrtpFilterTest, TestSettingSameKeyOnOneSide) {
+ std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
+ std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
+ TestSetParams(offer, answer);
+
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
+
+ // Re-applying the same keys on one end and it should not reset the ROC.
+ EXPECT_TRUE(f2_.SetOffer(offer, CS_REMOTE));
+ EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
+}
+
// Test that we can encrypt/decrypt after negotiating AES_CM_128_HMAC_SHA1_80.
TEST_F(SrtpFilterTest, TestProtect_AES_CM_128_HMAC_SHA1_80) {
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
@@ -301,8 +502,7 @@
offer[1].tag = 2;
offer[1].cipher_suite = CS_AES_CM_128_HMAC_SHA1_32;
TestSetParams(offer, answer);
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
}
// Test that we can encrypt/decrypt after negotiating AES_CM_128_HMAC_SHA1_32.
@@ -315,8 +515,7 @@
answer[0].tag = 2;
answer[0].cipher_suite = CS_AES_CM_128_HMAC_SHA1_32;
TestSetParams(offer, answer);
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_32,
- CS_AES_CM_128_HMAC_SHA1_32);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_32, CS_AES_CM_128_HMAC_SHA1_32);
}
// Test that we can change encryption parameters.
@@ -325,8 +524,7 @@
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
TestSetParams(offer, answer);
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
// Change the key parameters and cipher_suite.
offer[0].key_params = kTestKeyParams3;
@@ -340,15 +538,13 @@
EXPECT_TRUE(f1_.IsActive());
// Test that the old keys are valid until the negotiation is complete.
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
// Complete the negotiation and test that we can still understand each other.
EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_32,
- CS_AES_CM_128_HMAC_SHA1_32);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_32, CS_AES_CM_128_HMAC_SHA1_32);
}
// Test that we can send and receive provisional answers with crypto enabled.
@@ -368,8 +564,7 @@
EXPECT_TRUE(f1_.SetProvisionalAnswer(answer, CS_REMOTE));
EXPECT_TRUE(f1_.IsActive());
EXPECT_TRUE(f2_.IsActive());
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
answer[0].key_params = kTestKeyParams4;
answer[0].tag = 2;
@@ -378,8 +573,7 @@
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
EXPECT_TRUE(f1_.IsActive());
EXPECT_TRUE(f2_.IsActive());
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_32,
- CS_AES_CM_128_HMAC_SHA1_32);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_32, CS_AES_CM_128_HMAC_SHA1_32);
}
// Test that a provisional answer doesn't need to contain a crypto.
@@ -401,8 +595,7 @@
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
EXPECT_TRUE(f1_.IsActive());
EXPECT_TRUE(f2_.IsActive());
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
}
// Test that if we get a new local offer after a provisional answer
@@ -429,8 +622,7 @@
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
EXPECT_TRUE(f1_.IsActive());
EXPECT_TRUE(f2_.IsActive());
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
}
// Test that we can disable encryption.
@@ -439,8 +631,7 @@
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
TestSetParams(offer, answer);
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
offer.clear();
answer.clear();
@@ -450,8 +641,7 @@
EXPECT_TRUE(f2_.IsActive());
// Test that the old keys are valid until the negotiation is complete.
- VerifyCryptoParamsMatch(CS_AES_CM_128_HMAC_SHA1_80,
- CS_AES_CM_128_HMAC_SHA1_80);
+ TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
// Complete the negotiation.
EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
@@ -461,4 +651,85 @@
EXPECT_FALSE(f2_.IsActive());
}
+class SrtpFilterProtectSetParamsDirectTest
+ : public SrtpFilterTest,
+ public testing::WithParamInterface<bool> {};
+
+// Test directly setting the params with AES_CM_128_HMAC_SHA1_80.
+TEST_P(SrtpFilterProtectSetParamsDirectTest, Test_AES_CM_128_HMAC_SHA1_80) {
+ bool enable_external_auth = GetParam();
+ TestProtectSetParamsDirect(enable_external_auth, SRTP_AES128_CM_SHA1_80,
+ kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen,
+ CS_AES_CM_128_HMAC_SHA1_80);
+}
+
+TEST_F(SrtpFilterTest,
+ TestProtectSetParamsDirectHeaderEncryption_AES_CM_128_HMAC_SHA1_80) {
+ TestProtectSetParamsDirectHeaderEncryption(
+ 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();
+ TestProtectSetParamsDirect(enable_external_auth, SRTP_AES128_CM_SHA1_32,
+ kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen,
+ CS_AES_CM_128_HMAC_SHA1_32);
+}
+
+TEST_F(SrtpFilterTest,
+ TestProtectSetParamsDirectHeaderEncryption_AES_CM_128_HMAC_SHA1_32) {
+ TestProtectSetParamsDirectHeaderEncryption(
+ 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();
+ TestProtectSetParamsDirect(enable_external_auth, SRTP_AEAD_AES_128_GCM,
+ kTestKeyGcm128_1, kTestKeyGcm128Len,
+ kTestKeyGcm128_2, kTestKeyGcm128Len,
+ CS_AEAD_AES_128_GCM);
+}
+
+TEST_F(SrtpFilterTest,
+ TestProtectSetParamsDirectHeaderEncryption_SRTP_AEAD_AES_128_GCM) {
+ TestProtectSetParamsDirectHeaderEncryption(
+ 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();
+ TestProtectSetParamsDirect(enable_external_auth, SRTP_AEAD_AES_256_GCM,
+ kTestKeyGcm256_1, kTestKeyGcm256Len,
+ kTestKeyGcm256_2, kTestKeyGcm256Len,
+ CS_AEAD_AES_256_GCM);
+}
+
+TEST_F(SrtpFilterTest,
+ TestProtectSetParamsDirectHeaderEncryption_SRTP_AEAD_AES_256_GCM) {
+ TestProtectSetParamsDirectHeaderEncryption(
+ 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,
+ ::testing::Values(true, false));
+
+// Test directly setting the params with bogus keys.
+TEST_F(SrtpFilterTest, TestSetParamsKeyTooShort) {
+ EXPECT_FALSE(f1_.SetRtpParams(SRTP_AES128_CM_SHA1_80, kTestKey1,
+ kTestKeyLen - 1, SRTP_AES128_CM_SHA1_80,
+ kTestKey1, kTestKeyLen - 1));
+ EXPECT_FALSE(f1_.SetRtcpParams(SRTP_AES128_CM_SHA1_80, kTestKey1,
+ kTestKeyLen - 1, SRTP_AES128_CM_SHA1_80,
+ kTestKey1, kTestKeyLen - 1));
+}
+
} // namespace rtc
diff --git a/pc/srtptransport.cc b/pc/srtptransport.cc
index f3b5309..3dd3ce7 100644
--- a/pc/srtptransport.cc
+++ b/pc/srtptransport.cc
@@ -16,7 +16,6 @@
#include "pc/rtptransport.h"
#include "pc/srtpsession.h"
#include "rtc_base/asyncpacketsocket.h"
-#include "rtc_base/base64.h"
#include "rtc_base/copyonwritebuffer.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/trace_event.h"
@@ -43,322 +42,21 @@
&SrtpTransport::OnReadyToSend);
}
-bool SrtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) {
- return SendPacket(false, packet, options, flags);
-}
-
-bool SrtpTransport::SendRtcpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) {
- return SendPacket(true, packet, options, flags);
-}
-
bool SrtpTransport::SendPacket(bool rtcp,
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
int flags) {
- if (!IsActive()) {
- LOG(LS_ERROR)
- << "Failed to send the packet because SRTP transport is inactive.";
- return false;
- }
+ // TODO(zstein): Protect packet.
- rtc::PacketOptions updated_options = options;
- rtc::CopyOnWriteBuffer cp = *packet;
- TRACE_EVENT0("webrtc", "SRTP Encode");
- bool res;
- uint8_t* data = packet->data();
- int len = static_cast<int>(packet->size());
- if (!rtcp) {
-// If ENABLE_EXTERNAL_AUTH flag is on then packet authentication is not done
-// inside libsrtp for a RTP packet. A external HMAC module will be writing
-// a fake HMAC value. This is ONLY done for a RTP packet.
-// Socket layer will update rtp sendtime extension header if present in
-// packet with current time before updating the HMAC.
-#if !defined(ENABLE_EXTERNAL_AUTH)
- res = ProtectRtp(data, len, static_cast<int>(packet->capacity()), &len);
-#else
- if (!IsExternalAuthActive()) {
- res = ProtectRtp(data, len, static_cast<int>(packet->capacity()), &len);
- } else {
- updated_options.packet_time_params.rtp_sendtime_extension_id =
- rtp_abs_sendtime_extn_id_;
- res = ProtectRtp(data, len, static_cast<int>(packet->capacity()), &len,
- &updated_options.packet_time_params.srtp_packet_index);
- // If protection succeeds, let's get auth params from srtp.
- if (res) {
- uint8_t* auth_key = NULL;
- int key_len;
- res = GetRtpAuthParams(
- &auth_key, &key_len,
- &updated_options.packet_time_params.srtp_auth_tag_len);
- if (res) {
- updated_options.packet_time_params.srtp_auth_key.resize(key_len);
- updated_options.packet_time_params.srtp_auth_key.assign(
- auth_key, auth_key + key_len);
- }
- }
- }
-#endif
- if (!res) {
- int seq_num = -1;
- uint32_t ssrc = 0;
- cricket::GetRtpSeqNum(data, len, &seq_num);
- cricket::GetRtpSsrc(data, len, &ssrc);
- LOG(LS_ERROR) << "Failed to protect " << content_name_
- << " RTP packet: size=" << len << ", seqnum=" << seq_num
- << ", SSRC=" << ssrc;
- return false;
- }
- } else {
- res = ProtectRtcp(data, len, static_cast<int>(packet->capacity()), &len);
- if (!res) {
- int type = -1;
- cricket::GetRtcpType(data, len, &type);
- LOG(LS_ERROR) << "Failed to protect " << content_name_
- << " RTCP packet: size=" << len << ", type=" << type;
- return false;
- }
- }
-
- // Update the length of the packet now that we've added the auth tag.
- packet->SetSize(len);
- return rtcp ? rtp_transport_->SendRtcpPacket(packet, updated_options, flags)
- : rtp_transport_->SendRtpPacket(packet, updated_options, flags);
+ return rtp_transport_->SendPacket(rtcp, packet, options, flags);
}
void SrtpTransport::OnPacketReceived(bool rtcp,
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& packet_time) {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Inactive SRTP transport received a packet. Drop it.";
- return;
- }
+ // TODO(zstein): Unprotect packet.
- TRACE_EVENT0("webrtc", "SRTP Decode");
- char* data = packet->data<char>();
- int len = static_cast<int>(packet->size());
- bool res;
- if (!rtcp) {
- res = UnprotectRtp(data, len, &len);
- if (!res) {
- int seq_num = -1;
- uint32_t ssrc = 0;
- cricket::GetRtpSeqNum(data, len, &seq_num);
- cricket::GetRtpSsrc(data, len, &ssrc);
- LOG(LS_ERROR) << "Failed to unprotect " << content_name_
- << " RTP packet: size=" << len << ", seqnum=" << seq_num
- << ", SSRC=" << ssrc;
- return;
- }
- } else {
- res = UnprotectRtcp(data, len, &len);
- if (!res) {
- int type = -1;
- cricket::GetRtcpType(data, len, &type);
- LOG(LS_ERROR) << "Failed to unprotect " << content_name_
- << " RTCP packet: size=" << len << ", type=" << type;
- return;
- }
- }
-
- packet->SetSize(len);
SignalPacketReceived(rtcp, packet, packet_time);
}
-bool SrtpTransport::SetRtpParams(int send_cs,
- const uint8_t* send_key,
- int send_key_len,
- int recv_cs,
- const uint8_t* recv_key,
- int recv_key_len) {
- CreateSrtpSessions();
- send_session_->SetEncryptedHeaderExtensionIds(
- send_encrypted_header_extension_ids_);
- if (external_auth_enabled_) {
- send_session_->EnableExternalAuth();
- }
- if (!send_session_->SetSend(send_cs, send_key, send_key_len)) {
- ResetParams();
- return false;
- }
-
- recv_session_->SetEncryptedHeaderExtensionIds(
- recv_encrypted_header_extension_ids_);
- if (!recv_session_->SetRecv(recv_cs, recv_key, recv_key_len)) {
- ResetParams();
- return false;
- }
-
- LOG(LS_INFO) << "SRTP activated with negotiated parameters:"
- << " send cipher_suite " << send_cs << " recv cipher_suite "
- << recv_cs;
- return true;
-}
-
-bool SrtpTransport::SetRtcpParams(int send_cs,
- const uint8_t* send_key,
- int send_key_len,
- int recv_cs,
- const uint8_t* recv_key,
- int recv_key_len) {
- // This can only be called once, but can be safely called after
- // SetRtpParams
- if (send_rtcp_session_ || recv_rtcp_session_) {
- LOG(LS_ERROR) << "Tried to set SRTCP Params when filter already active";
- return false;
- }
-
- send_rtcp_session_.reset(new cricket::SrtpSession());
- if (!send_rtcp_session_->SetRecv(send_cs, send_key, send_key_len)) {
- return false;
- }
-
- recv_rtcp_session_.reset(new cricket::SrtpSession());
- 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 << " recv cipher_suite "
- << recv_cs;
-
- return true;
-}
-
-bool SrtpTransport::IsActive() const {
- return send_session_ && recv_session_;
-}
-
-void SrtpTransport::ResetParams() {
- send_session_ = nullptr;
- recv_session_ = nullptr;
- send_rtcp_session_ = nullptr;
- recv_rtcp_session_ = nullptr;
- LOG(LS_INFO) << "The params in SRTP transport are reset.";
-}
-
-void SrtpTransport::SetEncryptedHeaderExtensionIds(
- cricket::ContentSource source,
- const std::vector<int>& extension_ids) {
- if (source == cricket::CS_LOCAL) {
- recv_encrypted_header_extension_ids_ = extension_ids;
- } else {
- send_encrypted_header_extension_ids_ = extension_ids;
- }
-}
-
-void SrtpTransport::CreateSrtpSessions() {
- send_session_.reset(new cricket::SrtpSession());
- recv_session_.reset(new cricket::SrtpSession());
-
- if (external_auth_enabled_) {
- send_session_->EnableExternalAuth();
- }
-}
-
-bool SrtpTransport::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
- return false;
- }
- RTC_CHECK(send_session_);
- return send_session_->ProtectRtp(p, in_len, max_len, out_len);
-}
-
-bool SrtpTransport::ProtectRtp(void* p,
- int in_len,
- int max_len,
- int* out_len,
- int64_t* index) {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
- return false;
- }
- RTC_CHECK(send_session_);
- return send_session_->ProtectRtp(p, in_len, max_len, out_len, index);
-}
-
-bool SrtpTransport::ProtectRtcp(void* p,
- int in_len,
- int max_len,
- int* out_len) {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active";
- return false;
- }
- if (send_rtcp_session_) {
- return send_rtcp_session_->ProtectRtcp(p, in_len, max_len, out_len);
- } else {
- RTC_CHECK(send_session_);
- return send_session_->ProtectRtcp(p, in_len, max_len, out_len);
- }
-}
-
-bool SrtpTransport::UnprotectRtp(void* p, int in_len, int* out_len) {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to UnprotectRtp: SRTP not active";
- return false;
- }
- RTC_CHECK(recv_session_);
- return recv_session_->UnprotectRtp(p, in_len, out_len);
-}
-
-bool SrtpTransport::UnprotectRtcp(void* p, int in_len, int* out_len) {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to UnprotectRtcp: SRTP not active";
- return false;
- }
- if (recv_rtcp_session_) {
- return recv_rtcp_session_->UnprotectRtcp(p, in_len, out_len);
- } else {
- RTC_CHECK(recv_session_);
- return recv_session_->UnprotectRtcp(p, in_len, out_len);
- }
-}
-
-bool SrtpTransport::GetRtpAuthParams(uint8_t** key,
- int* key_len,
- int* tag_len) {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to GetRtpAuthParams: SRTP not active";
- return false;
- }
-
- RTC_CHECK(send_session_);
- return send_session_->GetRtpAuthParams(key, key_len, tag_len);
-}
-
-bool SrtpTransport::GetSrtpOverhead(int* srtp_overhead) const {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to GetSrtpOverhead: SRTP not active";
- return false;
- }
-
- RTC_CHECK(send_session_);
- *srtp_overhead = send_session_->GetSrtpOverhead();
- return true;
-}
-
-void SrtpTransport::EnableExternalAuth() {
- RTC_DCHECK(!IsActive());
- external_auth_enabled_ = true;
-}
-
-bool SrtpTransport::IsExternalAuthEnabled() const {
- return external_auth_enabled_;
-}
-
-bool SrtpTransport::IsExternalAuthActive() const {
- if (!IsActive()) {
- LOG(LS_WARNING) << "Failed to check IsExternalAuthActive: SRTP not active";
- return false;
- }
-
- RTC_CHECK(send_session_);
- return send_session_->IsExternalAuthActive();
-}
-
} // namespace webrtc
diff --git a/pc/srtptransport.h b/pc/srtptransport.h
index 9f20e1d..4d2ecb8 100644
--- a/pc/srtptransport.h
+++ b/pc/srtptransport.h
@@ -17,17 +17,20 @@
#include "pc/rtptransportinternal.h"
#include "pc/srtpfilter.h"
-#include "pc/srtpsession.h"
#include "rtc_base/checks.h"
namespace webrtc {
// This class will eventually be a wrapper around RtpTransportInternal
-// that protects and unprotects sent and received RTP packets.
+// that protects and unprotects sent and received RTP packets. This
+// functionality is currently implemented by SrtpFilter and BaseChannel, but
+// will be moved here in the future.
class SrtpTransport : public RtpTransportInternal {
public:
SrtpTransport(bool rtcp_mux_enabled, const std::string& content_name);
+ // TODO(zstein): Consider taking an RtpTransport instead of an
+ // RtpTransportInternal.
SrtpTransport(std::unique_ptr<RtpTransportInternal> transport,
const std::string& content_name);
@@ -58,21 +61,14 @@
return rtp_transport_->GetRtcpPacketTransport();
}
- bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) override;
-
- bool SendRtcpPacket(rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags) override;
-
bool IsWritable(bool rtcp) const override {
return rtp_transport_->IsWritable(rtcp);
}
- // The transport becomes active if the send_session_ and recv_session_ are
- // created.
- bool IsActive() const;
+ bool SendPacket(bool rtcp,
+ rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options,
+ int flags) override;
bool HandlesPayloadType(int payload_type) const override {
return rtp_transport_->HandlesPayloadType(payload_type);
@@ -93,104 +89,18 @@
// TODO(zstein): Remove this when we remove RtpTransportAdapter.
RtpTransportAdapter* GetInternal() override { return nullptr; }
- // Create new send/recv sessions and set the negotiated crypto keys for RTP
- // packet encryption. The keys can either come from SDES negotiation or DTLS
- // handshake.
- bool SetRtpParams(int send_cs,
- const uint8_t* send_key,
- int send_key_len,
- int recv_cs,
- const uint8_t* recv_key,
- int recv_key_len);
-
- // Create new send/recv sessions and set the negotiated crypto keys for RTCP
- // packet encryption. The keys can either come from SDES negotiation or DTLS
- // handshake.
- bool SetRtcpParams(int send_cs,
- const uint8_t* send_key,
- int send_key_len,
- int recv_cs,
- const uint8_t* recv_key,
- int recv_key_len);
-
- void ResetParams();
-
- // Set the header extension ids that should be encrypted for the given source.
- // This method doesn't immediately update the SRTP session with the new IDs,
- // and you need to call SetRtpParams for that to happen.
- void SetEncryptedHeaderExtensionIds(cricket::ContentSource source,
- const std::vector<int>& extension_ids);
-
- // If external auth is enabled, SRTP will write a dummy auth tag that then
- // later must get replaced before the packet is sent out. Only supported for
- // non-GCM cipher suites and can be checked through "IsExternalAuthActive"
- // if it is actually used. This method is only valid before the RTP params
- // have been set.
- void EnableExternalAuth();
- bool IsExternalAuthEnabled() const;
-
- // A SrtpTransport supports external creation of the auth tag if a non-GCM
- // cipher is used. This method is only valid after the RTP params have
- // been set.
- bool IsExternalAuthActive() const;
-
- // Returns srtp overhead for rtp packets.
- bool GetSrtpOverhead(int* srtp_overhead) const;
-
- // Returns rtp auth params from srtp context.
- bool GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len);
-
- // Helper method to get RTP Absoulute SendTime extension header id if
- // present in remote supported extensions list.
- void CacheRtpAbsSendTimeHeaderExtension(int rtp_abs_sendtime_extn_id) {
- rtp_abs_sendtime_extn_id_ = rtp_abs_sendtime_extn_id;
- }
-
private:
- void CreateSrtpSessions();
-
void ConnectToRtpTransport();
- bool SendPacket(bool rtcp,
- rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketOptions& options,
- int flags);
-
void OnPacketReceived(bool rtcp,
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& packet_time);
void OnReadyToSend(bool ready) { SignalReadyToSend(ready); }
- bool ProtectRtp(void* data, int in_len, int max_len, int* out_len);
-
- // Overloaded version, outputs packet index.
- bool ProtectRtp(void* data,
- int in_len,
- int max_len,
- int* out_len,
- int64_t* index);
- bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len);
-
- // Decrypts/verifies an invidiual RTP/RTCP packet.
- // If an HMAC is used, this will decrease the packet size.
- bool UnprotectRtp(void* data, int in_len, int* out_len);
-
- bool UnprotectRtcp(void* data, int in_len, int* out_len);
-
const std::string content_name_;
+
std::unique_ptr<RtpTransportInternal> rtp_transport_;
-
- std::unique_ptr<cricket::SrtpSession> send_session_;
- std::unique_ptr<cricket::SrtpSession> recv_session_;
- std::unique_ptr<cricket::SrtpSession> send_rtcp_session_;
- std::unique_ptr<cricket::SrtpSession> recv_rtcp_session_;
-
- std::vector<int> send_encrypted_header_extension_ids_;
- std::vector<int> recv_encrypted_header_extension_ids_;
- bool external_auth_enabled_ = false;
-
- int rtp_abs_sendtime_extn_id_ = -1;
};
} // namespace webrtc
diff --git a/pc/srtptransport_unittest.cc b/pc/srtptransport_unittest.cc
index d3bc259..6cc4ee7 100644
--- a/pc/srtptransport_unittest.cc
+++ b/pc/srtptransport_unittest.cc
@@ -10,413 +10,67 @@
#include "pc/srtptransport.h"
-#include "media/base/fakertp.h"
-#include "p2p/base/dtlstransportinternal.h"
-#include "p2p/base/fakepackettransport.h"
#include "pc/rtptransport.h"
#include "pc/rtptransporttestutil.h"
-#include "pc/srtptestutil.h"
#include "rtc_base/asyncpacketsocket.h"
#include "rtc_base/gunit.h"
#include "rtc_base/ptr_util.h"
-#include "rtc_base/sslstreamadapter.h"
-
-using rtc::kTestKey1;
-using rtc::kTestKey2;
-using rtc::kTestKeyLen;
-using rtc::SRTP_AEAD_AES_128_GCM;
+#include "test/gmock.h"
namespace webrtc {
-static const uint8_t kTestKeyGcm128_1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ12";
-static const uint8_t kTestKeyGcm128_2[] = "21ZYXWVUTSRQPONMLKJIHGFEDCBA";
-static const int kTestKeyGcm128Len = 28; // 128 bits key + 96 bits salt.
-static const uint8_t kTestKeyGcm256_1[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr";
-static const uint8_t kTestKeyGcm256_2[] =
- "rqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";
-static const int kTestKeyGcm256Len = 44; // 256 bits key + 96 bits salt.
-class SrtpTransportTest : public testing::Test, public sigslot::has_slots<> {
- protected:
- SrtpTransportTest() {
- bool rtcp_mux_enabled = true;
- auto rtp_transport1 = rtc::MakeUnique<RtpTransport>(rtcp_mux_enabled);
- auto rtp_transport2 = rtc::MakeUnique<RtpTransport>(rtcp_mux_enabled);
+using testing::_;
+using testing::Return;
- rtp_packet_transport1_ =
- rtc::MakeUnique<rtc::FakePacketTransport>("fake_packet_transport1");
- rtp_packet_transport2_ =
- rtc::MakeUnique<rtc::FakePacketTransport>("fake_packet_transport2");
+class MockRtpTransport : public RtpTransport {
+ public:
+ MockRtpTransport() : RtpTransport(true) {}
- bool asymmetric = false;
- rtp_packet_transport1_->SetDestination(rtp_packet_transport2_.get(),
- asymmetric);
+ MOCK_METHOD4(SendPacket,
+ bool(bool rtcp,
+ rtc::CopyOnWriteBuffer* packet,
+ const rtc::PacketOptions& options,
+ int flags));
- rtp_transport1->SetRtpPacketTransport(rtp_packet_transport1_.get());
- rtp_transport2->SetRtpPacketTransport(rtp_packet_transport2_.get());
-
- // Add payload type for RTP packet and RTCP packet.
- rtp_transport1->AddHandledPayloadType(0x00);
- rtp_transport2->AddHandledPayloadType(0x00);
- rtp_transport1->AddHandledPayloadType(0xc9);
- rtp_transport2->AddHandledPayloadType(0xc9);
-
- srtp_transport1_ =
- rtc::MakeUnique<SrtpTransport>(std::move(rtp_transport1), "content");
- srtp_transport2_ =
- rtc::MakeUnique<SrtpTransport>(std::move(rtp_transport2), "content");
-
- srtp_transport1_->SignalPacketReceived.connect(
- this, &SrtpTransportTest::OnPacketReceived1);
- srtp_transport2_->SignalPacketReceived.connect(
- this, &SrtpTransportTest::OnPacketReceived2);
+ void PretendReceivedPacket() {
+ bool rtcp = false;
+ rtc::CopyOnWriteBuffer buffer;
+ rtc::PacketTime time;
+ SignalPacketReceived(rtcp, &buffer, time);
}
-
- void OnPacketReceived1(bool rtcp,
- rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketTime& packet_time) {
- LOG(LS_INFO) << "SrtpTransport1 Received a packet.";
- last_recv_packet1_ = *packet;
- }
-
- void OnPacketReceived2(bool rtcp,
- rtc::CopyOnWriteBuffer* packet,
- const rtc::PacketTime& packet_time) {
- LOG(LS_INFO) << "SrtpTransport2 Received a packet.";
- last_recv_packet2_ = *packet;
- }
-
- // With external auth enabled, SRTP doesn't write the auth tag and
- // unprotect would fail. Check accessing the information about the
- // tag instead, similar to what the actual code would do that relies
- // on external auth.
- void TestRtpAuthParams(SrtpTransport* transport, const std::string& cs) {
- int overhead;
- EXPECT_TRUE(transport->GetSrtpOverhead(&overhead));
- switch (rtc::SrtpCryptoSuiteFromName(cs)) {
- case rtc::SRTP_AES128_CM_SHA1_32:
- EXPECT_EQ(32 / 8, overhead); // 32-bit tag.
- break;
- case rtc::SRTP_AES128_CM_SHA1_80:
- EXPECT_EQ(80 / 8, overhead); // 80-bit tag.
- break;
- default:
- RTC_NOTREACHED();
- break;
- }
-
- uint8_t* auth_key = nullptr;
- int key_len = 0;
- int tag_len = 0;
- EXPECT_TRUE(transport->GetRtpAuthParams(&auth_key, &key_len, &tag_len));
- EXPECT_NE(nullptr, auth_key);
- EXPECT_EQ(160 / 8, key_len); // Length of SHA-1 is 160 bits.
- EXPECT_EQ(overhead, tag_len);
- }
-
- void TestSendRecvRtpPacket(const std::string& cipher_suite_name) {
- size_t rtp_len = sizeof(kPcmuFrame);
- size_t packet_size = rtp_len + rtc::rtp_auth_tag_len(cipher_suite_name);
- rtc::Buffer rtp_packet_buffer(packet_size);
- char* rtp_packet_data = rtp_packet_buffer.data<char>();
- memcpy(rtp_packet_data, kPcmuFrame, 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_data) + 2,
- ++sequence_number_);
- rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
- packet_size);
- rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
- packet_size);
-
- char original_rtp_data[sizeof(kPcmuFrame)];
- memcpy(original_rtp_data, rtp_packet_data, rtp_len);
-
- rtc::PacketOptions options;
- // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify
- // that the packet can be successfully received and decrypted.
- ASSERT_TRUE(srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
- cricket::PF_SRTP_BYPASS));
- if (srtp_transport1_->IsExternalAuthActive()) {
- TestRtpAuthParams(srtp_transport1_.get(), cipher_suite_name);
- } else {
- ASSERT_TRUE(last_recv_packet2_.data());
- EXPECT_TRUE(
- memcmp(last_recv_packet2_.data(), original_rtp_data, rtp_len) == 0);
- // Get the encrypted packet from underneath packet transport and verify
- // the data is actually encrypted.
- auto fake_rtp_packet_transport = static_cast<rtc::FakePacketTransport*>(
- srtp_transport1_->rtp_packet_transport());
- EXPECT_FALSE(memcmp(fake_rtp_packet_transport->last_sent_packet()->data(),
- original_rtp_data, rtp_len) == 0);
- }
-
- // Do the same thing in the opposite direction;
- ASSERT_TRUE(srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
- cricket::PF_SRTP_BYPASS));
- if (srtp_transport2_->IsExternalAuthActive()) {
- TestRtpAuthParams(srtp_transport2_.get(), cipher_suite_name);
- } else {
- ASSERT_TRUE(last_recv_packet1_.data());
- EXPECT_TRUE(
- memcmp(last_recv_packet1_.data(), original_rtp_data, rtp_len) == 0);
- auto fake_rtp_packet_transport = static_cast<rtc::FakePacketTransport*>(
- srtp_transport2_->rtp_packet_transport());
- EXPECT_FALSE(memcmp(fake_rtp_packet_transport->last_sent_packet()->data(),
- original_rtp_data, rtp_len) == 0);
- }
- }
-
- void TestSendRecvRtcpPacket(const std::string& cipher_suite_name) {
- size_t rtcp_len = sizeof(kRtcpReport);
- size_t packet_size =
- rtcp_len + 4 + rtc::rtcp_auth_tag_len(cipher_suite_name);
- rtc::Buffer rtcp_packet_buffer(packet_size);
- char* rtcp_packet_data = rtcp_packet_buffer.data<char>();
- memcpy(rtcp_packet_data, kRtcpReport, rtcp_len);
-
- rtc::CopyOnWriteBuffer rtcp_packet1to2(rtcp_packet_data, rtcp_len,
- packet_size);
- rtc::CopyOnWriteBuffer rtcp_packet2to1(rtcp_packet_data, rtcp_len,
- packet_size);
-
- rtc::PacketOptions options;
- // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify
- // that the packet can be successfully received and decrypted.
- ASSERT_TRUE(srtp_transport1_->SendRtcpPacket(&rtcp_packet1to2, options,
- cricket::PF_SRTP_BYPASS));
- ASSERT_TRUE(last_recv_packet2_.data());
- EXPECT_TRUE(memcmp(last_recv_packet2_.data(), rtcp_packet_data, rtcp_len) ==
- 0);
- // Get the encrypted packet from underneath packet transport and verify the
- // data is actually encrypted.
- auto fake_rtp_packet_transport = static_cast<rtc::FakePacketTransport*>(
- srtp_transport1_->rtp_packet_transport());
- EXPECT_FALSE(memcmp(fake_rtp_packet_transport->last_sent_packet()->data(),
- rtcp_packet_data, rtcp_len) == 0);
-
- // Do the same thing in the opposite direction;
- ASSERT_TRUE(srtp_transport2_->SendRtcpPacket(&rtcp_packet2to1, options,
- cricket::PF_SRTP_BYPASS));
- ASSERT_TRUE(last_recv_packet1_.data());
- EXPECT_TRUE(memcmp(last_recv_packet1_.data(), rtcp_packet_data, rtcp_len) ==
- 0);
- fake_rtp_packet_transport = static_cast<rtc::FakePacketTransport*>(
- srtp_transport2_->rtp_packet_transport());
- EXPECT_FALSE(memcmp(fake_rtp_packet_transport->last_sent_packet()->data(),
- rtcp_packet_data, rtcp_len) == 0);
- }
-
- void TestSendRecvPacket(bool enable_external_auth,
- int cs,
- const uint8_t* key1,
- int key1_len,
- const uint8_t* key2,
- int key2_len,
- const std::string& cipher_suite_name) {
- EXPECT_EQ(key1_len, key2_len);
- EXPECT_EQ(cipher_suite_name, rtc::SrtpCryptoSuiteToName(cs));
- if (enable_external_auth) {
- srtp_transport1_->EnableExternalAuth();
- srtp_transport2_->EnableExternalAuth();
- }
- EXPECT_TRUE(
- srtp_transport1_->SetRtpParams(cs, key1, key1_len, cs, key2, key2_len));
- EXPECT_TRUE(
- srtp_transport2_->SetRtpParams(cs, key2, key2_len, cs, key1, key1_len));
- EXPECT_TRUE(srtp_transport1_->SetRtcpParams(cs, key1, key1_len, cs, key2,
- key2_len));
- EXPECT_TRUE(srtp_transport2_->SetRtcpParams(cs, key2, key2_len, cs, key1,
- key1_len));
- EXPECT_TRUE(srtp_transport1_->IsActive());
- EXPECT_TRUE(srtp_transport2_->IsActive());
- if (rtc::IsGcmCryptoSuite(cs)) {
- EXPECT_FALSE(srtp_transport1_->IsExternalAuthActive());
- EXPECT_FALSE(srtp_transport2_->IsExternalAuthActive());
- } else if (enable_external_auth) {
- EXPECT_TRUE(srtp_transport1_->IsExternalAuthActive());
- EXPECT_TRUE(srtp_transport2_->IsExternalAuthActive());
- }
- TestSendRecvRtpPacket(cipher_suite_name);
- TestSendRecvRtcpPacket(cipher_suite_name);
- }
-
- void TestSendRecvPacketWithEncryptedHeaderExtension(
- const std::string& cs,
- const std::vector<int>& encrypted_header_ids) {
- size_t rtp_len = sizeof(kPcmuFrameWithExtensions);
- size_t packet_size = rtp_len + rtc::rtp_auth_tag_len(cs);
- rtc::Buffer rtp_packet_buffer(packet_size);
- char* rtp_packet_data = rtp_packet_buffer.data<char>();
- memcpy(rtp_packet_data, 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_data) + 2,
- ++sequence_number_);
- rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
- packet_size);
- rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
- packet_size);
-
- char original_rtp_data[sizeof(kPcmuFrameWithExtensions)];
- memcpy(original_rtp_data, rtp_packet_data, rtp_len);
-
- rtc::PacketOptions options;
- // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify
- // that the packet can be successfully received and decrypted.
- ASSERT_TRUE(srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
- cricket::PF_SRTP_BYPASS));
- ASSERT_TRUE(last_recv_packet2_.data());
- EXPECT_TRUE(memcmp(last_recv_packet2_.data(), original_rtp_data, rtp_len) ==
- 0);
- // Get the encrypted packet from underneath packet transport and verify the
- // data and header extension are actually encrypted.
- auto fake_rtp_packet_transport = static_cast<rtc::FakePacketTransport*>(
- srtp_transport1_->rtp_packet_transport());
- EXPECT_FALSE(memcmp(fake_rtp_packet_transport->last_sent_packet()->data(),
- original_rtp_data, rtp_len) == 0);
- CompareHeaderExtensions(
- reinterpret_cast<const char*>(
- fake_rtp_packet_transport->last_sent_packet()->data()),
- fake_rtp_packet_transport->last_sent_packet()->size(),
- original_rtp_data, rtp_len, encrypted_header_ids, false);
-
- // Do the same thing in the opposite direction;
- ASSERT_TRUE(srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
- cricket::PF_SRTP_BYPASS));
- ASSERT_TRUE(last_recv_packet1_.data());
- EXPECT_TRUE(memcmp(last_recv_packet1_.data(), original_rtp_data, rtp_len) ==
- 0);
- fake_rtp_packet_transport = static_cast<rtc::FakePacketTransport*>(
- srtp_transport2_->rtp_packet_transport());
- EXPECT_FALSE(memcmp(fake_rtp_packet_transport->last_sent_packet()->data(),
- original_rtp_data, rtp_len) == 0);
- CompareHeaderExtensions(
- reinterpret_cast<const char*>(
- fake_rtp_packet_transport->last_sent_packet()->data()),
- fake_rtp_packet_transport->last_sent_packet()->size(),
- original_rtp_data, rtp_len, encrypted_header_ids, false);
- }
-
- void TestSendRecvEncryptedHeaderExtension(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));
- srtp_transport1_->SetEncryptedHeaderExtensionIds(cricket::CS_LOCAL,
- encrypted_headers);
- srtp_transport1_->SetEncryptedHeaderExtensionIds(cricket::CS_REMOTE,
- encrypted_headers);
- srtp_transport2_->SetEncryptedHeaderExtensionIds(cricket::CS_LOCAL,
- encrypted_headers);
- srtp_transport2_->SetEncryptedHeaderExtensionIds(cricket::CS_REMOTE,
- encrypted_headers);
- EXPECT_TRUE(
- srtp_transport1_->SetRtpParams(cs, key1, key1_len, cs, key2, key2_len));
- EXPECT_TRUE(
- srtp_transport2_->SetRtpParams(cs, key2, key2_len, cs, key1, key1_len));
- EXPECT_TRUE(srtp_transport1_->IsActive());
- EXPECT_TRUE(srtp_transport2_->IsActive());
- EXPECT_FALSE(srtp_transport1_->IsExternalAuthActive());
- EXPECT_FALSE(srtp_transport2_->IsExternalAuthActive());
- TestSendRecvPacketWithEncryptedHeaderExtension(cs_name, encrypted_headers);
- }
-
- std::unique_ptr<SrtpTransport> srtp_transport1_;
- std::unique_ptr<SrtpTransport> srtp_transport2_;
-
- std::unique_ptr<rtc::FakePacketTransport> rtp_packet_transport1_;
- std::unique_ptr<rtc::FakePacketTransport> rtp_packet_transport2_;
-
- rtc::CopyOnWriteBuffer last_recv_packet1_;
- rtc::CopyOnWriteBuffer last_recv_packet2_;
- int sequence_number_ = 0;
};
-class SrtpTransportTestWithExternalAuth
- : public SrtpTransportTest,
- public testing::WithParamInterface<bool> {};
+TEST(SrtpTransportTest, SendPacket) {
+ auto rtp_transport = rtc::MakeUnique<MockRtpTransport>();
+ EXPECT_CALL(*rtp_transport, SendPacket(_, _, _, _)).WillOnce(Return(true));
-TEST_P(SrtpTransportTestWithExternalAuth,
- SendAndRecvPacket_AES_CM_128_HMAC_SHA1_80) {
- bool enable_external_auth = GetParam();
- TestSendRecvPacket(enable_external_auth, rtc::SRTP_AES128_CM_SHA1_80,
- kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen,
- rtc::CS_AES_CM_128_HMAC_SHA1_80);
+ SrtpTransport srtp_transport(std::move(rtp_transport), "a");
+
+ const bool rtcp = false;
+ rtc::CopyOnWriteBuffer packet;
+ rtc::PacketOptions options;
+ int flags = 0;
+ EXPECT_TRUE(srtp_transport.SendPacket(rtcp, &packet, options, flags));
+
+ // TODO(zstein): Also verify that the packet received by RtpTransport has been
+ // protected once SrtpTransport handles that.
}
-TEST_F(SrtpTransportTest,
- SendAndRecvPacketWithHeaderExtension_AES_CM_128_HMAC_SHA1_80) {
- TestSendRecvEncryptedHeaderExtension(rtc::SRTP_AES128_CM_SHA1_80, kTestKey1,
- kTestKeyLen, kTestKey2, kTestKeyLen,
- rtc::CS_AES_CM_128_HMAC_SHA1_80);
-}
+// Test that SrtpTransport fires SignalPacketReceived when the underlying
+// RtpTransport fires SignalPacketReceived.
+TEST(SrtpTransportTest, SignalPacketReceived) {
+ auto rtp_transport = rtc::MakeUnique<MockRtpTransport>();
+ MockRtpTransport* rtp_transport_raw = rtp_transport.get();
+ SrtpTransport srtp_transport(std::move(rtp_transport), "a");
-TEST_P(SrtpTransportTestWithExternalAuth,
- SendAndRecvPacket_AES_CM_128_HMAC_SHA1_32) {
- bool enable_external_auth = GetParam();
- TestSendRecvPacket(enable_external_auth, rtc::SRTP_AES128_CM_SHA1_32,
- kTestKey1, kTestKeyLen, kTestKey2, kTestKeyLen,
- rtc::CS_AES_CM_128_HMAC_SHA1_32);
-}
+ SignalPacketReceivedCounter counter(&srtp_transport);
-TEST_F(SrtpTransportTest,
- SendAndRecvPacketWithHeaderExtension_AES_CM_128_HMAC_SHA1_32) {
- TestSendRecvEncryptedHeaderExtension(rtc::SRTP_AES128_CM_SHA1_32, kTestKey1,
- kTestKeyLen, kTestKey2, kTestKeyLen,
- rtc::CS_AES_CM_128_HMAC_SHA1_32);
-}
+ rtp_transport_raw->PretendReceivedPacket();
-TEST_P(SrtpTransportTestWithExternalAuth,
- SendAndRecvPacket_SRTP_AEAD_AES_128_GCM) {
- bool enable_external_auth = GetParam();
- TestSendRecvPacket(enable_external_auth, rtc::SRTP_AEAD_AES_128_GCM,
- kTestKeyGcm128_1, kTestKeyGcm128Len, kTestKeyGcm128_2,
- kTestKeyGcm128Len, rtc::CS_AEAD_AES_128_GCM);
-}
+ EXPECT_EQ(1, counter.rtp_count());
-TEST_F(SrtpTransportTest,
- SendAndRecvPacketWithHeaderExtension_SRTP_AEAD_AES_128_GCM) {
- TestSendRecvEncryptedHeaderExtension(
- rtc::SRTP_AEAD_AES_128_GCM, kTestKeyGcm128_1, kTestKeyGcm128Len,
- kTestKeyGcm128_2, kTestKeyGcm128Len, rtc::CS_AEAD_AES_128_GCM);
-}
-
-TEST_P(SrtpTransportTestWithExternalAuth,
- SendAndRecvPacket_SRTP_AEAD_AES_256_GCM) {
- bool enable_external_auth = GetParam();
- TestSendRecvPacket(enable_external_auth, rtc::SRTP_AEAD_AES_256_GCM,
- kTestKeyGcm256_1, kTestKeyGcm256Len, kTestKeyGcm256_2,
- kTestKeyGcm256Len, rtc::CS_AEAD_AES_256_GCM);
-}
-
-TEST_F(SrtpTransportTest,
- SendAndRecvPacketWithHeaderExtension_SRTP_AEAD_AES_256_GCM) {
- TestSendRecvEncryptedHeaderExtension(
- rtc::SRTP_AEAD_AES_256_GCM, kTestKeyGcm256_1, kTestKeyGcm256Len,
- kTestKeyGcm256_2, kTestKeyGcm256Len, rtc::CS_AEAD_AES_256_GCM);
-}
-
-// Run all tests both with and without external auth enabled.
-INSTANTIATE_TEST_CASE_P(ExternalAuth,
- SrtpTransportTestWithExternalAuth,
- ::testing::Values(true, false));
-
-// Test directly setting the params with bogus keys.
-TEST_F(SrtpTransportTest, TestSetParamsKeyTooShort) {
- EXPECT_FALSE(srtp_transport1_->SetRtpParams(
- rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen - 1,
- rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen - 1));
- EXPECT_FALSE(srtp_transport1_->SetRtcpParams(
- rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen - 1,
- rtc::SRTP_AES128_CM_SHA1_80, kTestKey1, kTestKeyLen - 1));
+ // TODO(zstein): Also verify that the packet is unprotected once SrtpTransport
+ // handles that.
}
} // namespace webrtc