Srtp add gcm (#111)

* srtp: add support for AES GCM cipher (RFC 7714)
This commit is contained in:
Alfred E. Heggestad 2018-03-09 14:21:47 +01:00 committed by Richard Aas
parent 66ae090ea6
commit 25dfb8ccf7
8 changed files with 220 additions and 16 deletions

View file

@ -198,6 +198,7 @@ legend:
* [RFC 6455](https://tools.ietf.org/html/rfc6455) - The WebSocket Protocol
* [RFC 7159](https://tools.ietf.org/html/rfc7159) - JavaScript Object Notation (JSON)
* [RFC 7350](https://tools.ietf.org/html/rfc7350) - DTLS as Transport for STUN
* [RFC 7714](https://tools.ietf.org/html/rfc7714) - AES-GCM Authenticated Encryption in SRTP
## Supported platforms

View file

@ -10,6 +10,8 @@ enum srtp_suite {
SRTP_AES_CM_128_HMAC_SHA1_80,
SRTP_AES_256_CM_HMAC_SHA1_32,
SRTP_AES_256_CM_HMAC_SHA1_80,
SRTP_AES_128_GCM,
SRTP_AES_256_GCM,
};
enum srtp_flags {

View file

@ -59,6 +59,7 @@ int srtp_derive(uint8_t *out, size_t out_len, uint8_t label,
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;
@ -86,6 +87,24 @@ void srtp_iv_calc(union vect128 *iv, const union vect128 *k_s,
}
/*
* 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) {
@ -94,6 +113,8 @@ const char *srtp_suite_name(enum srtp_suite suite)
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 "?";
}
}

View file

@ -6,6 +6,7 @@
#include <re_types.h>
#include <re_mbuf.h>
#include <re_list.h>
#include <re_aes.h>
#include <re_srtp.h>
#include "srtp.h"

View file

@ -53,7 +53,7 @@ int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb)
strm->rtcp_index = (strm->rtcp_index+1) & 0x7fffffff;
if (rtcp->aes) {
if (rtcp->aes && rtcp->mode == AES_MODE_CTR) {
union vect128 iv;
uint8_t *p = mbuf_buf(mb);
@ -66,6 +66,40 @@ int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb)
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;
@ -163,7 +197,7 @@ int srtcp_decrypt(struct srtp *srtp, struct mbuf *mb)
mb->end = eix_start;
if (rtcp->aes && ep) {
if (rtcp->aes && ep && rtcp->mode == AES_MODE_CTR) {
union vect128 iv;
uint8_t *p;
@ -177,6 +211,42 @@ int srtcp_decrypt(struct srtp *srtp, struct mbuf *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;

View file

@ -33,7 +33,8 @@ static inline int seq_diff(uint16_t x, uint16_t y)
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)
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;
@ -45,6 +46,7 @@ static int comp_init(struct comp *c, unsigned offs,
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);
@ -53,14 +55,16 @@ static int comp_init(struct comp *c, unsigned offs,
return err;
if (encrypted) {
err = aes_alloc(&c->aes, AES_MODE_CTR, k_e, key_b*8, NULL);
err = aes_alloc(&c->aes, mode, k_e, key_b*8, NULL);
if (err)
return err;
}
err = hmac_create(&c->hmac, HMAC_HASH_SHA1, k_a, sizeof(k_a));
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;
}
@ -84,7 +88,9 @@ int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,
{
struct srtp *srtp;
const uint8_t *master_salt;
size_t cipher_bytes, auth_bytes;
size_t cipher_bytes, salt_bytes, auth_bytes;
enum aes_mode mode;
bool hash;
int err = 0;
if (!srtpp || !key)
@ -93,30 +99,58 @@ int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,
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 + SRTP_SALT_SIZE) != key_bytes)
if ((cipher_bytes + salt_bytes) != key_bytes)
return EINVAL;
master_salt = &key[cipher_bytes];
@ -126,10 +160,11 @@ int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,
return ENOMEM;
err |= comp_init(&srtp->rtp, 0, key, cipher_bytes,
master_salt, SRTP_SALT_SIZE, auth_bytes, true);
master_salt, salt_bytes, auth_bytes,
true, hash, mode);
err |= comp_init(&srtp->rtcp, 3, key, cipher_bytes,
master_salt, SRTP_SALT_SIZE, auth_bytes,
!(flags & SRTP_UNENCRYPTED_SRTCP));
master_salt, salt_bytes, auth_bytes,
!(flags & SRTP_UNENCRYPTED_SRTCP), hash, mode);
if (err)
goto out;
@ -175,7 +210,7 @@ int srtp_encrypt(struct srtp *srtp, struct mbuf *mb)
ix = 65536ULL * strm->roc + hdr.seq;
if (comp->aes) {
if (comp->aes && comp->mode == AES_MODE_CTR) {
union vect128 iv;
uint8_t *p = mbuf_buf(mb);
@ -186,6 +221,34 @@ int srtp_encrypt(struct srtp *srtp, struct mbuf *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;
@ -303,7 +366,7 @@ int srtp_decrypt(struct srtp *srtp, struct mbuf *mb)
return EALREADY;
}
if (comp->aes) {
if (comp->aes && comp->mode == AES_MODE_CTR) {
union vect128 iv;
uint8_t *p = mbuf_buf(mb);
@ -315,6 +378,48 @@ int srtp_decrypt(struct srtp *srtp, struct mbuf *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;

View file

@ -6,7 +6,7 @@
enum {
SRTP_SALT_SIZE = 14
GCM_TAGLEN = 16, /**< GCM taglength in bytes */
};
@ -40,9 +40,10 @@ struct srtp_stream {
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; /**< Authentication tag length [bytes] */
size_t tag_len; /**< CTR Auth. tag length [bytes] */
} rtp, rtcp;
struct list streaml; /**< SRTP-streams (struct srtp_stream) */
@ -59,6 +60,8 @@ int srtp_derive(uint8_t *out, size_t out_len, uint8_t label,
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);

View file

@ -7,6 +7,7 @@
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_list.h>
#include <re_aes.h>
#include <re_srtp.h>
#include "srtp.h"