/* * Copyright 2017 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree.
*/
namespace { class LibSrtpInitializer { public: // Returns singleton instance of this class. Instance created on first use, // and never destroyed. static LibSrtpInitializer& Get() { static LibSrtpInitializer* const instance = new LibSrtpInitializer(); return *instance;
}
// There is only one global log handler in libsrtp so we can not resolve this // to a particular session. staticvoid LibSrtpLogHandler(srtp_log_level_t level, constchar* msg, void* data); void ProhibitLibsrtpInitialization();
// These methods are responsible for initializing libsrtp (if the usage count // is incremented from 0 to 1) or deinitializing it (when decremented from 1 // to 0). // // Returns true if successful (will always be successful if already inited). bool IncrementLibsrtpUsageCountAndMaybeInit(
srtp_event_handler_func_t* event_handler); void DecrementLibsrtpUsageCountAndMaybeDeinit();
private:
LibSrtpInitializer() = default;
webrtc::Mutex mutex_; int usage_count_ RTC_GUARDED_BY(mutex_) = 0;
};
RTC_DCHECK_GE(usage_count_, 1); if (--usage_count_ == 0) { int err = srtp_install_log_handler(nullptr, nullptr); if (err != srtp_err_status_ok) {
RTC_LOG(LS_ERROR) << "Failed to uninstall libsrtp log handler, err="
<< err;
}
err = srtp_shutdown(); if (err != srtp_err_status_ok) {
RTC_LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err;
}
}
}
} // namespace
using ::webrtc::ParseRtpSequenceNumber;
// One more than the maximum libsrtp error code. Required by // RTC_HISTOGRAM_ENUMERATION. Keep this in sync with srtp_error_status_t defined // in srtp.h.
constexpr int kSrtpErrorCodeBoundary = 28;
bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
RTC_DCHECK(thread_checker_.IsCurrent()); if (!session_) {
RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session"; returnfalse;
}
// Note: the need_len differs from the libsrtp recommendatіon to ensure // SRTP_MAX_TRAILER_LEN bytes of free space after the data. WebRTC // never includes a MKI, therefore the amount of bytes added by the // srtp_protect call is known in advance and depends on the cipher suite. int need_len = in_len + rtp_auth_tag_len_; // NOLINT if (max_len < need_len) {
RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length "
<< max_len << " is less than the needed " << need_len; returnfalse;
} if (dump_plain_rtp_) {
DumpPacket(p, in_len, /*outbound=*/true);
}
*out_len = in_len; int err = srtp_protect(session_, p, out_len); int seq_num = ParseRtpSequenceNumber(
rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(p), in_len)); if (err != srtp_err_status_ok) {
RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" << seq_num
<< ", err=" << err
<< ", last seqnum=" << last_send_seq_num_; returnfalse;
}
last_send_seq_num_ = seq_num; returntrue;
}
bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len,
int64_t* index) { if (!ProtectRtp(p, in_len, max_len, out_len)) { returnfalse;
} return (index) ? GetSendStreamPacketIndex(p, in_len, index) : true;
}
bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
RTC_DCHECK(thread_checker_.IsCurrent()); if (!session_) {
RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session"; returnfalse;
}
// Note: the need_len differs from the libsrtp recommendatіon to ensure // SRTP_MAX_TRAILER_LEN bytes of free space after the data. WebRTC // never includes a MKI, therefore the amount of bytes added by the // srtp_protect_rtp call is known in advance and depends on the cipher suite. int need_len = in_len + sizeof(uint32_t) + rtcp_auth_tag_len_; // NOLINT if (max_len < need_len) {
RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length "
<< max_len << " is less than the needed " << need_len; returnfalse;
} if (dump_plain_rtp_) {
DumpPacket(p, in_len, /*outbound=*/true);
}
*out_len = in_len; int err = srtp_protect_rtcp(session_, p, out_len); if (err != srtp_err_status_ok) {
RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err; returnfalse;
} returntrue;
}
bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) {
RTC_DCHECK(thread_checker_.IsCurrent()); if (!session_) {
RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session"; returnfalse;
}
*out_len = in_len; int err = srtp_unprotect(session_, p, out_len); if (err != srtp_err_status_ok) { // Limit the error logging to avoid excessive logs when there are lots of // bad packets. constint kFailureLogThrottleCount = 100; if (decryption_failure_count_ % kFailureLogThrottleCount == 0) {
RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err
<< ", previous failure count: "
<< decryption_failure_count_;
}
++decryption_failure_count_;
RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtpUnprotectError", static_cast<int>(err), kSrtpErrorCodeBoundary); returnfalse;
} if (dump_plain_rtp_) {
DumpPacket(p, *out_len, /*outbound=*/false);
} returntrue;
}
bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) {
RTC_DCHECK(thread_checker_.IsCurrent()); if (!session_) {
RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session"; returnfalse;
}
*out_len = in_len; int err = srtp_unprotect_rtcp(session_, p, out_len); if (err != srtp_err_status_ok) {
RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err;
RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtcpUnprotectError", static_cast<int>(err), kSrtpErrorCodeBoundary); returnfalse;
} if (dump_plain_rtp_) {
DumpPacket(p, *out_len, /*outbound=*/false);
} returntrue;
}
ExternalHmacContext* external_hmac = nullptr; // stream_template will be the reference context for other streams. // Let's use it for getting the keys.
srtp_stream_ctx_t* srtp_context = session_->stream_template; if (srtp_context && srtp_context->session_keys &&
srtp_context->session_keys->rtp_auth) {
external_hmac = reinterpret_cast<ExternalHmacContext*>(
srtp_context->session_keys->rtp_auth->state);
}
if (!external_hmac) {
RTC_LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!."; returnfalse;
}
policy.ssrc.type = static_cast<srtp_ssrc_type_t>(type);
policy.ssrc.value = 0;
policy.key = const_cast<uint8_t*>(key.data()); // TODO(astor) parse window size from WSH session-param
policy.window_size = 1024;
policy.allow_repeat_tx = 1; // If external authentication option is enabled, supply custom auth module // id EXTERNAL_HMAC_SHA1 in the policy structure. // We want to set this option only for rtp packets. // By default policy structure is initialized to HMAC_SHA1. // Enable external HMAC authentication only for outgoing streams and only // for cipher suites that support it (i.e. only non-GCM cipher suites). if (type == ssrc_any_outbound && IsExternalAuthEnabled() &&
!rtc::IsGcmCryptoSuite(crypto_suite)) {
policy.rtp.auth_type = EXTERNAL_HMAC_SHA1;
} if (!extension_ids.empty()) {
policy.enc_xtn_hdr = const_cast<int*>(&extension_ids[0]);
policy.enc_xtn_hdr_count = static_cast<int>(extension_ids.size());
}
policy.next = nullptr;
if (!session_) { int err = srtp_create(&session_, &policy); if (err != srtp_err_status_ok) {
session_ = nullptr;
RTC_LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err; returnfalse;
}
srtp_set_user_data(session_, this);
} else { int err = srtp_update(session_, &policy); if (err != srtp_err_status_ok) {
RTC_LOG(LS_ERROR) << "Failed to update SRTP session, err=" << err; returnfalse;
}
}
bool SrtpSession::SetKey(int type, int crypto_suite, const rtc::ZeroOnFreeBuffer<uint8_t>& key, const std::vector<int>& extension_ids) {
RTC_DCHECK(thread_checker_.IsCurrent()); if (session_) {
RTC_LOG(LS_ERROR) << "Failed to create SRTP session: " "SRTP session already created"; returnfalse;
}
// This is the first time we need to actually interact with libsrtp, so // initialize it if needed. if (LibSrtpInitializer::Get().IncrementLibsrtpUsageCountAndMaybeInit(
&SrtpSession::HandleEventThunk)) {
inited_ = true;
} else { returnfalse;
}
void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) { // Callback will be executed from same thread that calls the "srtp_protect" // and "srtp_unprotect" functions.
SrtpSession* session = static_cast<SrtpSession*>(srtp_get_user_data(ev->session)); if (session) {
session->HandleEvent(ev);
}
}
// Logs the unencrypted packet in text2pcap format. This can then be // extracted by searching for RTP_DUMP // grep RTP_DUMP chrome_debug.log > in.txt // and converted to pcap using // text2pcap -D -u 1000,2000 -t %H:%M:%S. in.txt out.pcap // The resulting file can be replayed using the WebRTC video_replay tool and // be inspected in Wireshark using the RTP, VP8 and H264 dissectors. void SrtpSession::DumpPacket(constvoid* buf, int len, bool outbound) {
int64_t time_of_day = rtc::TimeUTCMillis() % (24 * 3600 * 1000);
int64_t hours = time_of_day / (3600 * 1000);
int64_t minutes = (time_of_day / (60 * 1000)) % 60;
int64_t seconds = (time_of_day / 1000) % 60;
int64_t millis = time_of_day % 1000;
RTC_LOG(LS_VERBOSE) << "\n"
<< (outbound ? "O" : "I") << " " << std::setfill('0')
<< std::setw(2) << hours << ":" << std::setfill('0')
<< std::setw(2) << minutes << ":" << std::setfill('0')
<< std::setw(2) << seconds << "." << std::setfill('0')
<< std::setw(3) << millis << " "
<< "000000 "
<< rtc::hex_encode_with_delimiter(
absl::string_view((constchar*)buf, len), ' ')
<< " # RTP_DUMP";
}
} // namespace cricket
Messung V0.5
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.