blob: d67789870d3351bbcbfe67b58a135baade6e585c [file] [log] [blame]
/**
* @file srtcp.c Secure Real-time Transport Control Protocol (SRTCP)
*
* Copyright (C) 2010 Creytiv.com
*/
#include <string.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mbuf.h>
#include <re_list.h>
#include <re_hmac.h>
#include <re_sha.h>
#include <re_aes.h>
#include <re_net.h>
#include <re_srtp.h>
#include "srtp.h"
static int get_rtcp_ssrc(uint32_t *ssrc, struct mbuf *mb)
{
if (mbuf_get_left(mb) < 8)
return EBADMSG;
mbuf_advance(mb, 4);
*ssrc = ntohl(mbuf_read_u32(mb));
return 0;
}
int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb)
{
struct srtp_stream *strm;
struct comp *rtcp;
uint32_t ssrc;
size_t start;
uint32_t ep = 0;
int err;
if (!srtp || !mb)
return EINVAL;
rtcp = &srtp->rtcp;
start = mb->pos;
err = get_rtcp_ssrc(&ssrc, mb);
if (err)
return err;
err = stream_get(&strm, srtp, ssrc);
if (err)
return err;
strm->rtcp_index = (strm->rtcp_index+1) & 0x7fffffff;
if (rtcp->aes && rtcp->mode == AES_MODE_CTR) {
union vect128 iv;
uint8_t *p = mbuf_buf(mb);
srtp_iv_calc(&iv, &rtcp->k_s, ssrc, strm->rtcp_index);
aes_set_iv(rtcp->aes, iv.u8);
err = aes_encr(rtcp->aes, p, p, mbuf_get_left(mb));
if (err)
return err;
ep = 1;
}
else if (rtcp->aes && rtcp->mode == AES_MODE_GCM) {
union vect128 iv;
uint8_t *p = mbuf_buf(mb);
uint8_t tag[GCM_TAGLEN];
const uint32_t ix_be = htonl(1<<31 | strm->rtcp_index);
srtp_iv_calc_gcm(&iv, &rtcp->k_s, ssrc, strm->rtcp_index);
aes_set_iv(rtcp->aes, iv.u8);
/* The RTCP Header and Index is Associated Data */
err = aes_encr(rtcp->aes, NULL, &mb->buf[start],
mb->pos - start);
err |= aes_encr(rtcp->aes, NULL,
(void *)&ix_be, sizeof(ix_be));
if (err)
return err;
err = aes_encr(rtcp->aes, p, p, mbuf_get_left(mb));
if (err)
return err;
err = aes_get_authtag(rtcp->aes, tag, sizeof(tag));
if (err)
return err;
mb->pos = mb->end;
err = mbuf_write_mem(mb, tag, sizeof(tag));
if (err)
return err;
ep = 1;
}
/* append E-bit and SRTCP-index */
mb->pos = mb->end;
err = mbuf_write_u32(mb, htonl(ep<<31 | strm->rtcp_index));
if (err)
return err;
if (rtcp->hmac) {
uint8_t tag[SHA_DIGEST_LENGTH];
mb->pos = start;
err = hmac_digest(rtcp->hmac, tag, sizeof(tag),
mbuf_buf(mb), mbuf_get_left(mb));
if (err)
return err;
mb->pos = mb->end;
err = mbuf_write_mem(mb, tag, rtcp->tag_len);
if (err)
return err;
}
mb->pos = start;
return 0;
}
int srtcp_decrypt(struct srtp *srtp, struct mbuf *mb)
{
size_t start, eix_start, pld_start;
struct srtp_stream *strm;
struct comp *rtcp;
uint32_t v, ix;
uint32_t ssrc;
bool ep;
int err;
if (!srtp || !mb)
return EINVAL;
rtcp = &srtp->rtcp;
start = mb->pos;
err = get_rtcp_ssrc(&ssrc, mb);
if (err)
return err;
err = stream_get(&strm, srtp, ssrc);
if (err)
return err;
pld_start = mb->pos;
if (mbuf_get_left(mb) < (4 + rtcp->tag_len))
return EBADMSG;
/* Read out E-Bit, SRTCP-index and Authentication Tag */
eix_start = mb->end - (4 + rtcp->tag_len);
mb->pos = eix_start;
v = ntohl(mbuf_read_u32(mb));
ep = (v >> 31) & 1;
ix = v & 0x7fffffff;
if (rtcp->hmac) {
uint8_t tag[SHA_DIGEST_LENGTH], tag_pkt[SHA_DIGEST_LENGTH];
const size_t tag_start = mb->pos;
err = mbuf_read_mem(mb, tag_pkt, rtcp->tag_len);
if (err)
return err;
mb->pos = start;
mb->end = tag_start;
err = hmac_digest(rtcp->hmac, tag, sizeof(tag),
mbuf_buf(mb), mbuf_get_left(mb));
if (err)
return err;
if (0 != memcmp(tag, tag_pkt, rtcp->tag_len))
return EAUTH;
/*
* SRTCP replay protection is as defined in Section 3.3.2,
* but using the SRTCP index as the index i and a separate
* Replay List that is specific to SRTCP.
*/
if (!srtp_replay_check(&strm->replay_rtcp, ix))
return EALREADY;
}
mb->end = eix_start;
if (rtcp->aes && ep && rtcp->mode == AES_MODE_CTR) {
union vect128 iv;
uint8_t *p;
mb->pos = pld_start;
p = mbuf_buf(mb);
srtp_iv_calc(&iv, &rtcp->k_s, ssrc, ix);
aes_set_iv(rtcp->aes, iv.u8);
err = aes_decr(rtcp->aes, p, p, mbuf_get_left(mb));
if (err)
return err;
}
else if (rtcp->aes && ep && rtcp->mode == AES_MODE_GCM) {
union vect128 iv;
size_t tag_start;
uint8_t *p;
srtp_iv_calc_gcm(&iv, &rtcp->k_s, ssrc, ix);
aes_set_iv(rtcp->aes, iv.u8);
/* The RTP Header is Associated Data */
err = aes_decr(rtcp->aes, NULL, &mb->buf[start],
pld_start - start);
err |= aes_decr(rtcp->aes, NULL, &mb->buf[eix_start], 4);
if (err)
return err;
mb->pos = pld_start;
p = mbuf_buf(mb);
if (mbuf_get_left(mb) < GCM_TAGLEN)
return EBADMSG;
tag_start = mb->end - GCM_TAGLEN;
err = aes_decr(rtcp->aes, p, p, tag_start - pld_start);
if (err)
return err;
err = aes_authenticate(rtcp->aes, &mb->buf[tag_start],
GCM_TAGLEN);
if (err)
return err;
mb->end = tag_start;
}
mb->pos = start;
return 0;
}