#define MS_CLASS "RTC::SrtpSession" // #define MS_LOG_DEV_LEVEL 3 #include "SrtpSession.hpp" #include // std::memset(), std::memcpy() #include "logger.h" namespace RTC { /* Static. */ static constexpr size_t EncryptBufferSize{ 65536 }; static uint8_t EncryptBuffer[EncryptBufferSize]; std::vector DepLibSRTP::errors = { // From 0 (srtp_err_status_ok) to 24 (srtp_err_status_pfkey_err). "success (srtp_err_status_ok)", "unspecified failure (srtp_err_status_fail)", "unsupported parameter (srtp_err_status_bad_param)", "couldn't allocate memory (srtp_err_status_alloc_fail)", "couldn't deallocate memory (srtp_err_status_dealloc_fail)", "couldn't initialize (srtp_err_status_init_fail)", "can’t process as much data as requested (srtp_err_status_terminus)", "authentication failure (srtp_err_status_auth_fail)", "cipher failure (srtp_err_status_cipher_fail)", "replay check failed (bad index) (srtp_err_status_replay_fail)", "replay check failed (index too old) (srtp_err_status_replay_old)", "algorithm failed test routine (srtp_err_status_algo_fail)", "unsupported operation (srtp_err_status_no_such_op)", "no appropriate context found (srtp_err_status_no_ctx)", "unable to perform desired validation (srtp_err_status_cant_check)", "can’t use key any more (srtp_err_status_key_expired)", "error in use of socket (srtp_err_status_socket_err)", "error in use POSIX signals (srtp_err_status_signal_err)", "nonce check failed (srtp_err_status_nonce_bad)", "couldn’t read data (srtp_err_status_read_fail)", "couldn’t write data (srtp_err_status_write_fail)", "error parsing data (srtp_err_status_parse_err)", "error encoding data (srtp_err_status_encode_err)", "error while using semaphores (srtp_err_status_semaphore_err)", "error while using pfkey (srtp_err_status_pfkey_err)"}; // clang-format on /* Static methods. */ void DepLibSRTP::ClassInit() { MS_TRACE(); MS_DEBUG_TAG(info, "libsrtp version: \"%s\"", srtp_get_version_string()); srtp_err_status_t err = srtp_init(); if (DepLibSRTP::IsError(err)) MS_THROW_ERROR("srtp_init() failed: %s", DepLibSRTP::GetErrorString(err)); } void DepLibSRTP::ClassDestroy() { MS_TRACE(); srtp_shutdown(); } /* Class methods. */ void SrtpSession::ClassInit() { // Set libsrtp event handler. srtp_err_status_t err = srtp_install_event_handler(static_cast(OnSrtpEvent)); if (DepLibSRTP::IsError(err)) { MS_THROW_ERROR("srtp_install_event_handler() failed: %s", DepLibSRTP::GetErrorString(err)); } } void SrtpSession::OnSrtpEvent(srtp_event_data_t* data) { MS_TRACE(); switch (data->event) { case event_ssrc_collision: MS_WARN_TAG(srtp, "SSRC collision occurred"); break; case event_key_soft_limit: MS_WARN_TAG(srtp, "stream reached the soft key usage limit and will expire soon"); break; case event_key_hard_limit: MS_WARN_TAG(srtp, "stream reached the hard key usage limit and has expired"); break; case event_packet_index_limit: MS_WARN_TAG(srtp, "stream reached the hard packet limit (2^48 packets)"); break; } } /* Instance methods. */ SrtpSession::SrtpSession(Type type, CryptoSuite cryptoSuite, uint8_t* key, size_t keyLen) { MS_TRACE(); srtp_policy_t policy; // NOLINT(cppcoreguidelines-pro-type-member-init) // Set all policy fields to 0. std::memset(&policy, 0, sizeof(srtp_policy_t)); switch (cryptoSuite) { case CryptoSuite::AES_CM_128_HMAC_SHA1_80: { srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); break; } case CryptoSuite::AES_CM_128_HMAC_SHA1_32: { srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); // NOTE: Must be 80 for RTCP. srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); break; } case CryptoSuite::AEAD_AES_256_GCM: { srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp); srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp); break; } case CryptoSuite::AEAD_AES_128_GCM: { srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp); srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp); break; } default: { MS_ABORT("unknown SRTP crypto suite"); } } MS_ASSERT( (int)keyLen == policy.rtp.cipher_key_len, "given keyLen does not match policy.rtp.cipher_keyLen"); switch (type) { case Type::INBOUND: policy.ssrc.type = ssrc_any_inbound; break; case Type::OUTBOUND: policy.ssrc.type = ssrc_any_outbound; break; } policy.ssrc.value = 0; policy.key = key; // Required for sending RTP retransmission without RTX. policy.allow_repeat_tx = 1; policy.window_size = 1024; policy.next = nullptr; // Set the SRTP session. srtp_err_status_t err = srtp_create(&this->session, &policy); if (DepLibSRTP::IsError(err)) MS_THROW_ERROR("srtp_create() failed: %s", DepLibSRTP::GetErrorString(err)); } SrtpSession::~SrtpSession() { MS_TRACE(); if (this->session != nullptr) { srtp_err_status_t err = srtp_dealloc(this->session); if (DepLibSRTP::IsError(err)) MS_ABORT("srtp_dealloc() failed: %s", DepLibSRTP::GetErrorString(err)); } } bool SrtpSession::EncryptRtp(const uint8_t** data, size_t* len) { MS_TRACE(); // Ensure that the resulting SRTP packet fits into the encrypt buffer. if (*len + SRTP_MAX_TRAILER_LEN > EncryptBufferSize) { MS_WARN_TAG(srtp, "cannot encrypt RTP packet, size too big (%zu bytes)", *len); return false; } std::memcpy(EncryptBuffer, *data, *len); srtp_err_status_t err = srtp_protect(this->session, static_cast(EncryptBuffer), reinterpret_cast(len)); if (DepLibSRTP::IsError(err)) { MS_WARN_TAG(srtp, "srtp_protect() failed: %s", DepLibSRTP::GetErrorString(err)); return false; } // Update the given data pointer. *data = (const uint8_t*)EncryptBuffer; return true; } bool SrtpSession::DecryptSrtp(uint8_t* data, size_t* len) { MS_TRACE(); srtp_err_status_t err = srtp_unprotect(this->session, static_cast(data), reinterpret_cast(len)); if (DepLibSRTP::IsError(err)) { MS_DEBUG_TAG(srtp, "srtp_unprotect() failed: %s", DepLibSRTP::GetErrorString(err)); return false; } return true; } bool SrtpSession::EncryptRtcp(const uint8_t** data, size_t* len) { MS_TRACE(); // Ensure that the resulting SRTCP packet fits into the encrypt buffer. if (*len + SRTP_MAX_TRAILER_LEN > EncryptBufferSize) { MS_WARN_TAG(srtp, "cannot encrypt RTCP packet, size too big (%zu bytes)", *len); return false; } std::memcpy(EncryptBuffer, *data, *len); srtp_err_status_t err = srtp_protect_rtcp( this->session, static_cast(EncryptBuffer), reinterpret_cast(len)); if (DepLibSRTP::IsError(err)) { MS_WARN_TAG(srtp, "srtp_protect_rtcp() failed: %s", DepLibSRTP::GetErrorString(err)); return false; } // Update the given data pointer. *data = (const uint8_t*)EncryptBuffer; return true; } bool SrtpSession::DecryptSrtcp(uint8_t* data, size_t* len) { MS_TRACE(); srtp_err_status_t err = srtp_unprotect_rtcp(this->session, static_cast(data), reinterpret_cast(len)); if (DepLibSRTP::IsError(err)) { MS_DEBUG_TAG(srtp, "srtp_unprotect_rtcp() failed: %s", DepLibSRTP::GetErrorString(err)); return false; } return true; } } // namespace RTC