Add initial SRTP initialization

Current architecture is not optimal for ZRTP negotiations will require
some rethinking but it'll have to do for now.
This commit is contained in:
Aaro Altonen 2020-01-22 10:57:40 +02:00
parent 89f31a16e0
commit 911a86f3b9
12 changed files with 392 additions and 27 deletions

View File

@ -25,13 +25,16 @@ kvz_rtp::connection::connection(rtp_format_t fmt, bool reader):
wc_start_(0),
fqueue_(nullptr)
{
rtp_sequence_ = 1; //kvz_rtp::random::generate_32();
rtp_sequence_ = kvz_rtp::random::generate_32();
rtp_ssrc_ = kvz_rtp::random::generate_32();
rtp_payload_ = fmt;
std::memset(&conf_, 0, sizeof(conf_));
set_payload(fmt);
srtp_key_.first = nullptr;
srtp_key_.second = 0;
}
kvz_rtp::connection::~connection()
@ -259,3 +262,22 @@ rtp_error_t kvz_rtp::connection::configure(int flag)
return RTP_OK;
}
rtp_error_t kvz_rtp::connection::set_srtp_key(uint8_t *key, size_t keylen)
{
if (!key || keylen == 0)
return RTP_INVALID_VALUE;
if (!(conf_.flags & RCE_SRTP_KMNGMNT_USER))
return RTP_NOT_SUPPORTED;
srtp_key_.first = key;
srtp_key_.second = keylen;
return RTP_OK;
}
std::pair<uint8_t *, size_t>& kvz_rtp::connection::get_srtp_key()
{
return srtp_key_;
}

View File

@ -94,18 +94,40 @@ namespace kvz_rtp {
void install_dealloc_hook(void (*dealloc_hook)(void *));
/* Return pointer to RTCP object if RTCP has been enabled
* Otherwise return nullptr
*
* TODO make this const (TODO ???) */
* Otherwise return nullptr */
kvz_rtp::rtcp *get_rtcp();
/* Get the connection-specific context configuration
* Used by both readers and writers */
rtp_ctx_conf_t& get_ctx_conf();
/* TODO: */
/* Enable some feature of kvzRTP that does not require extra configuration.
*
* F.ex: writer->configure(RCE_SYSTEM_CALL_DISPATCHER);
*
* Return RTP_OK on success
* Return RTP_INVALID_VALUE if "flag" is not valid configuration option */
rtp_error_t configure(int flag);
/* Enable some feature of kvzRTP with additional parameter
*
* F.ex: writer->configure(RCC_UDP_BUF_SIZE, 4 * 1024 * 1024);
*
* Return RTP_OK on success
* Return RTP_INVALID_VALUE if "flag" is not valid configuration option */
rtp_error_t configure(int flag, ssize_t value);
/* TODO: */
rtp_error_t configure(int flag);
/* If user does not want to use ZRTP for key management but wishes to do it
* by himself, a master key and its length must be provided
*
* Return RTP_OK on success
* Return RTP_INVALID_VALUE if "key" or keylen are invalid
* Return RTP_NOT_SUPPORTED if RCE_SRTP_KMNGMNT_USER is not set and set_srtp_key() is called */
rtp_error_t set_srtp_key(uint8_t *key, size_t keylen);
/* Called internally by socket.cc to retrieve master key user provided
* when initializing SRTP context */
std::pair<uint8_t *, size_t>& get_srtp_key();
protected:
void *config_;
@ -131,5 +153,8 @@ namespace kvz_rtp {
/* After creation in writer.cc, pointer to frame queue is transferred
* to conn.cc so we can get rid of the dynamic cast in push_fram() */
kvz_rtp::frame_queue *fqueue_;
/* Store key temporarily here before SRTP context is initialized */
std::pair<uint8_t *, size_t> srtp_key_;
};
};

View File

