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;
+}