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.
This commit is contained in:
Aaro Altonen 2020-04-30 01:45:28 +03:00
parent 527b5e5c09
commit c2438e3017
4 changed files with 132 additions and 15 deletions

View File

@ -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_;
};
};

View File

@ -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

View File

@ -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<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(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;
}

View File

@ -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 {