Merge branch 'secure-rtp' into develop

This commit is contained in:
Aaro Altonen 2020-07-31 11:29:24 +03:00
commit 7c2142dddf
10 changed files with 221 additions and 87 deletions

View File

@ -14,6 +14,7 @@ namespace uvg_rtp {
uint32_t get_ssrc();
uint16_t get_sequence();
uint32_t get_clock_rate();
size_t get_payload_size();
void inc_sent_pkts();
void inc_sequence();
@ -22,6 +23,7 @@ namespace uvg_rtp {
void set_payload(rtp_format_t fmt);
void set_dynamic_payload(uint8_t payload);
void set_timestamp(uint64_t timestamp);
void set_payload_size(size_t payload_size);
void fill_header(uint8_t *buffer);
void update_sequence(uint8_t *buffer);
@ -41,6 +43,12 @@ namespace uvg_rtp {
/* Use custom timestamp for the outgoing RTP packets */
uint64_t timestamp_;
/* What is the maximum size of the payload available for this RTP instance
*
* By default, the value is set to 1443
* (maximum amount of payload bytes when MTU is 1500) */
size_t payload_size_;
};
};

View File

@ -23,6 +23,7 @@
#define AES_KEY_LENGTH 16 /* 128 bits */
#define HMAC_KEY_LENGTH 32 /* 256 bits */
#define SALT_LENGTH 14 /* 112 bits */
#define AUTH_TAG_LENGTH 8
namespace uvg_rtp {
@ -112,7 +113,7 @@ namespace uvg_rtp {
* Return RTP_OK if SRTP setup was successful
* Return RTP_INVALID_VALUE if "zrtp" is nullptr
* Return RTP_MEMORY allocation failed */
rtp_error_t init_zrtp(int type, uvg_rtp::rtp *rtp, uvg_rtp::zrtp *zrtp);
rtp_error_t init_zrtp(int type, int flags, uvg_rtp::rtp *rtp, uvg_rtp::zrtp *zrtp);
/* Setup Secure RTP/RTCP connection using user-managed keys
*
@ -122,7 +123,7 @@ namespace uvg_rtp {
* Return RTP_OK if SRTP setup was successful
* Return RTP_INVALID_VALUE if "key" or "salt" is nullptr
* Return RTP_MEMORY allocation failed */
rtp_error_t init_user(int type, uint8_t *key, uint8_t *salt);
rtp_error_t init_user(int type, int flags, uint8_t *key, uint8_t *salt);
/* Encrypt the payload of "frame" using the private session key
*
@ -141,10 +142,14 @@ namespace uvg_rtp {
rtp_error_t encrypt(std::vector<std::pair<size_t, uint8_t *>>& buffers);
/* Decrypt the payload payload of "frame" using the private session key
*
* If RTP packet authentication has been enabled during stream creation,
* decrypt() authenticates the received packet.
*
* Return RTP_OK on success
* Return RTP_INVALID_VALUE if "frame" is nullptr
* Return RTP_NOT_INITIALIZED if SRTP has not been initialized */
* Return RTP_NOT_INITIALIZED if SRTP has not been initialized
* Return RTP_AUTH_TAG_MISMATCH if authentication tags do not match */
rtp_error_t decrypt(uint8_t *buffer, size_t len);
/* Authenticate "frame" using the private session key
@ -168,10 +173,20 @@ namespace uvg_rtp {
rtp_error_t __encrypt(uint32_t ssrc, uint16_t seq, uint8_t *buffer, size_t len);
/* Internal init method that initialize the SRTP context using values in key_ctx_.master */
rtp_error_t __init(int type);
rtp_error_t __init(int type, int flags);
#endif
srtp_key_ctx_t key_ctx_;
srtp_ctx_t srtp_ctx_;
/* If NULL cipher is enabled, it means that RTP packets are not
* encrypted but other security mechanisms described in RFC 3711 may be used */
bool use_null_cipher_;
/* By default RTP packet authentication is disabled but by
* giving RCE_SRTP_AUTHENTICATE_RTP to create_stream() user can enable it.
*
* The authentication tag will occupy the last 8 bytes of the RTP packet */
bool authenticate_rtp_;
};
};

View File

@ -36,22 +36,23 @@ const int MAX_PACKET = 65536;
const int MAX_PAYLOAD = 1443;
typedef enum RTP_ERROR {
RTP_INTERRUPTED = 2,
RTP_NOT_READY = 1,
RTP_OK = 0,
RTP_GENERIC_ERROR = -1,
RTP_SOCKET_ERROR = -2,
RTP_BIND_ERROR = -3,
RTP_INVALID_VALUE = -4,
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_TIMEOUT = -12, /* operation timed out */
RTP_NOT_FOUND = -13, /* object not found */
RTP_INTERRUPTED = 2,
RTP_NOT_READY = 1,
RTP_OK = 0,
RTP_GENERIC_ERROR = -1,
RTP_SOCKET_ERROR = -2,
RTP_BIND_ERROR = -3,
RTP_INVALID_VALUE = -4,
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_TIMEOUT = -12, /* operation timed out */
RTP_NOT_FOUND = -13, /* object not found */
RTP_AUTH_TAG_MISMATCH = -14, /* authentication tag does not match the RTP packet contents */
} rtp_error_t;
typedef enum RTP_FORMAT {
@ -100,12 +101,22 @@ enum RTP_CTX_ENABLE_FLAGS {
/* Use ZRTP for key management
*
* TODO selitä paremmin */
* If this flag is provided, before the session starts,
* ZRTP will negotiate keys with the remote participants
* and these keys are used as salting/keying material for the session.
*
* This flag must be coupled with RCE_SRTP and is mutually exclusive
* with RCE_SRTP_KMNGMNT_USER. */
RCE_SRTP_KMNGMNT_ZRTP = 1 << 4,
/* Use user-defined way to manage keys
*
* TODO selitä paremmin */
* If this flag is provided, before the media transportation starts,
* user must provide a master key and salt form which SRTP session
* keys are derived
*
* This flag must be coupled with RCE_SRTP and is mutually exclusive
* with RCE_SRTP_KMNGMNT_ZRTP */
RCE_SRTP_KMNGMNT_USER = 1 << 5,
/* When uvgRTP is receiving HEVC stream, as an attempt to improve
@ -168,7 +179,18 @@ enum RTP_CTX_ENABLE_FLAGS {
* Mutually exclusive with RCE_UNIDIR_SENDER */
RCE_UNIDIR_RECEIVER = 1 << 11,
RCE_LAST = 1 << 12,
/* Disable RTP payload encryption */
RCE_SRTP_NULL_CIPHER = 1 << 12,
/* Enable RTP packet authentication
*
* This flag forces the security layer to add authentication tag
* to each outgoing RTP packet for all streams that have SRTP enabled.
*
* NOTE: this flag must be coupled with at least RCE_SRTP */
RCE_SRTP_AUTHENTICATE_RTP = 1 << 13,
RCE_LAST = 1 << 14,
};
/* These options are given to configuration() */

View File

@ -26,10 +26,11 @@ rtp_error_t uvg_rtp::generic::push_frame(uvg_rtp::sender *sender, uint8_t *data,
{
(void)flags;
size_t payload_size = sender->get_rtp_ctx()->get_payload_size();
uint8_t header[uvg_rtp::frame::HEADER_SIZE_RTP];
sender->get_rtp_ctx()->fill_header(header);
if (data_len > MAX_PAYLOAD) {
if (data_len > payload_size) {
if (sender->get_conf().flags & RCE_FRAGMENT_GENERIC) {
rtp_error_t ret = RTP_OK;
@ -39,16 +40,16 @@ rtp_error_t uvg_rtp::generic::push_frame(uvg_rtp::sender *sender, uint8_t *data,
/* set marker bit for the first fragment */
header[1] |= (1 << 7);
while (data_left > MAX_PAYLOAD) {
ret = uvg_rtp::send::send_frame(sender, header, sizeof(header), data + data_pos, MAX_PAYLOAD);
while (data_left > (ssize_t)payload_size) {
ret = uvg_rtp::send::send_frame(sender, header, sizeof(header), data + data_pos, payload_size);
if (ret != RTP_OK)
return ret;
sender->get_rtp_ctx()->update_sequence(header);
data_pos += MAX_PAYLOAD;
data_left -= MAX_PAYLOAD;
data_pos += payload_size;
data_left -= payload_size;
/* clear marker bit for middle fragments */
header[1] &= 0x7f;
@ -59,7 +60,7 @@ rtp_error_t uvg_rtp::generic::push_frame(uvg_rtp::sender *sender, uint8_t *data,
return uvg_rtp::send::send_frame(sender, header, sizeof(header), data + data_pos, data_left);
} else {
LOG_WARN("packet is larger (%zu bytes) than MAX_PAYLOAD (%u bytes)", data_len, MAX_PAYLOAD);
LOG_WARN("packet is larger (%zu bytes) than payload_size (%zu bytes)", data_len, payload_size);
}
}
@ -79,6 +80,7 @@ static rtp_error_t __fragment_receiver(uvg_rtp::receiver *receiver)
sockaddr_in sender_addr;
rtp_error_t ret = RTP_OK;
uvg_rtp::socket socket = receiver->get_socket();
size_t payload_size = receiver->get_rtp_ctx()->get_payload_size();
uvg_rtp::frame::rtp_frame *frame;
struct frame_info {
@ -162,7 +164,7 @@ static rtp_error_t __fragment_receiver(uvg_rtp::receiver *receiver)
recv = frames[ts].e_seq - frames[ts].s_seq + 1;
if (recv == frames[ts].npkts) {
auto retframe = uvg_rtp::frame::alloc_rtp_frame(recv * MAX_PAYLOAD);
auto retframe = uvg_rtp::frame::alloc_rtp_frame(recv * payload_size);
size_t ptr = 0;
std::memcpy(&retframe->header, &frame->header, sizeof(frame->header));

View File

@ -241,13 +241,14 @@ static rtp_error_t __push_hevc_nal(
if (data_len <= 3)
return RTP_INVALID_VALUE;
uint8_t nalType = (data[0] >> 1) & 0x3F;
rtp_error_t ret = RTP_OK;
size_t data_left = data_len;
size_t data_pos = 0;
uint8_t nalType = (data[0] >> 1) & 0x3F;
rtp_error_t ret = RTP_OK;
size_t data_left = data_len;
size_t data_pos = 0;
size_t payload_size = sender->get_rtp_ctx()->get_payload_size();
#ifdef __linux__
if (data_len - 3 <= MAX_PAYLOAD) {
if (data_len - 3 <= payload_size) {
if ((ret = fqueue->enqueue_message(sender, data, data_len)) != RTP_OK) {
LOG_ERROR("enqeueu failed for small packet");
return ret;
@ -278,13 +279,13 @@ static rtp_error_t __push_hevc_nal(
buffers.push_back(std::make_pair(sizeof(headers->nal_header), headers->nal_header));
buffers.push_back(std::make_pair(sizeof(uint8_t), &headers->fu_headers[0]));
buffers.push_back(std::make_pair(MAX_PAYLOAD, nullptr));
buffers.push_back(std::make_pair(payload_size, nullptr));
data_pos = uvg_rtp::frame::HEADER_SIZE_HEVC_NAL;
data_left -= uvg_rtp::frame::HEADER_SIZE_HEVC_NAL;
while (data_left > MAX_PAYLOAD) {
buffers.at(2).first = MAX_PAYLOAD;
while (data_left > payload_size) {
buffers.at(2).first = payload_size;
buffers.at(2).second = &data[data_pos];
if ((ret = fqueue->enqueue_message(sender, buffers)) != RTP_OK) {
@ -293,8 +294,8 @@ static rtp_error_t __push_hevc_nal(
return ret;
}
data_pos += MAX_PAYLOAD;
data_left -= MAX_PAYLOAD;
data_pos += payload_size;
data_left -= payload_size;
/* from now on, use the FU header meant for middle fragments */
buffers.at(1).second = &headers->fu_headers[1];
@ -316,7 +317,7 @@ static rtp_error_t __push_hevc_nal(
return RTP_NOT_READY;
return fqueue->flush_queue(sender);
#else
if (data_len - 3 <= MAX_PAYLOAD) {
if (data_len - 3 <= payload_size) {
LOG_DEBUG("send unfrag size %zu, type %u", data_len, nalType);
if ((ret = uvg_rtp::generic::push_frame(sender, data, data_len, 0)) != RTP_OK) {
@ -334,7 +335,7 @@ static rtp_error_t __push_hevc_nal(
uvg_rtp::frame::HEADER_SIZE_HEVC_NAL +
uvg_rtp::frame::HEADER_SIZE_HEVC_FU;
uint8_t buffer[HEADER_SIZE + MAX_PAYLOAD] = { 0 };
uint8_t buffer[HEADER_SIZE + payload_size] = { 0 };
sender->get_rtp_ctx()->fill_header(buffer);
@ -346,16 +347,16 @@ static rtp_error_t __push_hevc_nal(
data_pos = uvg_rtp::frame::HEADER_SIZE_HEVC_NAL;
data_left -= uvg_rtp::frame::HEADER_SIZE_HEVC_NAL;
while (data_left > MAX_PAYLOAD) {
memcpy(&buffer[HEADER_SIZE], &data[data_pos], MAX_PAYLOAD);
while (data_left > payload_size) {
memcpy(&buffer[HEADER_SIZE], &data[data_pos], payload_size);
if ((ret = uvg_rtp::send::send_frame(sender, buffer, sizeof(buffer))) != RTP_OK)
return RTP_GENERIC_ERROR;
sender->get_rtp_ctx()->update_sequence(buffer);
data_pos += MAX_PAYLOAD;
data_left -= MAX_PAYLOAD;
data_pos += payload_size;
data_left -= payload_size;
/* Clear extra bits */
buffer[uvg_rtp::frame::HEADER_SIZE_RTP +
@ -392,7 +393,7 @@ static rtp_error_t __push_hevc_slice(
return RTP_INVALID_VALUE;
}
if (data_len >= MAX_PAYLOAD) {
if (data_len >= sender->get_rtp_ctx()->get_payload_size()) {
LOG_ERROR("slice is too big!");
(void)fqueue->deinit_transaction();
return RTP_INVALID_VALUE;
@ -422,13 +423,14 @@ static rtp_error_t __push_hevc_frame(
#ifdef __linux__
/* find first start code */
uint8_t start_len = 0;
int offset = __get_hevc_start(data, data_len, 0, start_len);
int prev_offset = offset;
size_t r_off = 0;
rtp_error_t ret = RTP_GENERIC_ERROR;
uint8_t start_len = 0;
int offset = __get_hevc_start(data, data_len, 0, start_len);
int prev_offset = offset;
size_t r_off = 0;
rtp_error_t ret = RTP_GENERIC_ERROR;
size_t payload_size = sender->get_rtp_ctx()->get_payload_size();
if (data_len < MAX_PAYLOAD) {
if (data_len < payload_size) {
r_off = (offset < 0) ? 0 : offset; /* TODO: this looks ugly */
fqueue->deinit_transaction();
return uvg_rtp::generic::push_frame(sender, data + r_off, data_len - r_off, flags);

View File

@ -10,8 +10,8 @@
#define RECV_ONLY_FLAGS (RCE_UNIDIRECTIONAL | RCE_UNIDIR_RECEIVER)
#define SEND_ONLY_FLAGS (RCE_UNIDIRECTIONAL | RCE_UNIDIR_SENDER)
#define RECV_ONLY(flags) ((flags & RECV_ONLY_FLAGS) == RECV_ONLY_FLAGS)
#define SEND_ONLY(flags) ((flags & SEND_ONLY_FLAGS) == SEND_ONLY_FLAGS)
#define RECV_ONLY(flags) ((flags & RECV_ONLY_FLAGS) == RECV_ONLY_FLAGS)
#define SEND_ONLY(flags) ((flags & SEND_ONLY_FLAGS) == SEND_ONLY_FLAGS)
uvg_rtp::media_stream::media_stream(std::string addr, int src_port, int dst_port, rtp_format_t fmt, int flags):
srtp_(nullptr),
@ -173,11 +173,14 @@ rtp_error_t uvg_rtp::media_stream::init(uvg_rtp::zrtp *zrtp)
if ((srtp_ = new uvg_rtp::srtp()) == nullptr)
return RTP_MEMORY_ERROR;
if ((ret = srtp_->init_zrtp(SRTP, rtp_, zrtp)) != RTP_OK) {
if ((ret = srtp_->init_zrtp(SRTP, ctx_config_.flags, rtp_, zrtp)) != RTP_OK) {
LOG_WARN("Failed to initialize SRTP for media stream!");
return ret;
}
if (ctx_config_.flags & RCE_SRTP_AUTHENTICATE_RTP)
rtp_->set_payload_size(MAX_PAYLOAD - AUTH_TAG_LENGTH);
socket_.set_srtp(srtp_);
sender_ = new uvg_rtp::sender(socket_, ctx_config_, fmt_, rtp_);
@ -213,11 +216,14 @@ rtp_error_t uvg_rtp::media_stream::add_srtp_ctx(uint8_t *key, uint8_t *salt)
if ((srtp_ = new uvg_rtp::srtp()) == nullptr)
return RTP_MEMORY_ERROR;
if ((ret = srtp_->init_user(SRTP, key, salt)) != RTP_OK) {
if ((ret = srtp_->init_user(SRTP, ctx_config_.flags, key, salt)) != RTP_OK) {
LOG_WARN("Failed to initialize SRTP for media stream!");
return ret;
}
if (ctx_config_.flags & RCE_SRTP_AUTHENTICATE_RTP)
rtp_->set_payload_size(MAX_PAYLOAD - AUTH_TAG_LENGTH);
socket_.set_srtp(srtp_);
sender_ = new uvg_rtp::sender(socket_, ctx_config_, fmt_, rtp_);

View File

@ -15,7 +15,8 @@
uvg_rtp::rtp::rtp(rtp_format_t fmt):
wc_start_(0),
sent_pkts_(0),
timestamp_(INVALID_TS)
timestamp_(INVALID_TS),
payload_size_(MAX_PAYLOAD)
{
seq_ = uvg_rtp::random::generate_32() & 0xffff;
ts_ = uvg_rtp::random::generate_32();
@ -121,3 +122,13 @@ uint32_t uvg_rtp::rtp::get_clock_rate(void)
{
return clock_rate_;
}
void uvg_rtp::rtp::set_payload_size(size_t payload_size)
{
payload_size_ = payload_size;
}
size_t uvg_rtp::rtp::get_payload_size()
{
return payload_size_;
}

View File

@ -32,6 +32,12 @@ uvg_rtp::media_stream *uvg_rtp::session::create_stream(int r_port, int s_port, r
{
uvg_rtp::media_stream *stream = nullptr;
if (flags & RCE_SYSTEM_CALL_DISPATCHER) {
LOG_ERROR("SCD is not supported!");
rtp_errno = RTP_NOT_SUPPORTED;
return nullptr;
}
if (laddr_ == "")
stream = new uvg_rtp::media_stream(addr_, r_port, s_port, fmt, flags);
else

View File

@ -245,6 +245,11 @@ rtp_error_t uvg_rtp::socket::__sendtov(
return RTP_SEND_ERROR;
}
#ifdef __RTP_CRYPTO__
if (srtp_ && flags_ & RCE_SRTP_AUTHENTICATE_RTP)
delete buffers.at(buffers.size() - 1).second;
#endif
set_bytes(bytes_sent, sent_bytes);
return RTP_OK;

View File

@ -9,7 +9,9 @@
#endif
uvg_rtp::srtp::srtp():
srtp_ctx_()
srtp_ctx_(),
use_null_cipher_(false),
authenticate_rtp_(false)
{
}
@ -18,18 +20,15 @@ uvg_rtp::srtp::~srtp()
}
#ifdef __RTP_CRYPTO__
/* TODO: explain this code and refactor it! */
rtp_error_t uvg_rtp::srtp::derive_key(int label, uint8_t *key, uint8_t *salt, uint8_t *out, size_t out_len)
{
uint8_t input[AES_KEY_LENGTH] = { 0 };
memcpy(input, salt, SALT_LENGTH);
input[7] ^= label;
memset(out, 0, out_len);
uvg_rtp::crypto::aes::ecb ecb(key, AES_KEY_LENGTH);
ecb.encrypt(out, input, AES_KEY_LENGTH);
return RTP_OK;
@ -40,18 +39,15 @@ rtp_error_t uvg_rtp::srtp::create_iv(uint8_t *out, uint32_t ssrc, uint64_t index
if (!out || !salt)
return RTP_INVALID_VALUE;
uint8_t indexbuf[8];
uint8_t buf[8];
int i;
memset(out, 0, 16);
memcpy(&out[4], &ssrc, sizeof(uint32_t));
memcpy(indexbuf, &index, sizeof(uint64_t));
/* TODO: rewrite this */
memset(out, 0, AES_KEY_LENGTH);
memcpy(&out[4], &ssrc, sizeof(uint32_t));
memcpy(buf, &index, sizeof(uint64_t));
for (i = 0; i < 8; i++)
out[6 + i] ^= indexbuf[i];
out[6 + i] ^= buf[i];
for (i = 0; i < 14; i++)
out[i] ^= salt[i];
@ -59,7 +55,7 @@ rtp_error_t uvg_rtp::srtp::create_iv(uint8_t *out, uint32_t ssrc, uint64_t index
return RTP_OK;
}
rtp_error_t uvg_rtp::srtp::__init(int type)
rtp_error_t uvg_rtp::srtp::__init(int type, int flags)
{
srtp_ctx_.roc = 0;
srtp_ctx_.type = type;
@ -80,6 +76,9 @@ rtp_error_t uvg_rtp::srtp::__init(int type)
srtp_ctx_.s_l = 0;
srtp_ctx_.replay = nullptr;
use_null_cipher_ = !!(flags & RCE_SRTP_NULL_CIPHER);
authenticate_rtp_ = !!(flags & RCE_SRTP_AUTHENTICATE_RTP);
/* Local aka encryption keys */
(void)derive_key(
SRTP_ENCRYPTION,
@ -129,7 +128,7 @@ rtp_error_t uvg_rtp::srtp::__init(int type)
return RTP_OK;
}
rtp_error_t uvg_rtp::srtp::init_zrtp(int type, uvg_rtp::rtp *rtp, uvg_rtp::zrtp *zrtp)
rtp_error_t uvg_rtp::srtp::init_zrtp(int type, int flags, uvg_rtp::rtp *rtp, uvg_rtp::zrtp *zrtp)
{
(void)rtp;
@ -137,7 +136,7 @@ rtp_error_t uvg_rtp::srtp::init_zrtp(int type, uvg_rtp::rtp *rtp, uvg_rtp::zrtp
return RTP_INVALID_VALUE;
if (type != SRTP) {
LOG_ERROR("SRTCP NOT SUPPORTED!");
LOG_ERROR("SRTCP not supported!");
return RTP_INVALID_VALUE;
}
@ -154,10 +153,10 @@ rtp_error_t uvg_rtp::srtp::init_zrtp(int type, uvg_rtp::rtp *rtp, uvg_rtp::zrtp
return ret;
}
return __init(type);
return __init(type, flags);
}
rtp_error_t uvg_rtp::srtp::init_user(int type, uint8_t *key, uint8_t *salt)
rtp_error_t uvg_rtp::srtp::init_user(int type, int flags, uint8_t *key, uint8_t *salt)
{
if (!key || !salt)
return RTP_INVALID_VALUE;
@ -167,11 +166,14 @@ rtp_error_t uvg_rtp::srtp::init_user(int type, uint8_t *key, uint8_t *salt)
memcpy(key_ctx_.master.local_salt, salt, SALT_LENGTH);
memcpy(key_ctx_.master.remote_salt, salt, SALT_LENGTH);
return __init(type);
return __init(type, flags);
}
rtp_error_t uvg_rtp::srtp::__encrypt(uint32_t ssrc, uint16_t seq, uint8_t *buffer, size_t len)
{
if (use_null_cipher_)
return RTP_OK;
uint8_t iv[16] = { 0 };
uint64_t index = (((uint64_t)srtp_ctx_.roc) << 16) + seq;
@ -192,24 +194,57 @@ rtp_error_t uvg_rtp::srtp::__encrypt(uint32_t ssrc, uint16_t seq, uint8_t *buffe
rtp_error_t uvg_rtp::srtp::encrypt(uvg_rtp::frame::rtp_frame *frame)
{
return __encrypt(ntohl(frame->header.ssrc), ntohs(frame->header.seq), frame->payload, frame->payload_len);
/* TODO: authentication for complete RTP frame requires co-operation
* with the allocator of that frame -> no good solution for that right now */
return __encrypt(
ntohl(frame->header.ssrc),
ntohs(frame->header.seq),
frame->payload,
frame->payload_len
);
}
rtp_error_t uvg_rtp::srtp::encrypt(std::vector<std::pair<size_t, uint8_t *>>& buffers)
{
auto frame = (uvg_rtp::frame::rtp_frame *)buffers.at(0).second;
auto rtp = buffers.at(buffers.size() - 1);
auto frame = (uvg_rtp::frame::rtp_frame *)buffers.at(0).second;
auto rtp = buffers.at(buffers.size() - 1);
uint64_t *digest = new uint64_t;
return __encrypt(ntohl(frame->header.ssrc), ntohs(frame->header.seq), rtp.second, rtp.first);
rtp_error_t ret = __encrypt(
ntohl(frame->header.ssrc),
ntohs(frame->header.seq),
rtp.second,
rtp.first
);
if (!authenticate_rtp_)
return ret;
/* create authentication tag for the packet and push it to the vector buffer */
auto hmac_sha1 = uvg_rtp::crypto::hmac::sha1(key_ctx_.local.auth_key, AES_KEY_LENGTH);
for (auto& buffer : buffers)
hmac_sha1.update((uint8_t *)buffer.second, buffer.first);
hmac_sha1.update((uint8_t *)&srtp_ctx_.roc, sizeof(srtp_ctx_.roc));
hmac_sha1.final((uint8_t *)digest);
buffers.push_back(std::make_pair(sizeof(uint64_t), (uint8_t *)digest));
return ret;
}
rtp_error_t uvg_rtp::srtp::decrypt(uint8_t *buffer, size_t len)
{
uint8_t iv[16] = { 0 };
auto hdr = (uvg_rtp::frame::rtp_header *)buffer;
uint16_t seq = ntohs(hdr->seq);
uint32_t ssrc = ntohl(hdr->ssrc);
uint64_t index = (((uint64_t)srtp_ctx_.roc) << 16) + seq;
if (use_null_cipher_)
return RTP_OK;
uint8_t iv[16] = { 0 };
auto hdr = (uvg_rtp::frame::rtp_header *)buffer;
uint16_t seq = ntohs(hdr->seq);
uint32_t ssrc = ntohl(hdr->ssrc);
uint64_t index = (((uint64_t)srtp_ctx_.roc) << 16) + seq;
uint64_t digest = 0;
/* Sequence number has wrapped around, update Roll-over Counter */
if (seq == 0xffff)
@ -221,11 +256,33 @@ rtp_error_t uvg_rtp::srtp::decrypt(uint8_t *buffer, size_t len)
}
uint8_t *payload = buffer + sizeof(uvg_rtp::frame::rtp_header);
uvg_rtp::crypto::aes::ctr ctr(key_ctx_.remote.enc_key, sizeof(key_ctx_.remote.enc_key), iv);
ctr.decrypt(payload, payload, len - sizeof(uvg_rtp::frame::rtp_header));
/* exit early if RTP packet authentication is disabled... */
if (!authenticate_rtp_) {
ctr.decrypt(payload, payload, len - sizeof(uvg_rtp::frame::rtp_header));
return RTP_OK;
}
/* ... otherwise calculate authentication tag for the packet
* and compare it against the one we received */
auto hmac_sha1 = uvg_rtp::crypto::hmac::sha1(key_ctx_.remote.auth_key, AES_KEY_LENGTH);
hmac_sha1.update((uint8_t *)buffer, len - AUTH_TAG_LENGTH);
hmac_sha1.update((uint8_t *)&srtp_ctx_.roc, sizeof(srtp_ctx_.roc));
hmac_sha1.final((uint8_t *)&digest);
if (memcmp(&digest, &buffer[len - AUTH_TAG_LENGTH], AUTH_TAG_LENGTH)) {
LOG_ERROR("Authentication tag mismatch!");
return RTP_AUTH_TAG_MISMATCH;
}
size_t size_ = len - sizeof(uvg_rtp::frame::rtp_header) - 8;
uint8_t *new_buffer = new uint8_t[size_];
ctr.decrypt(new_buffer, payload, size_);
memset(payload, 0, len);
memcpy(payload, new_buffer, size_);
return RTP_OK;
}
#endif