Implement encryption/authentication handlers for SRTP

This commit is contained in:
Aaro Altonen 2020-08-12 16:30:15 +03:00
parent 9060ec9859
commit 359e84076c
3 changed files with 79 additions and 107 deletions

View File

@ -127,21 +127,8 @@ namespace uvg_rtp {
* Return RTP_MEMORY allocation failed */
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
*
* Return RTP_OK on success
* Return RTP_INVALID_VALUE if "frame" is nullptr
* Return RTP_NOT_INITIALIZED if SRTP has not been initialized */
rtp_error_t encrypt(uvg_rtp::frame::rtp_frame *frame);
/* Encrypt the payload of "buffers" vector using the private session key
* The payload that is encrypted is the last buffer of "buffers" and the
* RTP header is the first" buffer of "buffers"
*
* Return RTP_OK on success
* Return RTP_INVALID_VALUE if "frame" is nullptr
* Return RTP_NOT_INITIALIZED if SRTP has not been initialized */
rtp_error_t encrypt(std::vector<std::pair<size_t, uint8_t *>>& buffers);
/* 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
*
@ -159,6 +146,19 @@ namespace uvg_rtp {
* 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();
/* Has RTP packet authentication been enabled? */
bool authenticate_rtp();
/* Get reference to the SRTP context (including session keys) */
srtp_ctx_t& get_ctx();
/* Encrypt the payload and add authentication tag (if enabled) */
static rtp_error_t send_packet_handler_buf(void *arg, ssize_t len, void *buf);
static rtp_error_t send_packet_handler_vec(void *arg, std::vector<std::pair<size_t, uint8_t *>>& buffers);
#endif
private:
@ -172,7 +172,7 @@ namespace uvg_rtp {
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);
/* 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);

View File

@ -265,17 +265,6 @@ rtp_error_t uvg_rtp::socket::__sendtov(
#ifdef __linux__
int sent_bytes = 0;
#ifdef __RTP_CRYPTO__
if (srtp_) {
auto ret = srtp_->encrypt(buffers);
if (ret != RTP_OK) {
LOG_ERROR("Failed to encrypt RTP packet!");
return ret;
}
}
#endif
for (size_t i = 0; i < buffers.size(); ++i) {
chunks_[i].iov_len = buffers.at(i).first;
chunks_[i].iov_base = buffers.at(i).second;

View File

@ -55,6 +55,44 @@ 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::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;
@ -169,120 +207,65 @@ rtp_error_t uvg_rtp::srtp::init_user(int type, int flags, uint8_t *key, uint8_t
return __init(type, flags);
}
rtp_error_t uvg_rtp::srtp::__encrypt(uint32_t ssrc, uint16_t seq, uint8_t *buffer, size_t len)
rtp_error_t uvg_rtp::srtp::send_packet_handler_buf(void *arg, ssize_t len, void *buf)
{
if (use_null_cipher_)
auto srtp = (uvg_rtp::srtp *)arg;
if (srtp->use_null_cipher())
return RTP_OK;
uint8_t iv[16] = { 0 };
uint64_t index = (((uint64_t)srtp_ctx_.roc) << 16) + seq;
auto ctx = srtp->get_ctx();
auto frame = (uvg_rtp::frame::rtp_frame *)buf;
auto index = (((uint64_t)ctx.roc) << 16) + ntohs(frame->header.seq);
/* Sequence number has wrapped around, update Roll-over Counter */
if (seq == 0xffff)
srtp_ctx_.roc++;
if (ntohs(frame->header.seq) == 0xffff)
ctx.roc++;
if (create_iv(iv, ssrc, index, key_ctx_.local.salt_key) != RTP_OK) {
if (srtp->create_iv(iv, ntohl(frame->header.ssrc), index, 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(key_ctx_.local.enc_key, sizeof(key_ctx_.local.enc_key), iv);
ctr.encrypt(buffer, buffer, len);
uvg_rtp::crypto::aes::ctr ctr(ctx.key_ctx.local.enc_key, sizeof(ctx.key_ctx.local.enc_key), iv);
ctr.encrypt((uint8_t *)buf, (uint8_t *)buf, len);
return RTP_OK;
}
rtp_error_t uvg_rtp::srtp::encrypt(uvg_rtp::frame::rtp_frame *frame)
rtp_error_t uvg_rtp::srtp::send_packet_handler_vec(void *arg, std::vector<std::pair<size_t, uint8_t *>>& buffers)
{
/* TODO: authentication for complete RTP frame requires co-operation
* with the allocator of that frame -> no good solution for that right now */
auto srtp = (uvg_rtp::srtp *)arg;
return __encrypt(
ntohl(frame->header.ssrc),
ntohs(frame->header.seq),
frame->payload,
frame->payload_len
);
}
if (srtp->use_null_cipher())
return RTP_OK;
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);
uint64_t *digest = new uint64_t;
auto frame = (uvg_rtp::frame::rtp_frame *)buffers.at(0).second;
auto rtp = buffers.at(buffers.size() - 1);
auto ctx = srtp->get_ctx();
auto digest = new uint64_t;
rtp_error_t ret = __encrypt(
rtp_error_t ret = srtp->encrypt(
ntohl(frame->header.ssrc),
ntohs(frame->header.seq),
rtp.second,
rtp.first
);
if (!authenticate_rtp_)
return ret;
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(key_ctx_.local.auth_key, AES_KEY_LENGTH);
auto hmac_sha1 = uvg_rtp::crypto::hmac::sha1(ctx.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.update((uint8_t *)&ctx.roc, sizeof(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)
{
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)
srtp_ctx_.roc++;
if (create_iv(iv, ssrc, index, key_ctx_.remote.salt_key) != RTP_OK) {
LOG_ERROR("Failed to create IV, unable to encrypt the RTP packet!");
return RTP_INVALID_VALUE;
}
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);
/* 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