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