Merge branch 'secure-rtp' into develop
This commit is contained in:
commit
7c2142dddf
|
@ -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_;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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() */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_);
|
||||
|
|
13
src/rtp.cc
13
src/rtp.cc
|
@ -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_;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
115
src/srtp.cc
115
src/srtp.cc
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue