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:
parent
1aa6a17348
commit
bb826570ef
|
@ -2,6 +2,7 @@ src/*.o
|
|||
src/formats/*.o
|
||||
src/mzrtp/*.o
|
||||
src/rtcp/*.o
|
||||
src/srtp/*.o
|
||||
src/crypto/*.o
|
||||
libkvzrtp.a
|
||||
*.a
|
||||
|
|
4
Makefile
4
Makefile
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "srtp.hh"
|
||||
#include "util.hh"
|
||||
|
||||
namespace uvg_rtp {
|
||||
|
|
|
@ -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_;
|
|
@ -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);
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
};
|
||||
};
|
|
@ -5,6 +5,7 @@
|
|||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "debug.hh"
|
||||
#include "formats/media.hh"
|
||||
|
||||
#define INVALID_SEQ 0xffffffff
|
||||
|
|
|
@ -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;
|
||||
|
|
300
src/srtp.cc
300
src/srtp.cc
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
SOURCES += \
|
||||
src/srtp/base.cc \
|
||||
src/srtp/srtp.cc \
|
||||
src/srtp/srtcp.cc
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue