diff --git a/README.md b/README.md index d0c5a42..3b44799 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/include/re_srtp.h b/include/re_srtp.h index dae6051..cdb495b 100644 --- a/include/re_srtp.h +++ b/include/re_srtp.h @@ -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 { diff --git a/src/srtp/misc.c b/src/srtp/misc.c index 34c21c8..8b4d5c3 100644 --- a/src/srtp/misc.c +++ b/src/srtp/misc.c @@ -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 "?"; } } diff --git a/src/srtp/replay.c b/src/srtp/replay.c index 8f99b89..f6d16d9 100644 --- a/src/srtp/replay.c +++ b/src/srtp/replay.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "srtp.h" diff --git a/src/srtp/srtcp.c b/src/srtp/srtcp.c index d29889c..d677898 100644 --- a/src/srtp/srtcp.c +++ b/src/srtp/srtcp.c @@ -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; diff --git a/src/srtp/srtp.c b/src/srtp/srtp.c index 1e99e0e..7625126 100644 --- a/src/srtp/srtp.c +++ b/src/srtp/srtp.c @@ -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; diff --git a/src/srtp/srtp.h b/src/srtp/srtp.h index a937337..1c2dac2 100644 --- a/src/srtp/srtp.h +++ b/src/srtp/srtp.h @@ -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); diff --git a/src/srtp/stream.c b/src/srtp/stream.c index abc6c48..21393d8 100644 --- a/src/srtp/stream.c +++ b/src/srtp/stream.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "srtp.h"