Separate Secure RTP code into SRTP and SRTCP

Create separate classes for SRTP and SRTCP because even though
they are quite similar, they require some different actions when
en/decrypting the packets and create a whole bunch of if-elses
is ugly
This commit is contained in:
Aaro Altonen 2020-09-02 08:29:04 +03:00
parent 1aa6a17348
commit bb826570ef
16 changed files with 502 additions and 349 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@ src/*.o
src/formats/*.o
src/mzrtp/*.o
src/rtcp/*.o
src/srtp/*.o
src/crypto/*.o
libkvzrtp.a
*.a

View File

@ -3,7 +3,7 @@
CXX = g++
CXXFLAGS = -g -Wno-unused-function -Wall -Wextra -Wuninitialized -O2 -std=c++11 -Iinclude -fPIC -DNDEBUG
SOURCES = $(wildcard src/*.cc)
MODULES := src/formats src/mzrtp src/rtcp
MODULES := src/formats src/mzrtp src/rtcp src/srtp
-include $(patsubst %, %/module.mk, $(MODULES))
OBJECTS := $(patsubst %.cc, %.o, $(filter %.cc, $(SOURCES)))
@ -22,4 +22,4 @@ $(TARGET): $(OBJECTS)
$(AR) rcs $@ $(OBJECTS)
clean:
rm -f src/*.o src/formats/*.o src/mzrtp/*.o $(TARGET)
rm -f src/*.o src/formats/*.o src/mzrtp/*.o src/srtp/*.o $(TARGET)

View File

@ -15,6 +15,9 @@
#define INVALID_FRAME_TYPE(ft) (ft < RTP_FT_GENERIC|| ft > RTP_FT_HEVC_FU)
#define RTP_HEADER_LENGTH 12
#define RTCP_HEADER_LENGTH 12
namespace uvg_rtp {
namespace frame {
enum HEADER_SIZES {

View File

@ -6,7 +6,8 @@
#include "pkt_dispatch.hh"
#include "rtcp.hh"
#include "socket.hh"
#include "srtp.hh"
#include "srtp/srtcp.hh"
#include "srtp/srtp.hh"
#include "util.hh"
#include "formats/media.hh"
@ -175,6 +176,7 @@ namespace uvg_rtp {
uint32_t key_;
uvg_rtp::srtp *srtp_;
uvg_rtp::srtcp *srtcp_;
uvg_rtp::socket *socket_;
uvg_rtp::rtp *rtp_;
uvg_rtp::rtcp *rtcp_;

View File

@ -9,6 +9,7 @@
#include "frame.hh"
#include "runner.hh"
#include "socket.hh"
#include "srtp/srtcp.hh"
#include "util.hh"
namespace uvg_rtp {
@ -72,8 +73,8 @@ namespace uvg_rtp {
class rtcp : public runner {
public:
rtcp(uint32_t ssrc, bool receiver);
rtcp(uvg_rtp::rtp *rtp, int flags);
rtcp(uvg_rtp::rtp *rtp, uvg_rtp::srtcp *srtcp, int flags);
~rtcp();
/* start the RTCP runner thread
@ -253,6 +254,9 @@ namespace uvg_rtp {
* used to change SSRC if a collision is detected */
uvg_rtp::rtp *rtp_;
/* Secure RTCP context */
uvg_rtp::srtcp *srtcp_;
/* RTP context flags */
int flags_;

View File

@ -13,7 +13,6 @@
#include <vector>
#include <string>
#include "srtp.hh"
#include "util.hh"
namespace uvg_rtp {

View File

@ -12,19 +12,20 @@
#include <cstdint>
#include <vector>
#include "debug.hh"
#include "frame.hh"
#include "rtp.hh"
#include "util.hh"
#include "../debug.hh"
#include "../frame.hh"
#include "../rtp.hh"
#include "../util.hh"
#ifdef __RTP_CRYPTO__
#include "zrtp.hh"
#include "../zrtp.hh"
#endif
#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
#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
#define SRTCP_INDEX_LENGTH 8
namespace uvg_rtp {
@ -45,9 +46,12 @@ namespace uvg_rtp {
};
enum LABELS {
SRTP_ENCRYPTION = 0x0,
SRTP_AUTHENTICATION = 0x1,
SRTP_SALTING = 0x2
SRTP_ENCRYPTION = 0x0,
SRTP_AUTHENTICATION = 0x1,
SRTP_SALTING = 0x2,
SRTCP_ENCRYPTION = 0x3,
SRTCP_AUTHENTICATION = 0x4,
SRTCP_SALTING = 0x5
};
/* Key context for SRTP keys,
@ -108,10 +112,10 @@ namespace uvg_rtp {
srtp_key_ctx_t key_ctx;
} srtp_ctx_t;
class srtp {
class base_srtp {
public:
srtp();
~srtp();
base_srtp();
virtual ~base_srtp();
#ifdef __RTP_CRYPTO__
/* Setup Secure RTP/RTCP connection using ZRTP
@ -131,26 +135,6 @@ namespace uvg_rtp {
* Return RTP_MEMORY allocation failed */
rtp_error_t init_user(int type, int flags, uint8_t *key, uint8_t *salt);
/* TODO: */
rtp_error_t encrypt(uint32_t ssrc, uint16_t seq, uint8_t *buffer, size_t len);
/* 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_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
*
* Return RTP_OK on success
* Return RTP_INVALID_VALUE if "frame" is nullptr or if authentication failed */
rtp_error_t authenticate(uvg_rtp::frame::rtp_frame *frame);
/* Has RTP packet encryption been disabled? */
bool use_null_cipher();
@ -167,7 +151,7 @@ namespace uvg_rtp {
static rtp_error_t send_packet_handler(void *arg, buf_vec& buffers);
#endif
private:
protected:
#ifdef __RTP_CRYPTO__
rtp_error_t derive_key(int label, uint8_t *key, uint8_t *salt, uint8_t *out, size_t len);
@ -177,11 +161,8 @@ namespace uvg_rtp {
* Return RTP_INVALID_VALUE if one of the parameters is invalid */
rtp_error_t create_iv(uint8_t *out, uint32_t ssrc, uint64_t index, uint8_t *salt);
/* Internal encrypt method that takes only the necessary variables and encrypts "buffer" */
/* 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, int flags);
rtp_error_t init(int type, int flags);
#endif
srtp_ctx_t srtp_ctx_;

26
include/srtp/srtcp.hh Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "base.hh"
namespace uvg_rtp {
class srtcp : public base_srtp {
public:
srtcp();
~srtcp();
/* Encrypt the RTCP packet and calculate authentication tag for it
*
* Report RTP_OK on success
* Return RTP_INVALID_VALUE if IV creation fails */
rtp_error_t encrypt(uint32_t ssrc, uint16_t seq, uint8_t *buffer, size_t len);
/* Decrypt and verify the authenticity of the RTCP packet
*
* Report RTP_OK on success
* Return RTP_INVALID_VALUE if IV creation fails
* Return RTP_AUTH_TAG_MISMATCH if authentication tag is incorrect
* Return RTP_MEMORY_ERROR if memory allocation fails */
rtp_error_t decrypt(uint32_t ssrc, uint32_t seq, uint8_t *buffer, size_t len);
};
};

24
include/srtp/srtp.hh Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "base.hh"
namespace uvg_rtp {
class srtp : public base_srtp {
public:
srtp();
~srtp();
/* TODO: */
rtp_error_t encrypt(uint32_t ssrc, uint16_t seq, uint8_t *buffer, size_t len);
/* TODO: */
rtp_error_t decrypt(uint8_t *buffer, size_t len);
/* Decrypt the payload of an RTP packet and verify authentication tag (if enabled) */
static rtp_error_t recv_packet_handler(void *arg, int flags, frame::rtp_frame **out);
/* Encrypt the payload of an RTP packet and add authentication tag (if enabled) */
static rtp_error_t send_packet_handler(void *arg, buf_vec& buffers);
};
};

View File

@ -5,6 +5,7 @@
#include <map>
#include <unordered_map>
#include "debug.hh"
#include "formats/media.hh"
#define INVALID_SEQ 0xffffffff

View File

@ -17,6 +17,7 @@
uvg_rtp::media_stream::media_stream(std::string addr, int src_port, int dst_port, rtp_format_t fmt, int flags):
srtp_(nullptr),
srtcp_(nullptr),
socket_(nullptr),
rtp_(nullptr),
rtcp_(nullptr),
@ -60,6 +61,7 @@ uvg_rtp::media_stream::~media_stream()
delete rtcp_;
delete rtp_;
delete srtp_;
delete srtcp_;
delete pkt_dispatcher_;
delete dispatcher_thread_;
delete media_;
@ -186,8 +188,6 @@ rtp_error_t uvg_rtp::media_stream::init(uvg_rtp::zrtp *zrtp)
return ret;
}
/* TODO: install zrtp packet handler */
if ((srtp_ = new uvg_rtp::srtp()) == nullptr)
return RTP_MEMORY_ERROR;
@ -196,7 +196,12 @@ rtp_error_t uvg_rtp::media_stream::init(uvg_rtp::zrtp *zrtp)
return ret;
}
if (!(rtcp_ = new uvg_rtp::rtcp(rtp_))) {
if ((ret = srtcp_->init_zrtp(SRTCP, ctx_config_.flags, rtp_, zrtp)) != RTP_OK) {
LOG_ERROR("Failed to initialize SRTP for media stream!");
return ret;
}
if (!(rtcp_ = new uvg_rtp::rtcp(rtp_, srtcp_, ctx_config_.flags))) {
delete rtp_;
delete pkt_dispatcher_;
return RTP_MEMORY_ERROR;
@ -275,7 +280,7 @@ rtp_error_t uvg_rtp::media_stream::add_srtp_ctx(uint8_t *key, uint8_t *salt)
return ret;
}
if (!(rtcp_ = new uvg_rtp::rtcp(rtp_))) {
if (!(rtcp_ = new uvg_rtp::rtcp(rtp_, srtcp_, ctx_config_.flags))) {
delete rtp_;
delete pkt_dispatcher_;
return RTP_MEMORY_ERROR;

View File

@ -1,300 +0,0 @@
#include <cstring>
#include <iostream>
#include "srtp.hh"
#ifdef __RTP_CRYPTO__
#include "crypto.hh"
#include <cryptopp/hex.h>
#endif
uvg_rtp::srtp::srtp():
srtp_ctx_(),
use_null_cipher_(false),
authenticate_rtp_(false)
{
}
uvg_rtp::srtp::~srtp()
{
}
#ifdef __RTP_CRYPTO__
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;
}
rtp_error_t uvg_rtp::srtp::create_iv(uint8_t *out, uint32_t ssrc, uint64_t index, uint8_t *salt)
{
if (!out || !salt)
return RTP_INVALID_VALUE;
uint8_t buf[8];
int i;
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] ^= buf[i];
for (i = 0; i < 14; i++)
out[i] ^= salt[i];
return RTP_OK;
}
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;
/* Sequence number has wrapped around, update Roll-over Counter */
if (seq == 0xffff)
srtp_ctx_.roc++;
if (create_iv(iv, ssrc, index, srtp_ctx_.key_ctx.local.salt_key) != RTP_OK) {
LOG_ERROR("Failed to create IV, unable to encrypt the RTP packet!");
return RTP_INVALID_VALUE;
}
uvg_rtp::crypto::aes::ctr ctr(srtp_ctx_.key_ctx.local.enc_key, sizeof(srtp_ctx_.key_ctx.local.enc_key), iv);
ctr.encrypt(buffer, buffer, len);
return RTP_OK;
}
bool uvg_rtp::srtp::use_null_cipher()
{
return use_null_cipher_;
}
bool uvg_rtp::srtp::authenticate_rtp()
{
return authenticate_rtp_;
}
uvg_rtp::srtp_ctx_t& uvg_rtp::srtp::get_ctx()
{
return srtp_ctx_;
}
rtp_error_t uvg_rtp::srtp::__init(int type, int flags)
{
srtp_ctx_.roc = 0;
srtp_ctx_.type = type;
srtp_ctx_.enc = AES_128;
srtp_ctx_.hmac = HMAC_SHA1;
srtp_ctx_.mki_size = 0;
srtp_ctx_.mki_present = false;
srtp_ctx_.mki = nullptr;
srtp_ctx_.master_key = srtp_ctx_.key_ctx.master.local_key;
srtp_ctx_.master_salt = srtp_ctx_.key_ctx.master.local_salt;
srtp_ctx_.mk_cnt = 0;
srtp_ctx_.n_e = AES_KEY_LENGTH;
srtp_ctx_.n_a = HMAC_KEY_LENGTH;
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,
srtp_ctx_.key_ctx.master.local_key,
srtp_ctx_.key_ctx.master.local_salt,
srtp_ctx_.key_ctx.local.enc_key,
AES_KEY_LENGTH
);
(void)derive_key(
SRTP_AUTHENTICATION,
srtp_ctx_.key_ctx.master.local_key,
srtp_ctx_.key_ctx.master.local_salt,
srtp_ctx_.key_ctx.local.auth_key,
AES_KEY_LENGTH
);
(void)derive_key(
SRTP_SALTING,
srtp_ctx_.key_ctx.master.local_key,
srtp_ctx_.key_ctx.master.local_salt,
srtp_ctx_.key_ctx.local.salt_key,
SALT_LENGTH
);
/* Remote aka decryption keys */
(void)derive_key(
SRTP_ENCRYPTION,
srtp_ctx_.key_ctx.master.remote_key,
srtp_ctx_.key_ctx.master.remote_salt,
srtp_ctx_.key_ctx.remote.enc_key,
AES_KEY_LENGTH
);
(void)derive_key(
SRTP_AUTHENTICATION,
srtp_ctx_.key_ctx.master.remote_key,
srtp_ctx_.key_ctx.master.remote_salt,
srtp_ctx_.key_ctx.remote.auth_key,
AES_KEY_LENGTH
);
(void)derive_key(
SRTP_SALTING,
srtp_ctx_.key_ctx.master.remote_key,
srtp_ctx_.key_ctx.master.remote_salt,
srtp_ctx_.key_ctx.remote.salt_key,
SALT_LENGTH
);
return RTP_OK;
}
rtp_error_t uvg_rtp::srtp::init_zrtp(int type, int flags, uvg_rtp::rtp *rtp, uvg_rtp::zrtp *zrtp)
{
(void)rtp;
if (!zrtp)
return RTP_INVALID_VALUE;
if (type != SRTP) {
LOG_ERROR("SRTCP not supported!");
return RTP_INVALID_VALUE;
}
/* ZRTP key derivation function expects the keys lengths to be given in bits */
rtp_error_t ret = zrtp->get_srtp_keys(
srtp_ctx_.key_ctx.master.local_key, AES_KEY_LENGTH * 8,
srtp_ctx_.key_ctx.master.remote_key, AES_KEY_LENGTH * 8,
srtp_ctx_.key_ctx.master.local_salt, SALT_LENGTH * 8,
srtp_ctx_.key_ctx.master.remote_salt, SALT_LENGTH * 8
);
if (ret != RTP_OK) {
LOG_ERROR("Failed to derive keys for SRTP session!");
return ret;
}
return __init(type, flags);
}
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;
memcpy(srtp_ctx_.key_ctx.master.local_key, key, AES_KEY_LENGTH);
memcpy(srtp_ctx_.key_ctx.master.remote_key, key, AES_KEY_LENGTH);
memcpy(srtp_ctx_.key_ctx.master.local_salt, salt, SALT_LENGTH);
memcpy(srtp_ctx_.key_ctx.master.remote_salt, salt, SALT_LENGTH);
return __init(type, flags);
}
rtp_error_t uvg_rtp::srtp::recv_packet_handler(void *arg, int flags, frame::rtp_frame **out)
{
(void)flags;
uvg_rtp::srtp *srtp = (uvg_rtp::srtp *)arg;
uvg_rtp::srtp_ctx_t ctx = srtp->get_ctx();
uvg_rtp::frame::rtp_frame *frame = *out;
if (srtp->use_null_cipher())
return RTP_PKT_NOT_HANDLED;
uint8_t iv[16] = { 0 };
uint16_t seq = frame->header.seq;
uint32_t ssrc = frame->header.ssrc;
uint64_t index = (((uint64_t)ctx.roc) << 16) + seq;
uint64_t digest = 0;
/* Sequence number has wrapped around, update Roll-over Counter */
if (seq == 0xffff)
ctx.roc++;
if (srtp->create_iv(iv, ssrc, index, ctx.key_ctx.remote.salt_key) != RTP_OK) {
LOG_ERROR("Failed to create IV, unable to encrypt the RTP packet!");
return RTP_INVALID_VALUE;
}
uvg_rtp::crypto::aes::ctr ctr(ctx.key_ctx.remote.enc_key, sizeof(ctx.key_ctx.remote.enc_key), iv);
/* exit early if RTP packet authentication is disabled... */
if (!srtp->authenticate_rtp()) {
ctr.decrypt(frame->payload, frame->payload, frame->payload_len);
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(ctx.key_ctx.remote.auth_key, AES_KEY_LENGTH);
hmac_sha1.update((uint8_t *)frame, frame->payload_len - AUTH_TAG_LENGTH);
hmac_sha1.update((uint8_t *)&ctx.roc, sizeof(ctx.roc));
hmac_sha1.final((uint8_t *)&digest);
if (memcmp(&digest, &frame[frame->payload_len - AUTH_TAG_LENGTH], AUTH_TAG_LENGTH)) {
LOG_ERROR("Authentication tag mismatch!");
return RTP_AUTH_TAG_MISMATCH;
}
size_t size_ = frame->payload_len - sizeof(uvg_rtp::frame::rtp_header) - 8;
uint8_t *new_buffer = new uint8_t[size_];
ctr.decrypt(new_buffer, frame->payload, size_);
memset(frame->payload, 0, frame->payload_len);
memcpy(frame->payload, new_buffer, size_);
delete[] new_buffer;
return RTP_OK;
}
rtp_error_t uvg_rtp::srtp::send_packet_handler(void *arg, uvg_rtp::buf_vec& buffers)
{
auto srtp = (uvg_rtp::srtp *)arg;
if (srtp->use_null_cipher())
return RTP_OK;
auto frame = (uvg_rtp::frame::rtp_frame *)buffers.at(0).second;
auto rtp = buffers.at(buffers.size() - 1);
auto ctx = srtp->get_ctx();
rtp_error_t ret = srtp->encrypt(
ntohl(frame->header.ssrc),
ntohs(frame->header.seq),
rtp.second,
rtp.first
);
if (!srtp->authenticate_rtp())
return RTP_OK;
/* create authentication tag for the packet and push it to the vector buffer */
auto hmac_sha1 = uvg_rtp::crypto::hmac::sha1(ctx.key_ctx.local.auth_key, AES_KEY_LENGTH);
for (size_t i = 0; i < buffers.size() - 1; ++i)
hmac_sha1.update((uint8_t *)buffers[i].second, buffers[i].first);
hmac_sha1.update((uint8_t *)&ctx.roc, sizeof(ctx.roc));
hmac_sha1.final((uint8_t *)buffers[buffers.size() - 1].second);
return ret;
}
#endif

196
src/srtp/base.cc Normal file
View File

@ -0,0 +1,196 @@
#include <cstring>
#include <iostream>
#include "srtp/base.hh"
#ifdef __RTP_CRYPTO__
#include "crypto.hh"
#include <cryptopp/hex.h>
#endif
uvg_rtp::base_srtp::base_srtp():
srtp_ctx_(),
use_null_cipher_(false),
authenticate_rtp_(false)
{
}
uvg_rtp::base_srtp::~base_srtp()
{
}
#ifdef __RTP_CRYPTO__
bool uvg_rtp::base_srtp::use_null_cipher()
{
return use_null_cipher_;
}
bool uvg_rtp::base_srtp::authenticate_rtp()
{
return authenticate_rtp_;
}
uvg_rtp::srtp_ctx_t& uvg_rtp::base_srtp::get_ctx()
{
return srtp_ctx_;
}
rtp_error_t uvg_rtp::base_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;
}
rtp_error_t uvg_rtp::base_srtp::create_iv(uint8_t *out, uint32_t ssrc, uint64_t index, uint8_t *salt)
{
if (!out || !salt)
return RTP_INVALID_VALUE;
uint8_t buf[8];
int i;
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] ^= buf[i];
for (i = 0; i < 14; i++)
out[i] ^= salt[i];
return RTP_OK;
}
rtp_error_t uvg_rtp::base_srtp::init(int type, int flags)
{
srtp_ctx_.roc = 0;
srtp_ctx_.type = type;
srtp_ctx_.enc = AES_128;
srtp_ctx_.hmac = HMAC_SHA1;
srtp_ctx_.mki_size = 0;
srtp_ctx_.mki_present = false;
srtp_ctx_.mki = nullptr;
srtp_ctx_.master_key = srtp_ctx_.key_ctx.master.local_key;
srtp_ctx_.master_salt = srtp_ctx_.key_ctx.master.local_salt;
srtp_ctx_.mk_cnt = 0;
srtp_ctx_.n_e = AES_KEY_LENGTH;
srtp_ctx_.n_a = HMAC_KEY_LENGTH;
srtp_ctx_.s_l = 0;
srtp_ctx_.replay = nullptr;
use_null_cipher_ = !!(flags & RCE_SRTP_NULL_CIPHER);
authenticate_rtp_ = !!(flags & RCE_SRTP_AUTHENTICATE_RTP);
int label_enc = 0;
int label_auth = 0;
int label_salt = 0;
if (type == SRTP) {
label_enc = SRTP_ENCRYPTION;
label_auth = SRTP_AUTHENTICATION;
label_salt = SRTP_SALTING;
} else {
label_enc = SRTCP_ENCRYPTION;
label_auth = SRTCP_AUTHENTICATION;
label_salt = SRTCP_SALTING;
}
/* Local aka encryption keys */
(void)derive_key(
label_enc,
srtp_ctx_.key_ctx.master.local_key,
srtp_ctx_.key_ctx.master.local_salt,
srtp_ctx_.key_ctx.local.enc_key,
AES_KEY_LENGTH
);
(void)derive_key(
label_auth,
srtp_ctx_.key_ctx.master.local_key,
srtp_ctx_.key_ctx.master.local_salt,
srtp_ctx_.key_ctx.local.auth_key,
AES_KEY_LENGTH
);
(void)derive_key(
label_salt,
srtp_ctx_.key_ctx.master.local_key,
srtp_ctx_.key_ctx.master.local_salt,
srtp_ctx_.key_ctx.local.salt_key,
SALT_LENGTH
);
/* Remote aka decryption keys */
(void)derive_key(
label_enc,
srtp_ctx_.key_ctx.master.remote_key,
srtp_ctx_.key_ctx.master.remote_salt,
srtp_ctx_.key_ctx.remote.enc_key,
AES_KEY_LENGTH
);
(void)derive_key(
label_auth,
srtp_ctx_.key_ctx.master.remote_key,
srtp_ctx_.key_ctx.master.remote_salt,
srtp_ctx_.key_ctx.remote.auth_key,
AES_KEY_LENGTH
);
(void)derive_key(
label_salt,
srtp_ctx_.key_ctx.master.remote_key,
srtp_ctx_.key_ctx.master.remote_salt,
srtp_ctx_.key_ctx.remote.salt_key,
SALT_LENGTH
);
return RTP_OK;
}
rtp_error_t uvg_rtp::base_srtp::init_zrtp(int type, int flags, uvg_rtp::rtp *rtp, uvg_rtp::zrtp *zrtp)
{
(void)rtp;
if (!zrtp)
return RTP_INVALID_VALUE;
/* ZRTP key derivation function expects the keys lengths to be given in bits */
rtp_error_t ret = zrtp->get_srtp_keys(
srtp_ctx_.key_ctx.master.local_key, AES_KEY_LENGTH * 8,
srtp_ctx_.key_ctx.master.remote_key, AES_KEY_LENGTH * 8,
srtp_ctx_.key_ctx.master.local_salt, SALT_LENGTH * 8,
srtp_ctx_.key_ctx.master.remote_salt, SALT_LENGTH * 8
);
if (ret != RTP_OK) {
LOG_ERROR("Failed to derive keys for SRTP session!");
return ret;
}
return init(type, flags);
}
rtp_error_t uvg_rtp::base_srtp::init_user(int type, int flags, uint8_t *key, uint8_t *salt)
{
if (!key || !salt)
return RTP_INVALID_VALUE;
memcpy(srtp_ctx_.key_ctx.master.local_key, key, AES_KEY_LENGTH);
memcpy(srtp_ctx_.key_ctx.master.remote_key, key, AES_KEY_LENGTH);
memcpy(srtp_ctx_.key_ctx.master.local_salt, salt, SALT_LENGTH);
memcpy(srtp_ctx_.key_ctx.master.remote_salt, salt, SALT_LENGTH);
/* TODO: why srtp keys are derived using zrtp kdf? */
return init(type, flags);
}
#endif

4
src/srtp/module.mk Normal file
View File

@ -0,0 +1,4 @@
SOURCES += \
src/srtp/base.cc \
src/srtp/srtp.cc \
src/srtp/srtcp.cc

72
src/srtp/srtcp.cc Normal file
View File

@ -0,0 +1,72 @@
#include <cstring>
#include <iostream>
#include "srtp/srtcp.hh"
#ifdef __RTP_CRYPTO__
#include "crypto.hh"
#include <cryptopp/hex.h>
#endif
uvg_rtp::srtcp::srtcp()
{
}
uvg_rtp::srtcp::~srtcp()
{
}
#ifdef __RTP_CRYPTO__
rtp_error_t uvg_rtp::srtcp::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 };
if (create_iv(iv, ssrc, seq, srtp_ctx_.key_ctx.local.salt_key) != RTP_OK) {
LOG_ERROR("Failed to create IV, unable to encrypt the RTP packet!");
return RTP_INVALID_VALUE;
}
uvg_rtp::crypto::aes::ctr ctr(srtp_ctx_.key_ctx.local.enc_key, sizeof(srtp_ctx_.key_ctx.local.enc_key), iv);
ctr.encrypt(buffer, buffer, len);
return RTP_OK;
}
rtp_error_t uvg_rtp::srtcp::decrypt(uint32_t ssrc, uint32_t seq, uint8_t *buffer, size_t size)
{
uint8_t iv[16] = { 0 };
uint64_t digest = 0;
if (create_iv(iv, ssrc, seq, srtp_ctx_.key_ctx.remote.salt_key) != RTP_OK) {
LOG_ERROR("Failed to create IV, unable to encrypt the RTP packet!");
return RTP_INVALID_VALUE;
}
uvg_rtp::crypto::aes::ctr ctr(srtp_ctx_.key_ctx.remote.enc_key, sizeof(srtp_ctx_.key_ctx.remote.enc_key), iv);
/* ... otherwise calculate authentication tag for the packet
* and compare it against the one we received */
auto hmac_sha1 = uvg_rtp::crypto::hmac::sha1(srtp_ctx_.key_ctx.remote.auth_key, AES_KEY_LENGTH);
hmac_sha1.update(buffer, size - AUTH_TAG_LENGTH - SRTCP_INDEX_LENGTH);
hmac_sha1.final((uint8_t *)&digest);
if (memcmp(&digest, &buffer[size - AUTH_TAG_LENGTH - SRTCP_INDEX_LENGTH], AUTH_TAG_LENGTH)) {
LOG_ERROR("Authentication tag mismatch!");
return RTP_AUTH_TAG_MISMATCH;
}
size_t size_ = size - AUTH_TAG_LENGTH - SRTCP_INDEX_LENGTH - RTCP_HEADER_LENGTH;
uint8_t *new_buffer = new uint8_t[size_];
ctr.decrypt(new_buffer, buffer + RTCP_HEADER_LENGTH, size_);
memset(buffer + RTCP_HEADER_LENGTH, 0, size);
memcpy(buffer + RTCP_HEADER_LENGTH, new_buffer, size_);
delete[] new_buffer;
return RTP_OK;
}
#endif

