Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/srtp/README b/src/srtp/README
new file mode 100644
index 0000000..bd4b980
--- /dev/null
+++ b/src/srtp/README
@@ -0,0 +1,42 @@
+SRTP module
+-----------
+
+The SRTP module implements Secure RTP as defined in RFC 3711.
+It provides a clean and user friendly API and can be used
+as a standalone module.
+
+
+
+
+Requirements and features:
+
+RFC 3711 yes
+RFC 6188 yes
+Multiple Master keys: no
+Key derivation rate: 0 (zero)
+Salting keys: yes
+SRTP protection: yes
+SRTCP protection: yes
+Replay protection: yes
+Encryption: yes
+Authentication: yes
+MKI (Master Key Identifier): no
+Authentication tag length: 32-bit and 80-bit
+ROC (Roll Over Counter): yes
+Master key lifetime: no
+Multiple SSRCs: yes
+Performance: better than libsrtp
+
+Cryptographic transforms:
+- AES in Counter mode: yes
+- AES in f8-mode: no
+- NULL Cipher: no
+
+Authentication transform:
+- HMAC-SHA1: yes
+- NULL auth: no
+
+master key lengths:
+- 128 bits yes
+- 192 bits no
+- 256 bits yes
diff --git a/src/srtp/misc.c b/src/srtp/misc.c
new file mode 100644
index 0000000..8b4d5c3
--- /dev/null
+++ b/src/srtp/misc.c
@@ -0,0 +1,120 @@
+/**
+ * @file srtp/misc.c SRTP functions
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_aes.h>
+#include <re_sa.h>
+#include <re_srtp.h>
+#include "srtp.h"
+
+
+/*
+ * Appendix A: Pseudocode for Index Determination
+ *
+ * In the following, signed arithmetic is assumed.
+ */
+uint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq)
+{
+ int v;
+
+ if (s_l < 32768) {
+
+ if ((int)seq - (int)s_l > 32768)
+ v = (roc-1) & 0xffffffffu;
+ else
+ v = roc;
+ }
+ else {
+ if ((int)s_l - 32768 > seq)
+ v = (roc+1) & 0xffffffffu;
+ else
+ v = roc;
+ }
+
+ return seq + v*65536;
+}
+
+
+int srtp_derive(uint8_t *out, size_t out_len, uint8_t label,
+ const uint8_t *master_key, size_t key_bytes,
+ const uint8_t *master_salt, size_t salt_bytes)
+{
+ uint8_t x[AES_BLOCK_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ static const uint8_t null[AES_BLOCK_SIZE * 2];
+ struct aes *aes;
+ int err;
+
+ if (!out || !master_key || !master_salt)
+ return EINVAL;
+
+ if (out_len > sizeof(null) || salt_bytes > sizeof(x))
+ return EINVAL;
+
+ memcpy(x, master_salt, salt_bytes);
+ x[7] ^= label;
+
+ /* NOTE: Counter Mode is used for both CTR and GCM */
+ err = aes_alloc(&aes, AES_MODE_CTR, master_key, key_bytes*8, x);
+ if (err)
+ return err;
+
+ err = aes_encr(aes, out, null, out_len);
+
+ mem_deref(aes);
+
+ return err;
+
+}
+
+
+void srtp_iv_calc(union vect128 *iv, const union vect128 *k_s,
+ uint32_t ssrc, uint64_t ix)
+{
+ if (!iv || !k_s)
+ return;
+
+ iv->u32[0] = k_s->u32[0];
+ iv->u32[1] = k_s->u32[1] ^ htonl(ssrc);
+ iv->u32[2] = k_s->u32[2] ^ htonl((uint32_t)(ix>>16));
+ iv->u16[6] = k_s->u16[6] ^ htons((uint16_t)ix);
+ iv->u16[7] = 0;
+}
+
+
+/*
+ * NOTE: The IV for AES-GCM is 12 bytes
+ */
+void srtp_iv_calc_gcm(union vect128 *iv, const union vect128 *k_s,
+ uint32_t ssrc, uint64_t ix)
+{
+ if (!iv || !k_s)
+ return;
+
+ iv->u16[0] = k_s->u16[0];
+ iv->u16[1] = k_s->u16[1] ^ htons(ssrc >> 16);
+ iv->u16[2] = k_s->u16[2] ^ htons(ssrc & 0xffff);
+ iv->u16[3] = k_s->u16[3] ^ htons((ix >> 32) & 0xffff);
+ iv->u16[4] = k_s->u16[4] ^ htons((ix >> 16) & 0xffff);
+ iv->u16[5] = k_s->u16[5] ^ htons(ix & 0xffff);
+}
+
+
+const char *srtp_suite_name(enum srtp_suite suite)
+{
+ switch (suite) {
+
+ case SRTP_AES_CM_128_HMAC_SHA1_32: return "AES_CM_128_HMAC_SHA1_32";
+ case SRTP_AES_CM_128_HMAC_SHA1_80: return "AES_CM_128_HMAC_SHA1_80";
+ case SRTP_AES_256_CM_HMAC_SHA1_32: return "AES_256_CM_HMAC_SHA1_32";
+ case SRTP_AES_256_CM_HMAC_SHA1_80: return "AES_256_CM_HMAC_SHA1_80";
+ case SRTP_AES_128_GCM: return "AEAD_AES_128_GCM";
+ case SRTP_AES_256_GCM: return "AEAD_AES_256_GCM";
+ default: return "?";
+ }
+}
diff --git a/src/srtp/mod.mk b/src/srtp/mod.mk
new file mode 100644
index 0000000..1494c8b
--- /dev/null
+++ b/src/srtp/mod.mk
@@ -0,0 +1,11 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS += srtp/misc.c
+SRCS += srtp/replay.c
+SRCS += srtp/srtcp.c
+SRCS += srtp/srtp.c
+SRCS += srtp/stream.c
diff --git a/src/srtp/replay.c b/src/srtp/replay.c
new file mode 100644
index 0000000..f6d16d9
--- /dev/null
+++ b/src/srtp/replay.c
@@ -0,0 +1,64 @@
+/**
+ * @file srtp/replay.c SRTP replay protection
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_aes.h>
+#include <re_srtp.h>
+#include "srtp.h"
+
+
+enum {
+ SRTP_WINDOW_SIZE = 64
+};
+
+
+void srtp_replay_init(struct replay *replay)
+{
+ if (!replay)
+ return;
+
+ replay->bitmap = 0;
+ replay->lix = 0;
+}
+
+
+/*
+ * Returns false if packet disallowed, true if packet permitted
+ */
+bool srtp_replay_check(struct replay *replay, uint64_t ix)
+{
+ uint64_t diff;
+
+ if (!replay)
+ return false;
+
+ if (ix > replay->lix) {
+ diff = ix - replay->lix;
+
+ if (diff < SRTP_WINDOW_SIZE) { /* In window */
+ replay->bitmap <<= diff;
+ replay->bitmap |= 1; /* set bit for this packet */
+ }
+ else
+ replay->bitmap = 1;
+
+ replay->lix = ix;
+ return true;
+ }
+
+ diff = replay->lix - ix;
+ if (diff >= SRTP_WINDOW_SIZE)
+ return false;
+
+ if (replay->bitmap & (1ULL << diff))
+ return false; /* already seen */
+
+ /* mark as seen */
+ replay->bitmap |= (1ULL << diff);
+
+ return true;
+}
diff --git a/src/srtp/srtcp.c b/src/srtp/srtcp.c
new file mode 100644
index 0000000..d677898
--- /dev/null
+++ b/src/srtp/srtcp.c
@@ -0,0 +1,254 @@
+/**
+ * @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;
+}
diff --git a/src/srtp/srtp.c b/src/srtp/srtp.c
new file mode 100644
index 0000000..7625126
--- /dev/null
+++ b/src/srtp/srtp.c
@@ -0,0 +1,430 @@
+/**
+ * @file srtp.c Secure Real-time Transport Protocol (SRTP)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_hmac.h>
+#include <re_sha.h>
+#include <re_aes.h>
+#include <re_sa.h>
+#include <re_rtp.h>
+#include <re_srtp.h>
+#include "srtp.h"
+
+
+/** SRTP protocol values */
+enum {
+ MAX_KEYLEN = 32, /**< Maximum keylength in bytes */
+};
+
+
+static inline int seq_diff(uint16_t x, uint16_t y)
+{
+ return (int)y - (int)x;
+}
+
+
+static int comp_init(struct comp *c, unsigned offs,
+ const uint8_t *key, size_t key_b,
+ const uint8_t *s, size_t s_b,
+ size_t tag_len, bool encrypted, bool hash,
+ enum aes_mode mode)
+{
+ uint8_t k_e[MAX_KEYLEN], k_a[SHA_DIGEST_LENGTH];
+ int err = 0;
+
+ if (key_b > sizeof(k_e))
+ return EINVAL;
+
+ if (tag_len > SHA_DIGEST_LENGTH)
+ return EINVAL;
+
+ c->tag_len = tag_len;
+ c->mode = mode;
+
+ err |= srtp_derive(k_e, key_b, 0x00+offs, key, key_b, s, s_b);
+ err |= srtp_derive(k_a, sizeof(k_a), 0x01+offs, key, key_b, s, s_b);
+ err |= srtp_derive(c->k_s.u8, 14, 0x02+offs, key, key_b, s, s_b);
+ if (err)
+ return err;
+
+ if (encrypted) {
+ err = aes_alloc(&c->aes, mode, k_e, key_b*8, NULL);
+ if (err)
+ return err;
+ }
+
+ if (hash) {
+ err = hmac_create(&c->hmac, HMAC_HASH_SHA1, k_a, sizeof(k_a));
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+
+static void destructor(void *arg)
+{
+ struct srtp *srtp = arg;
+
+ mem_deref(srtp->rtp.aes);
+ mem_deref(srtp->rtcp.aes);
+ mem_deref(srtp->rtp.hmac);
+ mem_deref(srtp->rtcp.hmac);
+
+ list_flush(&srtp->streaml);
+}
+
+
+int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,
+ const uint8_t *key, size_t key_bytes, int flags)
+{
+ struct srtp *srtp;
+ const uint8_t *master_salt;
+ size_t cipher_bytes, salt_bytes, auth_bytes;
+ enum aes_mode mode;
+ bool hash;
+ int err = 0;
+
+ if (!srtpp || !key)
+ return EINVAL;
+
+ switch (suite) {
+
+ case SRTP_AES_CM_128_HMAC_SHA1_80:
+ mode = AES_MODE_CTR;
+ cipher_bytes = 16;
+ salt_bytes = 14;
+ auth_bytes = 10;
+ hash = true;
+ break;
+
+ case SRTP_AES_CM_128_HMAC_SHA1_32:
+ mode = AES_MODE_CTR;
+ cipher_bytes = 16;
+ salt_bytes = 14;
+ auth_bytes = 4;
+ hash = true;
+ break;
+
+ case SRTP_AES_256_CM_HMAC_SHA1_80:
+ mode = AES_MODE_CTR;
+ cipher_bytes = 32;
+ salt_bytes = 14;
+ auth_bytes = 10;
+ hash = true;
+ break;
+
+ case SRTP_AES_256_CM_HMAC_SHA1_32:
+ mode = AES_MODE_CTR;
+ cipher_bytes = 32;
+ salt_bytes = 14;
+ auth_bytes = 4;
+ hash = true;
+ break;
+
+ case SRTP_AES_128_GCM:
+ mode = AES_MODE_GCM;
+ cipher_bytes = 16;
+ salt_bytes = 12;
+ auth_bytes = 0;
+ hash = false;
+ break;
+
+ case SRTP_AES_256_GCM:
+ mode = AES_MODE_GCM;
+ cipher_bytes = 32;
+ salt_bytes = 12;
+ auth_bytes = 0;
+ hash = false;
+ break;
+
+ default:
+ return ENOTSUP;
+ };
+
+ if ((cipher_bytes + salt_bytes) != key_bytes)
+ return EINVAL;
+
+ master_salt = &key[cipher_bytes];
+
+ srtp = mem_zalloc(sizeof(*srtp), destructor);
+ if (!srtp)
+ return ENOMEM;
+
+ err |= comp_init(&srtp->rtp, 0, key, cipher_bytes,
+ master_salt, salt_bytes, auth_bytes,
+ true, hash, mode);
+ err |= comp_init(&srtp->rtcp, 3, key, cipher_bytes,
+ master_salt, salt_bytes, auth_bytes,
+ !(flags & SRTP_UNENCRYPTED_SRTCP), hash, mode);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(srtp);
+ else
+ *srtpp = srtp;
+
+ return err;
+}
+
+
+int srtp_encrypt(struct srtp *srtp, struct mbuf *mb)
+{
+ struct srtp_stream *strm;
+ struct rtp_header hdr;
+ struct comp *comp;
+ size_t start;
+ uint64_t ix;
+ int err;
+
+ if (!srtp || !mb)
+ return EINVAL;
+
+ comp = &srtp->rtp;
+
+ start = mb->pos;
+
+ err = rtp_hdr_decode(&hdr, mb);
+ if (err)
+ return err;
+
+ err = stream_get_seq(&strm, srtp, hdr.ssrc, hdr.seq);
+ if (err)
+ return err;
+
+ /* Roll-Over Counter (ROC) */
+ if (seq_diff(strm->s_l, hdr.seq) <= -32768) {
+ strm->roc++;
+ strm->s_l = 0;
+ }
+
+ ix = 65536ULL * strm->roc + hdr.seq;
+
+ if (comp->aes && comp->mode == AES_MODE_CTR) {
+ union vect128 iv;
+ uint8_t *p = mbuf_buf(mb);
+
+ srtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix);
+
+ aes_set_iv(comp->aes, iv.u8);
+ err = aes_encr(comp->aes, p, p, mbuf_get_left(mb));
+ if (err)
+ return err;
+ }
+ else if (comp->aes && comp->mode == AES_MODE_GCM) {
+ union vect128 iv;
+ uint8_t *p = mbuf_buf(mb);
+ uint8_t tag[GCM_TAGLEN];
+
+ srtp_iv_calc_gcm(&iv, &comp->k_s, strm->ssrc, ix);
+
+ aes_set_iv(comp->aes, iv.u8);
+
+ /* The RTP Header is Associated Data */
+ err = aes_encr(comp->aes, NULL, &mb->buf[start],
+ mb->pos - start);
+ if (err)
+ return err;
+
+ err = aes_encr(comp->aes, p, p, mbuf_get_left(mb));
+ if (err)
+ return err;
+
+ err = aes_get_authtag(comp->aes, tag, sizeof(tag));
+ if (err)
+ return err;
+
+ mb->pos = mb->end;
+ err = mbuf_write_mem(mb, tag, sizeof(tag));
+ if (err)
+ return err;
+ }
+
+ if (comp->hmac) {
+ const size_t tag_start = mb->end;
+ uint8_t tag[SHA_DIGEST_LENGTH];
+
+ mb->pos = tag_start;
+
+ err = mbuf_write_u32(mb, htonl(strm->roc));
+ if (err)
+ return err;
+
+ mb->pos = start;
+
+ err = hmac_digest(comp->hmac, tag, sizeof(tag),
+ mbuf_buf(mb), mbuf_get_left(mb));
+ if (err)
+ return err;
+
+ mb->pos = mb->end = tag_start;
+
+ err = mbuf_write_mem(mb, tag, comp->tag_len);
+ if (err)
+ return err;
+ }
+
+ if (hdr.seq > strm->s_l)
+ strm->s_l = hdr.seq;
+
+ mb->pos = start;
+
+ return 0;
+}
+
+
+int srtp_decrypt(struct srtp *srtp, struct mbuf *mb)
+{
+ struct srtp_stream *strm;
+ struct rtp_header hdr;
+ struct comp *comp;
+ uint64_t ix;
+ size_t start;
+ int diff;
+ int err;
+
+ if (!srtp || !mb)
+ return EINVAL;
+
+ comp = &srtp->rtp;
+
+ start = mb->pos;
+
+ err = rtp_hdr_decode(&hdr, mb);
+ if (err)
+ return err;
+
+ err = stream_get_seq(&strm, srtp, hdr.ssrc, hdr.seq);
+ if (err)
+ return err;
+
+ diff = seq_diff(strm->s_l, hdr.seq);
+ if (diff > 32768)
+ return ETIMEDOUT;
+
+ /* Roll-Over Counter (ROC) */
+ if (diff <= -32768) {
+ strm->roc++;
+ strm->s_l = 0;
+ }
+
+ ix = srtp_get_index(strm->roc, strm->s_l, hdr.seq);
+
+ if (comp->hmac) {
+ uint8_t tag_calc[SHA_DIGEST_LENGTH];
+ uint8_t tag_pkt[SHA_DIGEST_LENGTH];
+ size_t pld_start, tag_start;
+
+ if (mbuf_get_left(mb) < comp->tag_len)
+ return EBADMSG;
+
+ pld_start = mb->pos;
+ tag_start = mb->end - comp->tag_len;
+
+ mb->pos = tag_start;
+
+ err = mbuf_read_mem(mb, tag_pkt, comp->tag_len);
+ if (err)
+ return err;
+
+ mb->pos = mb->end = tag_start;
+
+ err = mbuf_write_u32(mb, htonl(strm->roc));
+ if (err)
+ return err;
+
+ mb->pos = start;
+
+ err = hmac_digest(comp->hmac, tag_calc, sizeof(tag_calc),
+ mbuf_buf(mb), mbuf_get_left(mb));
+ if (err)
+ return err;
+
+ mb->pos = pld_start;
+ mb->end = tag_start;
+
+ if (0 != memcmp(tag_calc, tag_pkt, comp->tag_len))
+ return EAUTH;
+
+ /*
+ * 3.3.2. Replay Protection
+ *
+ * Secure replay protection is only possible when
+ * integrity protection is present.
+ */
+ if (!srtp_replay_check(&strm->replay_rtp, ix))
+ return EALREADY;
+ }
+
+ if (comp->aes && comp->mode == AES_MODE_CTR) {
+
+ union vect128 iv;
+ uint8_t *p = mbuf_buf(mb);
+
+ srtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix);
+
+ aes_set_iv(comp->aes, iv.u8);
+ err = aes_decr(comp->aes, p, p, mbuf_get_left(mb));
+ if (err)
+ return err;
+ }
+ else if (comp->aes && comp->mode == AES_MODE_GCM) {
+
+ union vect128 iv;
+ uint8_t *p = mbuf_buf(mb);
+ size_t tag_start;
+
+ srtp_iv_calc_gcm(&iv, &comp->k_s, strm->ssrc, ix);
+
+ aes_set_iv(comp->aes, iv.u8);
+
+ /* The RTP Header is Associated Data */
+ err = aes_decr(comp->aes, NULL, &mb->buf[start],
+ mb->pos - start);
+ if (err)
+ return err;
+
+ if (mbuf_get_left(mb) < GCM_TAGLEN)
+ return EBADMSG;
+
+ tag_start = mb->end - GCM_TAGLEN;
+
+ err = aes_decr(comp->aes, p, p, tag_start - mb->pos);
+ if (err)
+ return err;
+
+ err = aes_authenticate(comp->aes, &mb->buf[tag_start],
+ GCM_TAGLEN);
+ if (err)
+ return err;
+
+ mb->end = tag_start;
+
+ /*
+ * 3.3.2. Replay Protection
+ *
+ * Secure replay protection is only possible when
+ * integrity protection is present.
+ */
+ if (!srtp_replay_check(&strm->replay_rtp, ix))
+ return EALREADY;
+
+ }
+
+ if (hdr.seq > strm->s_l)
+ strm->s_l = hdr.seq;
+
+ mb->pos = start;
+
+ return 0;
+}
diff --git a/src/srtp/srtp.h b/src/srtp/srtp.h
new file mode 100644
index 0000000..94b418e
--- /dev/null
+++ b/src/srtp/srtp.h
@@ -0,0 +1,72 @@
+/**
+ * @file srtp.h Secure Real-time Transport Protocol (SRTP) -- internal
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+/** SRTP Protocol values */
+enum {
+ GCM_TAGLEN = 16, /**< GCM taglength in bytes */
+};
+
+
+/** Defines a 128-bit vector in network order */
+union vect128 {
+ uint64_t u64[ 2];
+ uint32_t u32[ 4];
+ uint16_t u16[ 8];
+ uint8_t u8[16];
+};
+
+/** Replay protection */
+struct replay {
+ uint64_t bitmap; /**< Session state - must be 64 bits */
+ uint64_t lix; /**< Last received index */
+};
+
+/** SRTP stream/context -- shared state between RTP/RTCP */
+struct srtp_stream {
+ struct le le; /**< Linked-list element */
+ struct replay replay_rtp; /**< recv -- replay protection for RTP */
+ struct replay replay_rtcp; /**< recv -- replay protection for RTCP */
+ uint32_t ssrc; /**< SSRC -- lookup key */
+ uint32_t roc; /**< send/recv Roll-Over Counter (ROC) */
+ uint16_t s_l; /**< send/recv -- highest SEQ number */
+ bool s_l_set; /**< True if s_l has been set */
+ uint32_t rtcp_index; /**< RTCP-index for sending (31-bits) */
+};
+
+/** SRTP Session */
+struct srtp {
+ struct comp {
+ struct aes *aes; /**< AES Context */
+ enum aes_mode mode; /**< AES encryption mode */
+ struct hmac *hmac; /**< HMAC Context */
+ union vect128 k_s; /**< Derived salting key (14 bytes) */
+ size_t tag_len; /**< CTR Auth. tag length [bytes] */
+ } rtp, rtcp;
+
+ struct list streaml; /**< SRTP-streams (struct srtp_stream) */
+};
+
+
+int stream_get(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc);
+int stream_get_seq(struct srtp_stream **strmp, struct srtp *srtp,
+ uint32_t ssrc, uint16_t seq);
+
+
+int srtp_derive(uint8_t *out, size_t out_len, uint8_t label,
+ const uint8_t *master_key, size_t key_bytes,
+ const uint8_t *master_salt, size_t salt_bytes);
+void srtp_iv_calc(union vect128 *iv, const union vect128 *k_s,
+ uint32_t ssrc, uint64_t ix);
+void srtp_iv_calc_gcm(union vect128 *iv, const union vect128 *k_s,
+ uint32_t ssrc, uint64_t ix);
+uint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq);
+
+
+/* Replay protection */
+
+void srtp_replay_init(struct replay *replay);
+bool srtp_replay_check(struct replay *replay, uint64_t ix);
diff --git a/src/srtp/stream.c b/src/srtp/stream.c
new file mode 100644
index 0000000..21393d8
--- /dev/null
+++ b/src/srtp/stream.c
@@ -0,0 +1,109 @@
+/**
+ * @file srtp/stream.c Secure Real-time Transport Protocol (SRTP) -- stream
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_aes.h>
+#include <re_srtp.h>
+#include "srtp.h"
+
+
+/** SRTP protocol values */
+#ifndef SRTP_MAX_STREAMS
+#define SRTP_MAX_STREAMS (8) /**< Maximum number of SRTP streams */
+#endif
+
+
+static void stream_destructor(void *arg)
+{
+ struct srtp_stream *strm = arg;
+
+ list_unlink(&strm->le);
+}
+
+
+static struct srtp_stream *stream_find(struct srtp *srtp, uint32_t ssrc)
+{
+ struct le *le;
+
+ for (le = srtp->streaml.head; le; le = le->next) {
+
+ struct srtp_stream *strm = le->data;
+
+ if (strm->ssrc == ssrc)
+ return strm;
+ }
+
+ return NULL;
+}
+
+
+static int stream_new(struct srtp_stream **strmp, struct srtp *srtp,
+ uint32_t ssrc)
+{
+ struct srtp_stream *strm;
+
+ if (list_count(&srtp->streaml) >= SRTP_MAX_STREAMS)
+ return ENOSR;
+
+ strm = mem_zalloc(sizeof(*strm), stream_destructor);
+ if (!strm)
+ return ENOMEM;
+
+ strm->ssrc = ssrc;
+ srtp_replay_init(&strm->replay_rtp);
+ srtp_replay_init(&strm->replay_rtcp);
+
+ list_append(&srtp->streaml, &strm->le, strm);
+
+ if (strmp)
+ *strmp = strm;
+
+ return 0;
+}
+
+
+int stream_get(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc)
+{
+ struct srtp_stream *strm;
+
+ if (!strmp || !srtp)
+ return EINVAL;
+
+ strm = stream_find(srtp, ssrc);
+ if (strm) {
+ *strmp = strm;
+ return 0;
+ }
+
+ return stream_new(strmp, srtp, ssrc);
+}
+
+
+int stream_get_seq(struct srtp_stream **strmp, struct srtp *srtp,
+ uint32_t ssrc, uint16_t seq)
+{
+ struct srtp_stream *strm;
+ int err;
+
+ if (!strmp || !srtp)
+ return EINVAL;
+
+ err = stream_get(&strm, srtp, ssrc);
+ if (err)
+ return err;
+
+ /* Set the initial sequence number once only */
+ if (!strm->s_l_set) {
+ strm->s_l = seq;
+ strm->s_l_set = true;
+ }
+
+ *strmp = strm;
+
+ return 0;
+}