@ -76,6 +76,34 @@ rtp_error_t kvz_rtp::frame::dealloc_frame(kvz_rtp::frame::rtp_frame *frame)
return RTP_OK;
}
kvz_rtp::frame::zrtp_frame *kvz_rtp::frame::alloc_zrtp_frame(size_t size)
{
if (size == 0) {
rtp_errno = RTP_INVALID_VALUE;
return nullptr;
}
LOG_DEBUG("Allocate ZRTP frame, packet size %zu", size);
kvz_rtp::frame::zrtp_frame *frame = (kvz_rtp::frame::zrtp_frame *)new uint8_t[size];
if (frame == nullptr) {
rtp_errno = RTP_MEMORY_ERROR;
return nullptr;
}
return frame;
}
rtp_error_t kvz_rtp::frame::dealloc_frame(kvz_rtp::frame::zrtp_frame *frame)
{
if (!frame)
return RTP_INVALID_VALUE;
delete[] frame;
return RTP_OK;
}
kvz_rtp::frame::rtcp_sender_frame *kvz_rtp::frame::alloc_rtcp_sender_frame(size_t nblocks)
{
size_t total_size =

View File

@ -148,6 +148,15 @@ namespace kvz_rtp {
uint8_t payload[0];
};
PACKED_STRUCT(zrtp_frame) {
uint8_t version:4;
uint16_t unused:12;
uint16_t seq;
uint32_t magic;
uint32_t ssrc;
uint8_t payload[0];
};
/* Allocate an RTP frame
*
* First function allocates an empty RTP frame (no payload)
@ -164,6 +173,15 @@ namespace kvz_rtp {
rtp_frame *alloc_rtp_frame(size_t payload_len);
rtp_frame *alloc_rtp_frame(size_t payload_len, size_t pz_size);
/* Allocate ZRTP frame
* Parameter "payload_size" defines the length of the frame
*
* Return pointer to frame on success
* Return nullptr on error and set rtp_errno to:
* RTP_MEMORY_ERROR if allocation of memory failed
* RTP_INVALID_VALUE if "payload_size" is 0 */
zrtp_frame *alloc_zrtp_frame(size_t payload_size);
/* Allocate various types of RTCP frames, see src/rtcp.cc for more details
*
* Return pointer to frame on success
@ -182,6 +200,12 @@ namespace kvz_rtp {
* Return RTP_INVALID_VALUE if "frame" is nullptr */
rtp_error_t dealloc_frame(kvz_rtp::frame::rtp_frame *frame);
/* Deallocate ZRTP frame
*
* Return RTP_OK on successs
* Return RTP_INVALID_VALUE if "frame" is nullptr */
rtp_error_t dealloc_frame(kvz_rtp::frame::zrtp_frame *frame);
/* Deallocate various types of RTCP frames
*
* Return RTP_OK on successs

View File

@ -35,10 +35,6 @@ kvz_rtp::frame_queue::frame_queue(rtp_format_t fmt, rtp_ctx_conf_t& conf):
if (max_ccount_ <= 0)
max_ccount_ = MAX_CHUNK_COUNT * max_mcount_;
LOG_ERROR("max transactions: %zu", max_queued_);
LOG_ERROR("max messages: %zu", max_mcount_);
LOG_ERROR("max chunk: %zu", max_ccount_);
free_.reserve(max_queued_);
}

View File

@ -47,8 +47,8 @@ rtp_error_t kvz_rtp::reader::start()
if ((ret = socket_.setsockopt(SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(int))) != RTP_OK)
return ret;
auto ctx_conf = get_ctx_conf();
ssize_t buf_size = ctx_conf.ctx_values[RCC_UDP_BUF_SIZE];
auto conf = get_ctx_conf();
ssize_t buf_size = conf.ctx_values[RCC_UDP_BUF_SIZE];
if (buf_size <= 0)
buf_size = 4 * 1000 * 1000;
@ -68,6 +68,20 @@ rtp_error_t kvz_rtp::reader::start()
recv_buffer_len_ = 0;
}
if (conf.flags & RCE_SRTP) {
/* Create socket address entry if SRTP has been enabled for the reader too,
* because we need to exchange keys with remote using ZRTP */
auto addr_out_ = socket_.create_sockaddr(AF_INET, src_addr_, src_port_);
socket_.set_sockaddr(addr_out_);
if ((ret = socket_.setup_srtp(get_ssrc())) != RTP_OK) {
LOG_ERROR("Failed to initialize SRTP, all traffic is unencrypted!");
} else {
LOG_WARN("Reader SRTP initialized!");
}
}
for (;;);
active_ = true;
switch (get_payload()) {

View File

@ -24,7 +24,8 @@ kvz_rtp::socket::socket():
recv_handler_(nullptr),
sendto_handler_(nullptr),
sendtov_handler_(nullptr),
socket_(-1)
socket_(-1),
srtp_(nullptr)
{
}
@ -35,6 +36,68 @@ kvz_rtp::socket::~socket()
#else
closesocket(socket_);
#endif
delete srtp_;
}
rtp_error_t kvz_rtp::socket::setup_srtp(uint32_t ssrc)
{
if (srtp_) {
LOG_DEBUG("SRTP has already been initialized");
return RTP_INITIALIZED;
}
if ((srtp_ = new kvz_rtp::srtp(SRTP)) == nullptr) {
LOG_DEBUG("Failed to allocate SRTP context!");
return RTP_MEMORY_ERROR;
}
return srtp_->init_zrtp(ssrc, socket_, addr_);
}
rtp_error_t kvz_rtp::socket::setup_srtcp(uint32_t ssrc)
{
if (srtp_) {
LOG_DEBUG("SRTP has already been initialized");
return RTP_INITIALIZED;
}
if ((srtp_ = new kvz_rtp::srtp(SRTCP)) == nullptr) {
LOG_DEBUG("Failed to allocate SRTP context!");
return RTP_MEMORY_ERROR;
}
return srtp_->init_zrtp(ssrc, socket_, addr_);
}
rtp_error_t kvz_rtp::socket::setup_srtp(uint32_t ssrc, std::pair<uint8_t *, size_t>& key)
{
if (srtp_) {
LOG_DEBUG("SRTP has already been initialized");
return RTP_INITIALIZED;
}
if ((srtp_ = new kvz_rtp::srtp(SRTP)) == nullptr) {
LOG_DEBUG("Failed to allocate SRTP context!");
return RTP_MEMORY_ERROR;
}
return srtp_->init_user(ssrc, key);
}
rtp_error_t kvz_rtp::socket::setup_srtcp(uint32_t ssrc, std::pair<uint8_t *, size_t>& key)
{
if (srtp_) {
LOG_DEBUG("SRTP has already been initialized");
return RTP_INITIALIZED;
}
if ((srtp_ = new kvz_rtp::srtp(SRTCP)) == nullptr) {
LOG_DEBUG("Failed to allocate SRTP context!");
return RTP_MEMORY_ERROR;
}
return srtp_->init_user(ssrc, key);
}
rtp_error_t kvz_rtp::socket::init(short family, int type, int protocol)

View File

@ -13,6 +13,7 @@
#include <vector>
#include <string>
#include "srtp.hh"
#include "util.hh"
namespace kvz_rtp {
@ -31,6 +32,38 @@ namespace kvz_rtp {
socket();
~socket();
/* Setup Secure RTP/RTCP connection
*
* The process consists of initializing ZRTP context and
* exchaning keys with remote participant and finally
* initializing the SRTP context.
*
* NOTE: This function must be called **after** socket
* initialization but before any media exchange happens!
*
* Return RTP_OK if SRTP setup was successful
* Return RTP_NOT_SUPPORTED if remote does not support {SRTP,SRTCP}/ZRTP
* Return RTP_MEMORY_ERROR if allocation failed
* Return RTP_INITIALIZED if SRTP has already been initialized for this socket
* Return RTP_GENERIC_ERROR for any other error */
rtp_error_t setup_srtp(uint32_t ssrc);
rtp_error_t setup_srtcp(uint32_t ssrc);
/* Setup Secure RTP/RTCP connection
*
* The process consists of initializing SRTP context
* using the master key provided by the user (ie. no ZRTP)
*
* NOTE 2: This function must be called **after** socket
* initialization but before any media exchange happens!
*
* Return RTP_OK if SRTP setup was successful
* Return RTP_MEMORY_ERROR if allocation failed
* Return RTP_INITIALIZED if SRTP has already been initialized for this socket
* Return RTP_GENERIC_ERROR for any other error */
rtp_error_t setup_srtp(uint32_t ssrc, std::pair<uint8_t *, size_t>& key);
rtp_error_t setup_srtcp(uint32_t ssrc, std::pair<uint8_t *, size_t>& key);
/* Create socket using "family", "type" and "protocol"
*
* NOTE: Only family AF_INET (ie. IPv4) is supported
@ -157,6 +190,8 @@ namespace kvz_rtp {
socket_t socket_;
sockaddr_in addr_;
kvz_rtp::srtp *srtp_;
#ifdef _WIN32
WSABUF buffers_[MAX_BUFFER_COUNT];
#else

56
src/srtp.cc Normal file
View File

@ -0,0 +1,56 @@
#include "srtp.hh"
kvz_rtp::srtp::srtp(int type):
zrtp_(nullptr),
s_ctx(nullptr)
{
(void)type;
}
kvz_rtp::srtp::~srtp()
{
delete zrtp_;
}
rtp_error_t kvz_rtp::srtp::init_zrtp(uint32_t ssrc, socket_t& socket, sockaddr_in& addr)
{
LOG_INFO("Begin SRTP initialization procedure...");
(void)socket, (void)ssrc;
if ((zrtp_ = new kvz_rtp::zrtp()) == nullptr) {
LOG_ERROR("Failed to allocate ZRTP context");
return RTP_MEMORY_ERROR;
}
LOG_DEBUG("Begin ZRTP initialization procedure...");
return zrtp_->init(ssrc, socket, addr);
}
rtp_error_t kvz_rtp::srtp::init_user(uint32_t ssrc, std::pair<uint8_t *, size_t>& key)
{
(void)ssrc, (void)key;
return RTP_OK;
}
rtp_error_t kvz_rtp::srtp::encrypt(uint8_t *buf, size_t len)
{
(void)buf, (void)len;
if (!zrtp_)
return RTP_NOT_INITIALIZED;
return RTP_OK;
}
rtp_error_t kvz_rtp::srtp::decrypt(uint8_t *buf, size_t len)
{
(void)buf, (void)len;
if (!zrtp_)
return RTP_NOT_INITIALIZED;
return RTP_OK;
}

75
src/srtp.hh Normal file
View File

@ -0,0 +1,75 @@
#pragma once
#ifdef _WIN32
#include <winsock2.h>
#include <mswsock.h>
#include <inaddr.h>
#else
#include <netinet/ip.h>
#include <arpa/inet.h>
#endif
#include <cstdint>
#include "debug.hh"
#include "util.hh"
#include "zrtp.hh"
namespace kvz_rtp {
enum STYPE {
SRTP = 0,
SRTCP = 1
};
struct srtp_ctx {
};
struct srtcp_ctx {
};
struct secure_context {
int type; /* srtp or srtcp */
};
class srtp {
public:
srtp(int type);
~srtp();
/* Initialize SRTP state using ZRTP
* Socket is needed to exchange keys using ZRTP
*
* After this call, encrypt()/decrypt() functions can be called
*
* Return RTP_OK on success
* Return RTP_NOT_SUPPORTED if remote does not support ZRTP
* Return RTP_MEMORY_ERROR if allocation failed
* Return RTP_INIT_ERROR if ZRTP has already been initialized for this socket
* Return RTP_GENERIC_ERROR for any other error */
rtp_error_t init_zrtp(uint32_t ssrc, socket_t& socket, sockaddr_in& addr);
/* Initialize SRTP state using user-managed key
*
* Parameter "key" is the master key from which all encryption,
* authentication, and salt keys are derived
*
* After this call, encrypt()/decrypt() functions can be called
*
* Return RTP_OK on success
* Return RTP_NOT_SUPPORTED if remote does support SRTP (TODO: is this true?)
* Return RTP_MEMORY_ERROR if allocation failed
* Return RTP_GENERIC_ERROR for any other error */
rtp_error_t init_user(uint32_t ssrc, std::pair<uint8_t *, size_t>& key);
/* TODO: */
rtp_error_t encrypt(uint8_t *buf, size_t len);
/* TODO: */
rtp_error_t decrypt(uint8_t *buf, size_t len);
private:
kvz_rtp::zrtp *zrtp_;
secure_context *s_ctx;
};
};

View File

@ -42,6 +42,10 @@ typedef enum RTP_ERROR {
RTP_SEND_ERROR = -5,
RTP_MEMORY_ERROR = -6,
RTP_SSRC_COLLISION = -7,
RTP_INITIALIZED = -8, /* object already initialized */
RTP_NOT_INITIALIZED = -9, /* object has not been initialized */
RTP_NOT_SUPPORTED = -10, /* method/version/extension not supported */
RTP_RECV_ERROR = -11, /* recv(2) or one of its derivatives failed */
} rtp_error_t;
typedef enum RTP_FORMAT {
@ -85,7 +89,20 @@ enum RTP_CTX_ENABLE_FLAGS {
/* Enable system call dispatcher (HEVC only) */
RCE_SYSTEM_CALL_DISPATCHER = 1 << 2,
RCE_LAST
/* Use SRTP for this connection */
RCE_SRTP = 1 << 3,
/* Use ZRTP for key management
*
* TODO selitä paremmin */
RCE_SRTP_KMNGMNT_ZRTP = 1 << 4,
/* Use user-defined way to manage keys
*
* TODO selitä paremmin */
RCE_SRTP_KMNGMNT_USER = 1 << 4,
RCE_LAST = 1 << 5,
};
/* These options are given to configuration() */

View File

@ -92,6 +92,16 @@ rtp_error_t kvz_rtp::writer::start()
auto conf = get_ctx_conf();
auto fmt = get_payload();
if (conf.flags & RCE_SRTP) {
if ((ret = socket_.setup_srtp(get_ssrc())) != RTP_OK) {
LOG_ERROR("Failed to initialize SRTP, all traffic is unencrypted!");
} else {
LOG_WARN("Writer SRTP initialized!");
}
}
for (;;);
#ifndef _WIN32
if (fmt == RTP_FORMAT_HEVC && conf.flags & RCE_SYSTEM_CALL_DISPATCHER) {
dispatcher_ = new kvz_rtp::dispatcher(&socket_);