135
src/srtp/srtp.cc Normal file
View File

@ -0,0 +1,135 @@
#include <cstring>
#include <iostream>
/* #include "srtp.hh" */
#include "srtp/base.hh"
#include "srtp/srtp.hh"
#ifdef __RTP_CRYPTO__
#include "crypto.hh"
#include <cryptopp/hex.h>
#endif
uvg_rtp::srtp::srtp()
{
}
uvg_rtp::srtp::~srtp()
{
}
#ifdef __RTP_CRYPTO__
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;
/* Sequence number has wrapped around, update Roll-over Counter */
if (seq == 0xffff)
srtp_ctx_.roc++;
if (create_iv(iv, ssrc, index, srtp_ctx_.key_ctx.local.salt_key) != RTP_OK) {
LOG_ERROR("Failed to create IV, unable to encrypt the RTP packet!");
return RTP_INVALID_VALUE;
}
uvg_rtp::crypto::aes::ctr ctr(srtp_ctx_.key_ctx.local.enc_key, sizeof(srtp_ctx_.key_ctx.local.enc_key), iv);
ctr.encrypt(buffer, buffer, len);
return RTP_OK;
}
rtp_error_t uvg_rtp::srtp::recv_packet_handler(void *arg, int flags, frame::rtp_frame **out)
{
(void)flags;
uvg_rtp::srtp *srtp = (uvg_rtp::srtp *)arg;
uvg_rtp::srtp_ctx_t ctx = srtp->get_ctx();
uvg_rtp::frame::rtp_frame *frame = *out;
if (srtp->use_null_cipher())
return RTP_PKT_NOT_HANDLED;
uint8_t iv[16] = { 0 };
uint16_t seq = frame->header.seq;
uint32_t ssrc = frame->header.ssrc;
uint64_t index = (((uint64_t)ctx.roc) << 16) + seq;
uint64_t digest = 0;
/* Sequence number has wrapped around, update Roll-over Counter */
if (seq == 0xffff)
ctx.roc++;
if (srtp->create_iv(iv, ssrc, index, ctx.key_ctx.remote.salt_key) != RTP_OK) {
LOG_ERROR("Failed to create IV, unable to encrypt the RTP packet!");
return RTP_INVALID_VALUE;
}
uvg_rtp::crypto::aes::ctr ctr(ctx.key_ctx.remote.enc_key, sizeof(ctx.key_ctx.remote.enc_key), iv);
/* exit early if RTP packet authentication is disabled... */
if (!srtp->authenticate_rtp()) {
ctr.decrypt(frame->payload, frame->payload, frame->payload_len);
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(ctx.key_ctx.remote.auth_key, AES_KEY_LENGTH);
hmac_sha1.update((uint8_t *)frame, frame->payload_len - AUTH_TAG_LENGTH);
hmac_sha1.update((uint8_t *)&ctx.roc, sizeof(ctx.roc));
hmac_sha1.final((uint8_t *)&digest);
if (memcmp(&digest, &frame[frame->payload_len - AUTH_TAG_LENGTH], AUTH_TAG_LENGTH)) {
LOG_ERROR("Authentication tag mismatch!");
return RTP_AUTH_TAG_MISMATCH;
}
size_t size_ = frame->payload_len - sizeof(uvg_rtp::frame::rtp_header) - 8;
uint8_t *new_buffer = new uint8_t[size_];
ctr.decrypt(new_buffer, frame->payload, size_);
memset(frame->payload, 0, frame->payload_len);
memcpy(frame->payload, new_buffer, size_);
delete[] new_buffer;
return RTP_OK;
}
rtp_error_t uvg_rtp::srtp::send_packet_handler(void *arg, uvg_rtp::buf_vec& buffers)
{
auto srtp = (uvg_rtp::srtp *)arg;
if (srtp->use_null_cipher())
return RTP_OK;
auto frame = (uvg_rtp::frame::rtp_frame *)buffers.at(0).second;
auto rtp = buffers.at(buffers.size() - 1);
auto ctx = srtp->get_ctx();
rtp_error_t ret = srtp->encrypt(
ntohl(frame->header.ssrc),
ntohs(frame->header.seq),
rtp.second,
rtp.first
);
if (!srtp->authenticate_rtp())
return RTP_OK;
/* create authentication tag for the packet and push it to the vector buffer */
auto hmac_sha1 = uvg_rtp::crypto::hmac::sha1(ctx.key_ctx.local.auth_key, AES_KEY_LENGTH);
for (size_t i = 0; i < buffers.size() - 1; ++i)
hmac_sha1.update((uint8_t *)buffers[i].second, buffers[i].first);
hmac_sha1.update((uint8_t *)&ctx.roc, sizeof(ctx.roc));
hmac_sha1.final((uint8_t *)buffers[buffers.size() - 1].second);
return ret;
}
#endif