From c2438e301758cf4a5e892ce93c3c46e25ffa09e9 Mon Sep 17 00:00:00 2001 From: Aaro Altonen Date: Thu, 30 Apr 2020 01:45:28 +0300 Subject: [PATCH] Add support for user-managed SRTP session Because the architecture has changed a bit, the context is initialized only after add_srtp_ctx() has been called to prevent the user from sending unencrypted messages. --- include/media_stream.hh | 20 +++++++- include/util.hh | 2 +- src/media_stream.cc | 101 ++++++++++++++++++++++++++++++++++++++-- src/session.cc | 24 ++++++---- 4 files changed, 132 insertions(+), 15 deletions(-) diff --git a/include/media_stream.hh b/include/media_stream.hh index 09b83a7..a2d6c08 100644 --- a/include/media_stream.hh +++ b/include/media_stream.hh @@ -38,6 +38,21 @@ namespace uvg_rtp { * Other error return codes are defined in {conn,writer,reader,srtp}.hh */ #ifdef __RTP_CRYPTO__ rtp_error_t init(uvg_rtp::zrtp *zrtp); + + /* Add key for user-managed SRTP session + * + * For user-managed SRTP session, the media stream is not started + * until SRTP key has been added and all calls to push_frame() will fail + * + * Currently uvgRTP only supports key length of 16 bytes (128 bits) + * and salt length of 14 bytes (112 bits). + * If the key/salt is longer, it is implicitly truncated to correct length + * and if the key/salt is shorter a memory violation may occur + * + * Return RTP_OK on success + * Return RTP_INVALID_VALUE if "key" or "salt" is invalid + * Return RTP_NOT_SUPPORTED if user-managed SRTP was not specified in create_stream() */ + rtp_error_t add_srtp_ctx(uint8_t *key, uint8_t *salt); #endif /* Split "data" into 1500 byte chunks and send them to remote @@ -119,7 +134,7 @@ namespace uvg_rtp { void *get_media_config(); /* Overwrite the payload type set during initialization */ - void set_dynamic_payload(uint8_t payload); + rtp_error_t set_dynamic_payload(uint8_t payload); /* Get unique key of the media stream * Used by session to index media streams */ @@ -152,5 +167,8 @@ namespace uvg_rtp { /* Media config f.ex. for Opus */ void *media_config_; + + /* Has the media stream been initialized */ + bool initialized_; }; }; diff --git a/include/util.hh b/include/util.hh index 4bd90ca..5ee7fcf 100644 --- a/include/util.hh +++ b/include/util.hh @@ -106,7 +106,7 @@ enum RTP_CTX_ENABLE_FLAGS { /* Use user-defined way to manage keys * * TODO selitä paremmin */ - RCE_SRTP_KMNGMNT_USER = 1 << 4, + RCE_SRTP_KMNGMNT_USER = 1 << 5, /* When uvgRTP is receiving HEVC stream, as an attempt to improve * QoS, it will set frame delay for intra frames to be the same diff --git a/src/media_stream.cc b/src/media_stream.cc index 430420f..7557ca4 100644 --- a/src/media_stream.cc +++ b/src/media_stream.cc @@ -8,8 +8,12 @@ uvg_rtp::media_stream::media_stream(std::string addr, int src_port, int dst_port, rtp_format_t fmt, int flags): srtp_(nullptr), socket_(), + sender_(nullptr), + receiver_(nullptr), + rtp_(nullptr), ctx_config_(), - media_config_(nullptr) + media_config_(nullptr), + initialized_(false) { fmt_ = fmt; addr_ = addr; @@ -34,8 +38,10 @@ uvg_rtp::media_stream::media_stream( uvg_rtp::media_stream::~media_stream() { - sender_->destroy(); - receiver_->stop(); + if (initialized_) { + sender_->destroy(); + receiver_->stop(); + } delete sender_; delete receiver_; @@ -96,6 +102,8 @@ rtp_error_t uvg_rtp::media_stream::init() sender_->init(); receiver_->start(); + initialized_ = true; + return RTP_OK; } @@ -138,27 +146,90 @@ rtp_error_t uvg_rtp::media_stream::init(uvg_rtp::zrtp *zrtp) sender_->init(); receiver_->start(); + initialized_ = true; + + return ret; +} + +rtp_error_t uvg_rtp::media_stream::add_srtp_ctx(uint8_t *key, uint8_t *salt) +{ + if (!key || !salt) + return RTP_INVALID_VALUE; + + unsigned srtp_flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER; + rtp_error_t ret = RTP_OK; + + if ((flags_ & srtp_flags) != srtp_flags) + return RTP_NOT_SUPPORTED; + + if (init_connection() != RTP_OK) { + LOG_ERROR("Failed to initialize the underlying socket: %s!", strerror(errno)); + return RTP_GENERIC_ERROR; + } + + if ((rtp_ = new uvg_rtp::rtp(fmt_)) == nullptr) + return RTP_MEMORY_ERROR; + + if ((srtp_ = new uvg_rtp::srtp()) == nullptr) + return RTP_MEMORY_ERROR; + + if ((ret = srtp_->init_user(SRTP, key, salt)) != RTP_OK) { + LOG_WARN("Failed to initialize SRTP for media stream!"); + return ret; + } + + socket_.set_srtp(srtp_); + + sender_ = new uvg_rtp::sender(socket_, ctx_config_, fmt_, rtp_); + receiver_ = new uvg_rtp::receiver(socket_, ctx_config_, fmt_, rtp_); + + sender_->init(); + receiver_->start(); + + initialized_ = true; + return ret; } #endif rtp_error_t uvg_rtp::media_stream::push_frame(uint8_t *data, size_t data_len, int flags) { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + return RTP_NOT_INITIALIZED; + } + return sender_->push_frame(data, data_len, flags); } rtp_error_t uvg_rtp::media_stream::push_frame(std::unique_ptr data, size_t data_len, int flags) { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + return RTP_NOT_INITIALIZED; + } + return sender_->push_frame(std::move(data), data_len, flags); } uvg_rtp::frame::rtp_frame *uvg_rtp::media_stream::pull_frame() { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + rtp_errno = RTP_NOT_INITIALIZED; + return nullptr; + } + return receiver_->pull_frame(); } rtp_error_t uvg_rtp::media_stream::install_receive_hook(void *arg, void (*hook)(void *, uvg_rtp::frame::rtp_frame *)) { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + return RTP_NOT_INITIALIZED; + } + if (!hook) return RTP_INVALID_VALUE; @@ -169,6 +240,11 @@ rtp_error_t uvg_rtp::media_stream::install_receive_hook(void *arg, void (*hook)( rtp_error_t uvg_rtp::media_stream::install_deallocation_hook(void (*hook)(void *)) { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + return RTP_NOT_INITIALIZED; + } + if (!hook) return RTP_INVALID_VALUE; @@ -179,6 +255,11 @@ rtp_error_t uvg_rtp::media_stream::install_deallocation_hook(void (*hook)(void * rtp_error_t uvg_rtp::media_stream::install_notify_hook(void *arg, void (*hook)(void *, int)) { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + return RTP_NOT_INITIALIZED; + } + if (!hook) return RTP_INVALID_VALUE; @@ -199,6 +280,11 @@ void *uvg_rtp::media_stream::get_media_config() rtp_error_t uvg_rtp::media_stream::configure_ctx(int flag, ssize_t value) { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + return RTP_NOT_INITIALIZED; + } + rtp_error_t ret = RTP_OK; switch (flag) { @@ -234,7 +320,14 @@ uint32_t uvg_rtp::media_stream::get_key() return key_; } -void uvg_rtp::media_stream::set_dynamic_payload(uint8_t payload) +rtp_error_t uvg_rtp::media_stream::set_dynamic_payload(uint8_t payload) { + if (!initialized_) { + LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); + return RTP_NOT_INITIALIZED; + } + rtp_->set_dynamic_payload(payload); + + return RTP_OK; } diff --git a/src/session.cc b/src/session.cc index 04eafe4..0ba65e6 100644 --- a/src/session.cc +++ b/src/session.cc @@ -45,16 +45,22 @@ uvg_rtp::media_stream *uvg_rtp::session::create_stream(int r_port, int s_port, r } #ifdef __RTP_CRYPTO__ - int zrtp_flags = (RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP); + if (flags & RCE_SRTP) { + if (flags & RCE_SRTP_KMNGMNT_ZRTP) { + if ((zrtp_ = new uvg_rtp::zrtp()) == nullptr) { + rtp_errno = RTP_MEMORY_ERROR; + return nullptr; + } - if ((flags & zrtp_flags) == zrtp_flags) { - if ((zrtp_ = new uvg_rtp::zrtp()) == nullptr) { - rtp_errno = RTP_MEMORY_ERROR; - return nullptr; - } - - if (stream->init(zrtp_) != RTP_OK) { - LOG_ERROR("Failed to initialize media stream %s:%d/%d", addr_.c_str(), r_port, s_port); + if (stream->init(zrtp_) != RTP_OK) { + LOG_ERROR("Failed to initialize media stream %s:%d/%d", addr_.c_str(), r_port, s_port); + return nullptr; + } + } else if (flags & RCE_SRTP_KMNGMNT_USER) { + LOG_DEBUG("SRTP with user-managed keys enabled, postpone initialization"); + } else { + LOG_ERROR("SRTP key management scheme not specified!"); + rtp_errno = RTP_INVALID_VALUE; return nullptr; } } else {