From d4a47807d8af758542a56bca91f33a6f6936e856 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Mon, 25 Aug 2025 17:30:28 +0300 Subject: [PATCH] - improved SRTP support --- src/CMakeLists.txt | 1 + src/engine/endpoint/EP_AudioProvider.cpp | 26 +---- src/engine/helper/CMakeLists.txt | 2 +- src/engine/helper/HL_Types.cpp | 1 + src/engine/helper/HL_Types.h | 133 ++++++++++++++++++++++- src/engine/media/MT_SrtpHelper.cpp | 111 ++++++++++++------- src/engine/media/MT_SrtpHelper.h | 90 ++++++++------- src/libs/ice/ICELog.cpp | 7 ++ src/libs/ice/ICELog.h | 1 + 9 files changed, 267 insertions(+), 105 deletions(-) create mode 100644 src/engine/helper/HL_Types.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7f2a0672..f5f9bbb9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -203,6 +203,7 @@ set (RTPHONE_SOURCES ${E}/helper/HL_Time.cpp ${E}/helper/HL_Time.h ${E}/helper/HL_Types.h + ${E}/helper/HL_Types.cpp ${E}/helper/HL_Usb.cpp ${E}/helper/HL_Usb.h ${E}/helper/HL_Uuid.cpp diff --git a/src/engine/endpoint/EP_AudioProvider.cpp b/src/engine/endpoint/EP_AudioProvider.cpp index 00a0bdc4..2a17d847 100644 --- a/src/engine/endpoint/EP_AudioProvider.cpp +++ b/src/engine/endpoint/EP_AudioProvider.cpp @@ -303,26 +303,12 @@ std::string AudioProvider::createCryptoAttribute(SrtpSuite suite) if (!mActiveStream) return ""; - // Use tag 1 - it is ok, as we use only single crypto attribute - int srtpTag = 1; - // Print key to base64 string PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first; resip::Data d(keyBuffer->data(), keyBuffer->size()); resip::Data keyText = d.base64encode(); - // Create "crypto" attribute value - char buffer[512]; - const char* suiteName = NULL; - switch (suite) - { - case SRTP_AES_128_AUTH_80: suiteName = SRTP_SUITE_NAME_1; break; - case SRTP_AES_256_AUTH_80: suiteName = SRTP_SUITE_NAME_2; break; - default: assert(0); - } - sprintf(buffer, "%d %s inline:%s", srtpTag, suiteName, keyText.c_str()); - - return buffer; + return std::format("{} {} inline:{}", 1, toString(suite), keyText.c_str()); } SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key) @@ -343,15 +329,7 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu resip::Data rawkey = keyText.base64decode(); key = ByteBuffer(rawkey.c_str(), rawkey.size()); - // Open srtp - SrtpSuite result = SRTP_NONE; - if (strcmp(suite, SRTP_SUITE_NAME_1) == 0) - result = SRTP_AES_128_AUTH_80; - else - if (strcmp(suite, SRTP_SUITE_NAME_2) == 0) - result = SRTP_AES_256_AUTH_80; - - return result; + return toSrtpSuite(suite); } void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs) diff --git a/src/engine/helper/CMakeLists.txt b/src/engine/helper/CMakeLists.txt index 5c1c965b..93802c29 100644 --- a/src/engine/helper/CMakeLists.txt +++ b/src/engine/helper/CMakeLists.txt @@ -17,4 +17,4 @@ target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .) target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE) if (TARGET_LINUX) target_link_libraries (helper_lib PUBLIC uuid) -endif() \ No newline at end of file +endif() diff --git a/src/engine/helper/HL_Types.cpp b/src/engine/helper/HL_Types.cpp new file mode 100644 index 00000000..505060b8 --- /dev/null +++ b/src/engine/helper/HL_Types.cpp @@ -0,0 +1 @@ +#include "HL_Types.h" diff --git a/src/engine/helper/HL_Types.h b/src/engine/helper/HL_Types.h index 1f66539b..58cd4d83 100644 --- a/src/engine/helper/HL_Types.h +++ b/src/engine/helper/HL_Types.h @@ -1,4 +1,4 @@ -/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com) +/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -26,4 +26,135 @@ enum SdpDirection Sdp_Offer }; + +#include +#include +#include +#include + +template< + class K, class V, + class HashK = std::hash, class EqK = std::equal_to, + class HashV = std::hash, class EqV = std::equal_to +> +class BiMap { +public: + using key_type = K; + using mapped_type = V; + + BiMap(const std::map& initializers) { + for (const auto& item: initializers) { + insert(item.first, item.second); + } + } + + // Insert a new (key, value) pair. Returns false if either key or value already exists. + bool insert(const K& k, const V& v) { + if (contains_key(k) || contains_value(v)) return false; + auto ok = forward_.emplace(k, v); + try { + auto ov = reverse_.emplace(v, k); + if (!ov.second) { // shouldn't happen given the guard above + forward_.erase(k); + return false; + } + } catch (...) { + forward_.erase(k); + throw; + } + return ok.second; + } + + bool insert(K&& k, V&& v) { + if (contains_key(k) || contains_value(v)) return false; + auto ok = forward_.emplace(std::move(k), std::move(v)); + try { + auto ov = reverse_.emplace(ok.first->second, ok.first->first); // use stored refs + if (!ov.second) { + forward_.erase(ok.first); + return false; + } + } catch (...) { + forward_.erase(ok.first); + throw; + } + return ok.second; + } + + // Replace value for existing key (and update reverse map). Returns false if value is already bound elsewhere. + bool replace_by_key(const K& k, const V& new_v) { + auto it = forward_.find(k); + if (it == forward_.end()) return false; + if (contains_value(new_v)) return false; + // remove old reverse, insert new reverse, then update forward + reverse_.erase(it->second); + reverse_.emplace(new_v, k); + it->second = new_v; + return true; + } + + // Replace key for existing value (and update forward map). Returns false if key is already bound elsewhere. + bool replace_by_value(const V& v, const K& new_k) { + auto it = reverse_.find(v); + if (it == reverse_.end()) return false; + if (contains_key(new_k)) return false; + forward_.erase(it->second); + forward_.emplace(new_k, v); + it->second = new_k; + return true; + } + + // Erase by key/value. Return number erased (0 or 1). + size_t erase_key(const K& k) { + auto it = forward_.find(k); + if (it == forward_.end()) return 0; + reverse_.erase(it->second); + forward_.erase(it); + return 1; + } + + size_t erase_value(const V& v) { + auto it = reverse_.find(v); + if (it == reverse_.end()) return 0; + forward_.erase(it->second); + reverse_.erase(it); + return 1; + } + + // Lookup + bool contains_key(const K& k) const { return forward_.find(k) != forward_.end(); } + bool contains_value(const V& v) const { return reverse_.find(v) != reverse_.end(); } + + const V* find_by_key(const K& k) const { + auto it = forward_.find(k); + return (it == forward_.end()) ? nullptr : &it->second; + } + const K* find_by_value(const V& v) const { + auto it = reverse_.find(v); + return (it == reverse_.end()) ? nullptr : &it->second; + } + + // at() variants throw std::out_of_range on missing entries + const V& at_key(const K& k) const { return forward_.at(k); } + const K& at_value(const V& v) const { return reverse_.at(v); } + + void clear() noexcept { + forward_.clear(); + reverse_.clear(); + } + + bool empty() const noexcept { return forward_.empty(); } + size_t size() const noexcept { return forward_.size(); } + + // Reserve buckets for performance (optional) + void reserve(size_t n) { + forward_.reserve(n); + reverse_.reserve(n); + } + +private: + std::unordered_map forward_; + std::unordered_map reverse_; +}; + #endif diff --git a/src/engine/media/MT_SrtpHelper.cpp b/src/engine/media/MT_SrtpHelper.cpp index 9abb58a3..1b145605 100644 --- a/src/engine/media/MT_SrtpHelper.cpp +++ b/src/engine/media/MT_SrtpHelper.cpp @@ -10,6 +10,50 @@ #include "../helper/HL_Rtp.h" #include #include +#include + +BiMap SrtpSuiteNames{{ + {SRTP_AES_256_AUTH_80, "AES_CM_256_HMAC_SHA1_80"}, + {SRTP_AES_128_AUTH_80, "AES_CM_128_HMAC_SHA1_80"}, + {SRTP_AES_192_AUTH_80, "AES_CM_192_HMAC_SHA1_80"}, + {SRTP_AES_256_AUTH_32, "AES_CM_256_HMAC_SHA1_32"}, + {SRTP_AES_192_AUTH_32, "AES_CM_192_HMAC_SHA1_32"}, + {SRTP_AES_128_AUTH_32, "AES_CM_128_HMAC_SHA1_32"}, + {SRTP_AES_128_AUTH_NULL, "AES_CM_128_NULL_AUTH"}, + {SRTP_AED_AES_256_GCM, "AEAD_AES_256_GCM"}, + {SRTP_AED_AES_128_GCM, "AEAD_AES_128_GCM"}}}; + +extern SrtpSuite toSrtpSuite(const std::string_view& s) +{ + auto* suite = SrtpSuiteNames.find_by_value(s); + return !suite ? SRTP_NONE : *suite; +} + +extern std::string_view toString(SrtpSuite suite) +{ + auto* s = SrtpSuiteNames.find_by_key(suite); + return s ? *s : std::string_view(); +} + +typedef void (*set_srtp_policy_function) (srtp_crypto_policy_t*); + +set_srtp_policy_function findPolicyFunction(SrtpSuite suite) +{ + switch (suite) + { + case SRTP_AES_128_AUTH_80: return &srtp_crypto_policy_set_rtp_default; break; + case SRTP_AES_192_AUTH_80: return &srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80; break; + case SRTP_AES_256_AUTH_80: return &srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80; break; + case SRTP_AES_128_AUTH_32: return &srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32; break; + case SRTP_AES_192_AUTH_32: return &srtp_crypto_policy_set_aes_cm_192_hmac_sha1_32; break; + case SRTP_AES_256_AUTH_32: return &srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32; break; + case SRTP_AES_128_AUTH_NULL: return &srtp_crypto_policy_set_aes_cm_128_null_auth; break; + case SRTP_AED_AES_256_GCM: return &srtp_crypto_policy_set_aes_gcm_256_16_auth; break; + case SRTP_AED_AES_128_GCM: return &srtp_crypto_policy_set_aes_gcm_128_16_auth; break; + default: + throw std::runtime_error(std::format("SRTP suite {} is not supported", toString(suite))); + } +} // --- SrtpStream --- static void configureSrtpStream(SrtpStream& s, uint16_t ssrc, SrtpSuite suite) @@ -17,21 +61,11 @@ static void configureSrtpStream(SrtpStream& s, uint16_t ssrc, SrtpSuite suite) s.second.ssrc.type = ssrc_specific; s.second.ssrc.value = ntohl(ssrc); s.second.next = nullptr; - switch (suite) - { - case SRTP_AES_128_AUTH_80: - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&s.second.rtp); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&s.second.rtcp); - break; - - case SRTP_AES_256_AUTH_80: - srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&s.second.rtp); - srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&s.second.rtcp); - break; - - default: - assert(0); - } + set_srtp_policy_function func = findPolicyFunction(suite); + if (!func) + throw std::runtime_error(std::format("SRTP suite {} is not supported", toString(suite))); + func(&s.second.rtp); + func(&s.second.rtcp); } SrtpSession::SrtpSession() @@ -45,14 +79,19 @@ SrtpSession::SrtpSession() memset(&mOutboundPolicy, 0, sizeof mOutboundPolicy); mOutboundPolicy.ssrc.type = ssrc_specific; - // Generate outgoing keys - mOutgoingKey[SRTP_AES_128_AUTH_80-1].first = std::make_shared(); - mOutgoingKey[SRTP_AES_128_AUTH_80-1].first->resize(30); - RAND_bytes((unsigned char*)mOutgoingKey[SRTP_AES_128_AUTH_80-1].first->mutableData(), 30); + // Generate outgoing keys for all ciphers + auto putKey = [this](SrtpSuite suite, size_t length){ + auto key = std::make_shared(); + key->resize(length); + RAND_bytes(key->mutableData(), key->size()); + mOutgoingKey[suite].first = key; + }; + putKey(SRTP_AES_128_AUTH_80, 30); putKey(SRTP_AES_128_AUTH_32, 30); + putKey(SRTP_AES_192_AUTH_80, 38); putKey(SRTP_AES_192_AUTH_32, 38); + putKey(SRTP_AES_256_AUTH_80, 46); putKey(SRTP_AES_256_AUTH_32, 46); + putKey(SRTP_AED_AES_128_GCM, 28); + putKey(SRTP_AED_AES_256_GCM, 44); - mOutgoingKey[SRTP_AES_256_AUTH_80-1].first = std::make_shared(); - mOutgoingKey[SRTP_AES_256_AUTH_80-1].first->resize(46); - RAND_bytes((unsigned char*)mOutgoingKey[SRTP_AES_256_AUTH_80-1].first->mutableData(), 46); } SrtpSession::~SrtpSession() @@ -106,27 +145,17 @@ void SrtpSession::open(ByteBuffer& incomingKey, SrtpSuite suite) // Save key mIncomingKey.first = std::make_shared(incomingKey); - // Update policy - switch (suite) - { - case SRTP_AES_128_AUTH_80: - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&mInboundPolicy.rtp); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&mInboundPolicy.rtcp); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&mOutboundPolicy.rtp); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&mOutboundPolicy.rtcp); - break; + auto policyFunction = findPolicyFunction(suite); + if (!policyFunction) + throw std::runtime_error(std::format("SRTP suite {} not found", toString(suite))); - case SRTP_AES_256_AUTH_80: - srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&mInboundPolicy.rtp); - srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&mInboundPolicy.rtcp); - srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&mOutboundPolicy.rtp); - srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&mOutboundPolicy.rtcp); - break; + // Configure policies + policyFunction(&mInboundPolicy.rtp); + policyFunction(&mInboundPolicy.rtcp); + policyFunction(&mOutboundPolicy.rtp); + policyFunction(&mOutboundPolicy.rtcp); - case SRTP_NONE: - break; - } - mOutboundPolicy.key = (unsigned char*)mOutgoingKey[int(suite)-1].first->mutableData(); + mOutboundPolicy.key = (unsigned char*)mOutgoingKey[int(suite)].first->mutableData(); mInboundPolicy.key = (unsigned char*)mIncomingKey.first->mutableData(); // Create SRTP session diff --git a/src/engine/media/MT_SrtpHelper.h b/src/engine/media/MT_SrtpHelper.h index aa3304e6..155ba08a 100644 --- a/src/engine/media/MT_SrtpHelper.h +++ b/src/engine/media/MT_SrtpHelper.h @@ -11,63 +11,77 @@ #include #include "libsrtp/include/srtp.h" -#include "../helper/HL_Sync.h" -#include "../helper/HL_ByteBuffer.h" +#include "HL_Sync.h" +#include "HL_ByteBuffer.h" +#include "HL_Types.h" -#define SRTP_SUITE_NAME_2 "AES_CM_256_HMAC_SHA1_80" -#define SRTP_SUITE_NAME_1 "AES_CM_128_HMAC_SHA1_80" +#define NAME_SRTP_AES_256_AUTH_80 "AES_CM_256_HMAC_SHA1_80" +#define NAME_SRTP_AES_128_AUTH_80 "AES_CM_128_HMAC_SHA1_80" enum SrtpSuite { - SRTP_NONE, - SRTP_AES_128_AUTH_80, - SRTP_AES_256_AUTH_80, - SRTP_LAST = SRTP_AES_256_AUTH_80 + SRTP_NONE, + SRTP_AES_128_AUTH_80, + SRTP_AES_256_AUTH_80, + SRTP_AES_192_AUTH_80, + SRTP_AES_128_AUTH_32, + SRTP_AES_256_AUTH_32, + SRTP_AES_192_AUTH_32, + SRTP_AES_128_AUTH_NULL, + SRTP_AED_AES_256_GCM, + SRTP_AED_AES_128_GCM, + SRTP_LAST = SRTP_AED_AES_128_GCM + // ToDo: + // a=crypto:1 AEAD_AES_256_GCM_8 inline:tN2A0vRjFBimpQsW2GasuJuPe7hKE26gki30APC8DVuySqCOYTs8lYBPR5I= + // a=crypto:3 AEAD_AES_128_GCM_8 inline:Ok7VL8SmBHSbZLw4dK6iQgpliYKGdY9BHLJcRw== }; +extern SrtpSuite toSrtpSuite(const std::string_view& s); +extern std::string_view toString(SrtpSuite suite); + typedef std::pair SrtpKeySalt; typedef std::pair SrtpStream; class SrtpSession { public: - SrtpSession(); - ~SrtpSession(); - - enum SsrcDirection - { - sdIncoming, - sdOutgoing - }; - SrtpKeySalt& outgoingKey(SrtpSuite suite); + SrtpSession(); + ~SrtpSession(); - void open(ByteBuffer& incomingKey, SrtpSuite suite); - void close(); - bool active(); + enum SsrcDirection + { + sdIncoming, + sdOutgoing + }; + SrtpKeySalt& outgoingKey(SrtpSuite suite); - /* bufferPtr is RTP packet data i.e. header + payload. Buffer must be big enough to hold encrypted data. */ - bool protectRtp(void* buffer, int* length); - bool protectRtcp(void* buffer, int* length); - bool unprotectRtp(const void* src, size_t srcLength, void* dst, size_t* dstLength); - bool unprotectRtcp(const void* src, size_t srcLength, void* dst, size_t* dstLength); - + void open(ByteBuffer& incomingKey, SrtpSuite suite); + void close(); + bool active(); - static void initSrtp(); + /* bufferPtr is RTP packet data i.e. header + payload. Buffer must be big enough to hold encrypted data. */ + bool protectRtp(void* buffer, int* length); + bool protectRtcp(void* buffer, int* length); + bool unprotectRtp(const void* src, size_t srcLength, void* dst, size_t* dstLength); + bool unprotectRtcp(const void* src, size_t srcLength, void* dst, size_t* dstLength); + + + static void initSrtp(); protected: - srtp_t mInboundSession, - mOutboundSession; + srtp_t mInboundSession, + mOutboundSession; - SrtpKeySalt mIncomingKey, - mOutgoingKey[SRTP_LAST]; - srtp_policy_t mInboundPolicy; - srtp_policy_t mOutboundPolicy; - SrtpSuite mSuite; + SrtpKeySalt mIncomingKey, + mOutgoingKey[SRTP_LAST]; + srtp_policy_t mInboundPolicy; + srtp_policy_t mOutboundPolicy; + SrtpSuite mSuite; - typedef std::map SrtpStreamMap; - SrtpStreamMap mIncomingMap, mOutgoingMap; - Mutex mGuard; + typedef std::map SrtpStreamMap; + SrtpStreamMap mIncomingMap, mOutgoingMap; + Mutex mGuard; - void addSsrc(unsigned ssrc, SsrcDirection d); + void addSsrc(unsigned ssrc, SsrcDirection d); }; #endif diff --git a/src/libs/ice/ICELog.cpp b/src/libs/ice/ICELog.cpp index 139eba9c..f6720a5c 100644 --- a/src/libs/ice/ICELog.cpp +++ b/src/libs/ice/ICELog.cpp @@ -304,3 +304,10 @@ Logger::operator << (const std::string& data) *mStream << data.c_str(); return *this; } + +Logger& +Logger::operator << (const std::string_view& data) +{ + *mStream << data; + return *this; +} diff --git a/src/libs/ice/ICELog.h b/src/libs/ice/ICELog.h index e13641ea..5feac16f 100644 --- a/src/libs/ice/ICELog.h +++ b/src/libs/ice/ICELog.h @@ -121,6 +121,7 @@ public: Logger& operator << (const int data); Logger& operator << (const float data); Logger& operator << (const std::string& data); + Logger& operator << (const std::string_view& data); Logger& operator << (const int64_t data); Logger& operator << (const unsigned int data); Logger& operator << (const uint64_t data);