/* * libjingle * Copyright 2009 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #undef HAVE_CONFIG_H #include "talk/session/media/srtpfilter.h" #include #include #include "talk/media/base/rtputils.h" #include "webrtc/base/base64.h" #include "webrtc/base/byteorder.h" #include "webrtc/base/common.h" #include "webrtc/base/logging.h" #include "webrtc/base/stringencode.h" #include "webrtc/base/timeutils.h" // Enable this line to turn on SRTP debugging // #define SRTP_DEBUG #ifdef HAVE_SRTP extern "C" { #ifdef SRTP_RELATIVE_PATH #include "srtp.h" // NOLINT #include "srtp_priv.h" // NOLINT #else #include "third_party/libsrtp/srtp/include/srtp.h" #include "third_party/libsrtp/srtp/include/srtp_priv.h" #endif // SRTP_RELATIVE_PATH } #ifdef ENABLE_EXTERNAL_AUTH #include "talk/session/media/externalhmac.h" #endif // ENABLE_EXTERNAL_AUTH #if !defined(NDEBUG) extern "C" debug_module_t mod_srtp; extern "C" debug_module_t mod_auth; extern "C" debug_module_t mod_cipher; extern "C" debug_module_t mod_stat; extern "C" debug_module_t mod_alloc; extern "C" debug_module_t mod_aes_icm; extern "C" debug_module_t mod_aes_hmac; #endif #else // SrtpFilter needs that constant. #define SRTP_MASTER_KEY_LEN 30 #endif // HAVE_SRTP namespace cricket { const int SRTP_MASTER_KEY_BASE64_LEN = SRTP_MASTER_KEY_LEN * 4 / 3; const int SRTP_MASTER_KEY_KEY_LEN = 16; const int SRTP_MASTER_KEY_SALT_LEN = 14; #ifndef HAVE_SRTP // This helper function is used on systems that don't (yet) have SRTP, // to log that the functions that require it won't do anything. namespace { bool SrtpNotAvailable(const char *func) { LOG(LS_ERROR) << func << ": SRTP is not available on your system."; return false; } } // anonymous namespace #endif // !HAVE_SRTP void EnableSrtpDebugging() { #ifdef HAVE_SRTP #if !defined(NDEBUG) debug_on(mod_srtp); debug_on(mod_auth); debug_on(mod_cipher); debug_on(mod_stat); debug_on(mod_alloc); debug_on(mod_aes_icm); // debug_on(mod_aes_cbc); // debug_on(mod_hmac); #endif #endif // HAVE_SRTP } // NOTE: This is called from ChannelManager D'tor. void ShutdownSrtp() { #ifdef HAVE_SRTP // If srtp_dealloc is not executed then this will clear all existing sessions. // This should be called when application is shutting down. SrtpSession::Terminate(); #endif } SrtpFilter::SrtpFilter() : state_(ST_INIT), signal_silent_time_in_ms_(0) { } SrtpFilter::~SrtpFilter() { } bool SrtpFilter::IsActive() const { return state_ >= ST_ACTIVE; } bool SrtpFilter::SetOffer(const std::vector& offer_params, ContentSource source) { if (!ExpectOffer(source)) { LOG(LS_ERROR) << "Wrong state to update SRTP offer"; return false; } return StoreParams(offer_params, source); } bool SrtpFilter::SetAnswer(const std::vector& answer_params, ContentSource source) { return DoSetAnswer(answer_params, source, true); } bool SrtpFilter::SetProvisionalAnswer( const std::vector& answer_params, ContentSource source) { return DoSetAnswer(answer_params, source, false); } bool SrtpFilter::SetRtpParams(const std::string& send_cs, const uint8_t* send_key, int send_key_len, const std::string& 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(); if (!send_session_->SetSend(send_cs, send_key, send_key_len)) return false; 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; } // 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(const std::string& send_cs, const uint8_t* send_key, int send_key_len, const std::string& 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()); SignalSrtpError.repeat(send_rtcp_session_->SignalSrtpError); send_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms_); if (!send_rtcp_session_->SetRecv(send_cs, send_key, send_key_len)) return false; recv_rtcp_session_.reset(new SrtpSession()); SignalSrtpError.repeat(recv_rtcp_session_->SignalSrtpError); recv_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms_); 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; } ASSERT(send_session_ != NULL); 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; } ASSERT(send_session_ != NULL); 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 { ASSERT(send_session_ != NULL); 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; } ASSERT(recv_session_ != NULL); 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 { ASSERT(recv_session_ != NULL); 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; } ASSERT(send_session_ != NULL); return send_session_->GetRtpAuthParams(key, key_len, tag_len); } void SrtpFilter::set_signal_silent_time(uint32_t signal_silent_time_in_ms) { signal_silent_time_in_ms_ = signal_silent_time_in_ms; if (IsActive()) { ASSERT(send_session_ != NULL); send_session_->set_signal_silent_time(signal_silent_time_in_ms); ASSERT(recv_session_ != NULL); recv_session_->set_signal_silent_time(signal_silent_time_in_ms); if (send_rtcp_session_) send_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms); if (recv_rtcp_session_) recv_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms); } } bool SrtpFilter::ExpectOffer(ContentSource source) { return ((state_ == ST_INIT) || (state_ == ST_ACTIVE) || (state_ == ST_SENTOFFER && source == CS_LOCAL) || (state_ == ST_SENTUPDATEDOFFER && source == CS_LOCAL) || (state_ == ST_RECEIVEDOFFER && source == CS_REMOTE) || (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_REMOTE)); } bool SrtpFilter::StoreParams(const std::vector& params, ContentSource source) { offer_params_ = params; if (state_ == ST_INIT) { state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER; } else if (state_ == ST_ACTIVE) { state_ = (source == CS_LOCAL) ? ST_SENTUPDATEDOFFER : ST_RECEIVEDUPDATEDOFFER; } return true; } bool SrtpFilter::ExpectAnswer(ContentSource source) { return ((state_ == ST_SENTOFFER && source == CS_REMOTE) || (state_ == ST_RECEIVEDOFFER && source == CS_LOCAL) || (state_ == ST_SENTUPDATEDOFFER && source == CS_REMOTE) || (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_LOCAL) || (state_ == ST_SENTPRANSWER_NO_CRYPTO && source == CS_LOCAL) || (state_ == ST_SENTPRANSWER && source == CS_LOCAL) || (state_ == ST_RECEIVEDPRANSWER_NO_CRYPTO && source == CS_REMOTE) || (state_ == ST_RECEIVEDPRANSWER && source == CS_REMOTE)); } bool SrtpFilter::DoSetAnswer(const std::vector& answer_params, ContentSource source, bool final) { if (!ExpectAnswer(source)) { LOG(LS_ERROR) << "Invalid state for SRTP answer"; return false; } // If the answer doesn't requests crypto complete the negotiation of an // unencrypted session. // Otherwise, finalize the parameters and apply them. if (answer_params.empty()) { if (final) { return ResetParams(); } else { // Need to wait for the final answer to decide if // we should go to Active state. state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER_NO_CRYPTO : ST_RECEIVEDPRANSWER_NO_CRYPTO; return true; } } CryptoParams selected_params; if (!NegotiateParams(answer_params, &selected_params)) return false; const CryptoParams& send_params = (source == CS_REMOTE) ? selected_params : answer_params[0]; const CryptoParams& recv_params = (source == CS_REMOTE) ? answer_params[0] : selected_params; if (!ApplyParams(send_params, recv_params)) { return false; } if (final) { offer_params_.clear(); state_ = ST_ACTIVE; } else { state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER : ST_RECEIVEDPRANSWER; } return true; } void SrtpFilter::CreateSrtpSessions() { send_session_.reset(new SrtpSession()); applied_send_params_ = CryptoParams(); recv_session_.reset(new SrtpSession()); applied_recv_params_ = CryptoParams(); SignalSrtpError.repeat(send_session_->SignalSrtpError); SignalSrtpError.repeat(recv_session_->SignalSrtpError); send_session_->set_signal_silent_time(signal_silent_time_in_ms_); recv_session_->set_signal_silent_time(signal_silent_time_in_ms_); } bool SrtpFilter::NegotiateParams(const std::vector& answer_params, CryptoParams* selected_params) { // We're processing an accept. We should have exactly one set of params, // unless the offer didn't mention crypto, in which case we shouldn't be here. bool ret = (answer_params.size() == 1U && !offer_params_.empty()); if (ret) { // We should find a match between the answer params and the offered params. std::vector::const_iterator it; for (it = offer_params_.begin(); it != offer_params_.end(); ++it) { if (answer_params[0].Matches(*it)) { break; } } if (it != offer_params_.end()) { *selected_params = *it; } else { ret = false; } } if (!ret) { LOG(LS_WARNING) << "Invalid parameters in SRTP answer"; } return ret; } 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 && 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; } // TODO(juberti): Zero these buffers after use. bool ret; uint8_t send_key[SRTP_MASTER_KEY_LEN], recv_key[SRTP_MASTER_KEY_LEN]; ret = (ParseKeyParams(send_params.key_params, send_key, sizeof(send_key)) && ParseKeyParams(recv_params.key_params, recv_key, sizeof(recv_key))); if (ret) { CreateSrtpSessions(); ret = (send_session_->SetSend(send_params.cipher_suite, send_key, sizeof(send_key)) && recv_session_->SetRecv(recv_params.cipher_suite, recv_key, sizeof(recv_key))); } 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; LOG(LS_INFO) << "SRTP reset to init state"; return true; } bool SrtpFilter::ParseKeyParams(const std::string& key_params, uint8_t* key, int len) { // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2" // Fail if key-method is wrong. if (key_params.find("inline:") != 0) { return false; } // Fail if base64 decode fails, or the key is the wrong size. std::string key_b64(key_params.substr(7)), key_str; if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, &key_str, NULL) || static_cast(key_str.size()) != len) { return false; } memcpy(key, key_str.c_str(), len); return true; } /////////////////////////////////////////////////////////////////////////////// // SrtpSession #ifdef HAVE_SRTP bool SrtpSession::inited_ = false; // This lock protects SrtpSession::inited_ and SrtpSession::sessions_. rtc::GlobalLockPod SrtpSession::lock_; SrtpSession::SrtpSession() : session_(NULL), rtp_auth_tag_len_(0), rtcp_auth_tag_len_(0), srtp_stat_(new SrtpStat()), last_send_seq_num_(-1) { { rtc::GlobalLockScope ls(&lock_); sessions()->push_back(this); } SignalSrtpError.repeat(srtp_stat_->SignalSrtpError); } SrtpSession::~SrtpSession() { { rtc::GlobalLockScope ls(&lock_); sessions()->erase(std::find(sessions()->begin(), sessions()->end(), this)); } if (session_) { srtp_dealloc(session_); } } bool SrtpSession::SetSend(const std::string& cs, const uint8_t* key, int len) { return SetKey(ssrc_any_outbound, cs, key, len); } bool SrtpSession::SetRecv(const std::string& cs, const uint8_t* key, int len) { return SetKey(ssrc_any_inbound, cs, key, len); } bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { if (!session_) { LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session"; return false; } int need_len = in_len + rtp_auth_tag_len_; // NOLINT if (max_len < need_len) { LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length " << max_len << " is less than the needed " << need_len; return false; } *out_len = in_len; int err = srtp_protect(session_, p, out_len); uint32_t ssrc; if (GetRtpSsrc(p, in_len, &ssrc)) { srtp_stat_->AddProtectRtpResult(ssrc, err); } int seq_num; GetRtpSeqNum(p, in_len, &seq_num); if (err != err_status_ok) { LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" << seq_num << ", err=" << err << ", last seqnum=" << last_send_seq_num_; return false; } last_send_seq_num_ = seq_num; return true; } 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)) { return false; } return (index) ? GetSendStreamPacketIndex(p, in_len, index) : true; } bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { if (!session_) { LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session"; return false; } int need_len = in_len + sizeof(uint32_t) + rtcp_auth_tag_len_; // NOLINT if (max_len < need_len) { LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length " << max_len << " is less than the needed " << need_len; return false; } *out_len = in_len; int err = srtp_protect_rtcp(session_, p, out_len); srtp_stat_->AddProtectRtcpResult(err); if (err != err_status_ok) { LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err; return false; } return true; } bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) { if (!session_) { LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session"; return false; } *out_len = in_len; int err = srtp_unprotect(session_, p, out_len); uint32_t ssrc; if (GetRtpSsrc(p, in_len, &ssrc)) { srtp_stat_->AddUnprotectRtpResult(ssrc, err); } if (err != err_status_ok) { LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err; return false; } return true; } bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) { if (!session_) { LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session"; return false; } *out_len = in_len; int err = srtp_unprotect_rtcp(session_, p, out_len); srtp_stat_->AddUnprotectRtcpResult(err); if (err != err_status_ok) { LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err; return false; } return true; } bool SrtpSession::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) { #if defined(ENABLE_EXTERNAL_AUTH) ExternalHmacContext* external_hmac = NULL; // 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->rtp_auth) { external_hmac = reinterpret_cast( srtp_context->rtp_auth->state); } if (!external_hmac) { LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!."; return false; } *key = external_hmac->key; *key_len = external_hmac->key_length; *tag_len = rtp_auth_tag_len_; return true; #else return false; #endif } bool SrtpSession::GetSendStreamPacketIndex(void* p, int in_len, int64_t* index) { srtp_hdr_t* hdr = reinterpret_cast(p); srtp_stream_ctx_t* stream = srtp_get_stream(session_, hdr->ssrc); if (stream == NULL) return false; // Shift packet index, put into network byte order *index = static_cast( rtc::NetworkToHost64(rdbx_get_packet_index(&stream->rtp_rdbx) << 16)); return true; } void SrtpSession::set_signal_silent_time(uint32_t signal_silent_time_in_ms) { srtp_stat_->set_signal_silent_time(signal_silent_time_in_ms); } bool SrtpSession::SetKey(int type, const std::string& cs, const uint8_t* key, int len) { if (session_) { LOG(LS_ERROR) << "Failed to create SRTP session: " << "SRTP session already created"; return false; } if (!Init()) { return false; } srtp_policy_t policy; memset(&policy, 0, sizeof(policy)); if (cs == rtc::CS_AES_CM_128_HMAC_SHA1_80) { crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); } else if (cs == rtc::CS_AES_CM_128_HMAC_SHA1_32) { crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); // rtp is 32, crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // rtcp still 80 } else { LOG(LS_WARNING) << "Failed to create SRTP session: unsupported" << " cipher_suite " << cs.c_str(); return false; } if (!key || len != SRTP_MASTER_KEY_LEN) { LOG(LS_WARNING) << "Failed to create SRTP session: invalid key"; return false; } policy.ssrc.type = static_cast(type); policy.ssrc.value = 0; policy.key = const_cast(key); // 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. #if defined(ENABLE_EXTERNAL_AUTH) // Enable external HMAC authentication only for outgoing streams. if (type == ssrc_any_outbound) { policy.rtp.auth_type = EXTERNAL_HMAC_SHA1; } #endif policy.next = NULL; int err = srtp_create(&session_, &policy); if (err != err_status_ok) { session_ = NULL; LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err; return false; } rtp_auth_tag_len_ = policy.rtp.auth_tag_len; rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len; return true; } bool SrtpSession::Init() { rtc::GlobalLockScope ls(&lock_); if (!inited_) { int err; err = srtp_init(); if (err != err_status_ok) { LOG(LS_ERROR) << "Failed to init SRTP, err=" << err; return false; } err = srtp_install_event_handler(&SrtpSession::HandleEventThunk); if (err != err_status_ok) { LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err; return false; } #if defined(ENABLE_EXTERNAL_AUTH) err = external_crypto_init(); if (err != err_status_ok) { LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err; return false; } #endif inited_ = true; } return true; } void SrtpSession::Terminate() { rtc::GlobalLockScope ls(&lock_); if (inited_) { int err = srtp_shutdown(); if (err) { LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err; return; } inited_ = false; } } void SrtpSession::HandleEvent(const srtp_event_data_t* ev) { switch (ev->event) { case event_ssrc_collision: LOG(LS_INFO) << "SRTP event: SSRC collision"; break; case event_key_soft_limit: LOG(LS_INFO) << "SRTP event: reached soft key usage limit"; break; case event_key_hard_limit: LOG(LS_INFO) << "SRTP event: reached hard key usage limit"; break; case event_packet_index_limit: LOG(LS_INFO) << "SRTP event: reached hard packet limit (2^48 packets)"; break; default: LOG(LS_INFO) << "SRTP event: unknown " << ev->event; break; } } void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) { rtc::GlobalLockScope ls(&lock_); for (std::list::iterator it = sessions()->begin(); it != sessions()->end(); ++it) { if ((*it)->session_ == ev->session) { (*it)->HandleEvent(ev); break; } } } std::list* SrtpSession::sessions() { RTC_DEFINE_STATIC_LOCAL(std::list, sessions, ()); return &sessions; } #else // !HAVE_SRTP // On some systems, SRTP is not (yet) available. SrtpSession::SrtpSession() { LOG(WARNING) << "SRTP implementation is missing."; } SrtpSession::~SrtpSession() { } bool SrtpSession::SetSend(const std::string& cs, const uint8_t* key, int len) { return SrtpNotAvailable(__FUNCTION__); } bool SrtpSession::SetRecv(const std::string& cs, const uint8_t* key, int len) { return SrtpNotAvailable(__FUNCTION__); } bool SrtpSession::ProtectRtp(void* data, int in_len, int max_len, int* out_len) { return SrtpNotAvailable(__FUNCTION__); } bool SrtpSession::ProtectRtcp(void* data, int in_len, int max_len, int* out_len) { return SrtpNotAvailable(__FUNCTION__); } bool SrtpSession::UnprotectRtp(void* data, int in_len, int* out_len) { return SrtpNotAvailable(__FUNCTION__); } bool SrtpSession::UnprotectRtcp(void* data, int in_len, int* out_len) { return SrtpNotAvailable(__FUNCTION__); } void SrtpSession::set_signal_silent_time(uint32_t signal_silent_time) { // Do nothing. } #endif // HAVE_SRTP /////////////////////////////////////////////////////////////////////////////// // SrtpStat #ifdef HAVE_SRTP SrtpStat::SrtpStat() : signal_silent_time_(1000) { } void SrtpStat::AddProtectRtpResult(uint32_t ssrc, int result) { FailureKey key; key.ssrc = ssrc; key.mode = SrtpFilter::PROTECT; switch (result) { case err_status_ok: key.error = SrtpFilter::ERROR_NONE; break; case err_status_auth_fail: key.error = SrtpFilter::ERROR_AUTH; break; default: key.error = SrtpFilter::ERROR_FAIL; } HandleSrtpResult(key); } void SrtpStat::AddUnprotectRtpResult(uint32_t ssrc, int result) { FailureKey key; key.ssrc = ssrc; key.mode = SrtpFilter::UNPROTECT; switch (result) { case err_status_ok: key.error = SrtpFilter::ERROR_NONE; break; case err_status_auth_fail: key.error = SrtpFilter::ERROR_AUTH; break; case err_status_replay_fail: case err_status_replay_old: key.error = SrtpFilter::ERROR_REPLAY; break; default: key.error = SrtpFilter::ERROR_FAIL; } HandleSrtpResult(key); } void SrtpStat::AddProtectRtcpResult(int result) { AddProtectRtpResult(0U, result); } void SrtpStat::AddUnprotectRtcpResult(int result) { AddUnprotectRtpResult(0U, result); } void SrtpStat::HandleSrtpResult(const SrtpStat::FailureKey& key) { // Handle some cases where error should be signalled right away. For other // errors, trigger error for the first time seeing it. After that, silent // the same error for a certain amount of time (default 1 sec). if (key.error != SrtpFilter::ERROR_NONE) { // For errors, signal first time and wait for 1 sec. FailureStat* stat = &(failures_[key]); uint32_t current_time = rtc::Time(); if (stat->last_signal_time == 0 || rtc::TimeDiff(current_time, stat->last_signal_time) > static_cast(signal_silent_time_)) { SignalSrtpError(key.ssrc, key.mode, key.error); stat->last_signal_time = current_time; } } } #else // !HAVE_SRTP // On some systems, SRTP is not (yet) available. SrtpStat::SrtpStat() : signal_silent_time_(1000) { LOG(WARNING) << "SRTP implementation is missing."; } void SrtpStat::AddProtectRtpResult(uint32_t ssrc, int result) { SrtpNotAvailable(__FUNCTION__); } void SrtpStat::AddUnprotectRtpResult(uint32_t ssrc, int result) { SrtpNotAvailable(__FUNCTION__); } void SrtpStat::AddProtectRtcpResult(int result) { SrtpNotAvailable(__FUNCTION__); } void SrtpStat::AddUnprotectRtcpResult(int result) { SrtpNotAvailable(__FUNCTION__); } void SrtpStat::HandleSrtpResult(const SrtpStat::FailureKey& key) { SrtpNotAvailable(__FUNCTION__); } #endif // HAVE_SRTP } // namespace cricket