Skip to content
Snippets Groups Projects
Commit dbd3f6a5 authored by Guillaume Roguez's avatar Guillaume Roguez
Browse files

srtp: add SRTP_AEAD_AES_256_GCM support

This patch adds SRTP_AEAD_AES_256_GCM cipher-suite to SRTP protocol.
This cipher-suite has been standardized by RCF 7714
since December 2015 [1].

This gives us extra security from older AES-128, with only 64-bits
of security (AES-256 has 128-bits of security).
This AEAD cipher is also "constant-time" reducing risk of
side-channel attacks.

On performances, AES-GCM outperforms AES-HMAC if AES-NI cpu instructions
is supported [2]. The drawback is only supported on x86 like machines.
If no hardware acceleration is available, AES-GCM performs as well as
AES-HMAC (even a bit better).
See [3] and [4] for more details about this cipher and its advantages.

Important Changes on implementation:

This cipher uses a 32-bytes master key and a 12-bytes master-salt.

** This cipher-suite requests a 44-bytes of params (was 30 before) **

This implementation is based on nettle AEAD-ciphers API
and is compatible with the older implementation.

Notice that the RCF 7714 requests a full authentication tag to be used,
giving an extra 16 bytes added to the size of the encrypt input data.

** These extra 16 bytes have to be taken in account by the user **

Usage: pass SRTP_AEAD_AES_256_GCM string as suite argument and
a 44-bytes params argument (master key+salt) to ff_srtp_set_crypto.
Use ff_srtp_encrypt and ff_srtp_decrypt as usual.

Tests: unit testing block by compiling srtp.c as a binary and
enable the define TEST_SRTP_AEAD.

[1] https://tools.ietf.org/html/rfc7714
[2] https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf
[3] https://en.wikipedia.org/wiki/Authenticated_encryption
[4] http://crypto.stackexchange.com/questions/27243/what-is-the-advantage-of-aead-ciphers

Change-Id: I0ebab13986dc007cc002ff3618e716e5103c2770
Tuleap: #747
parent a2f6c8d9
No related branches found
No related tags found
No related merge requests found
......@@ -28,6 +28,20 @@
#include <libavutil/log.h>
#include "srtp.h"
#ifdef TEST_SRTP_AEAD
static void dump_memory(const uint8_t *ptr, size_t len)
{
for (size_t i=0; i < len; ++i) {
fprintf(stderr, "%02x", ptr[i]);
if (((i+1) % 16) == 0)
fputc('\n', stderr);
else if (((i+1) % 4) == 0)
fputc(' ', stderr);
}
fputc('\n', stderr);
}
#endif
void ff_srtp_free(struct SRTPContext *s)
{
if (!s)
......@@ -35,7 +49,8 @@ void ff_srtp_free(struct SRTPContext *s)
av_freep(&s->aes);
if (s->hmac)
av_hmac_free(s->hmac);
s->hmac = NULL;
//s->hmac = NULL;
memset(s, 0, sizeof(*s));
}
static void encrypt_counter(struct AVAES *aes, uint8_t *iv, uint8_t *outbuf,
......@@ -62,32 +77,79 @@ static void derive_key(struct AVAES *aes, const uint8_t *salt, int label,
encrypt_counter(aes, input, out, outlen);
}
static int strp_aead_init(struct SRTPContext *s, gnutls_cipher_algorithm_t cipher,
uint8_t master_key_size, const uint8_t *params)
{
s->aes = av_aes_alloc();
if (!s->aes)
return AVERROR(ENOMEM);
memcpy(s->master_key, params, master_key_size);
memcpy(s->master_salt, params + master_key_size, 12);
av_aes_init(s->aes, s->master_key, master_key_size * 8, 0);
derive_key(s->aes, s->master_salt, 0x00, s->rtp_key, master_key_size);
derive_key(s->aes, s->master_salt, 0x02, s->rtp_salt, 12);
derive_key(s->aes, s->master_salt, 0x03, s->rtcp_key, master_key_size);
derive_key(s->aes, s->master_salt, 0x05, s->rtcp_salt, 12);
#ifdef TEST_SRTP_AEAD
memcpy(s->rtp_key, s->master_key, master_key_size);
memcpy(s->rtp_salt, s->master_salt, 12);
memcpy(s->rtcp_key, s->master_key, master_key_size);
memcpy(s->rtcp_salt, s->master_salt, 12);
#endif
gcm_aes256_set_key(&s->aead_rtp_ctx, s->rtp_key);
gcm_aes256_set_key(&s->aead_rtcp_ctx, s->rtcp_key);
return 0;
}
int ff_srtp_set_crypto(struct SRTPContext *s, const char *suite,
const char *params)
{
uint8_t buf[30];
uint8_t buf[32+12];
int params_length;
gnutls_cipher_algorithm_t aead_cipher;
ff_srtp_free(s);
// RFC 4568
if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_80") ||
// RFC 5764 and RFC 7714
if (!strcmp(suite, "AEAD_AES_256_GCM") ||
!strcmp(suite, "SRTP_AEAD_AES_256_GCM")) {
s->aead = 1;
aead_cipher = GNUTLS_CIPHER_AES_256_GCM;
params_length = 32+12;
} else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_80") ||
!strcmp(suite, "SRTP_AES128_CM_HMAC_SHA1_80")) {
s->rtp_hmac_size = s->rtcp_hmac_size = 10;
params_length = 16+14;
} else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_32")) {
s->rtp_hmac_size = s->rtcp_hmac_size = 4;
params_length = 16+14;
} else if (!strcmp(suite, "SRTP_AES128_CM_HMAC_SHA1_32")) {
// RFC 5764 section 4.1.2
s->rtp_hmac_size = 4;
s->rtcp_hmac_size = 10;
params_length = 16+14;
} else {
av_log(NULL, AV_LOG_WARNING, "SRTP Crypto suite %s not supported\n",
suite);
return AVERROR(EINVAL);
}
if (av_base64_decode(buf, params, sizeof(buf)) != sizeof(buf)) {
av_log(NULL, AV_LOG_WARNING, "Incorrect amount of SRTP params\n");
int ret;
if ((ret = av_base64_decode(buf, params, sizeof(buf))) != params_length) {
av_log(NULL, AV_LOG_WARNING, "Incorrect amount of SRTP params (%d)\n", ret);
return AVERROR(EINVAL);
}
// AEAD algorithms setup
if (s->aead)
return strp_aead_init(s, aead_cipher, 32, buf);
// MKI and lifetime not handled yet
s->aes = av_aes_alloc();
s->hmac = av_hmac_alloc(AV_HMAC_SHA1);
......@@ -123,8 +185,158 @@ static void create_iv(uint8_t *iv, const uint8_t *salt, uint64_t index,
iv[i] ^= salt[i];
}
static void create_eaed_iv(uint8_t *iv, int rtcp, const uint8_t *salt, uint64_t index,
uint32_t ssrc, uint32_t roc, uint16_t seq)
{
int i;
memset(iv, 0, 12);
AV_WB32(&iv[2], ssrc);
if (rtcp) {
AV_WB32(&iv[8], index);
} else {
AV_WB32(&iv[6], roc);
AV_WB16(&iv[10], seq);
}
#ifdef TEST_SRTP_AEAD
fprintf(stderr, "base: ");
dump_memory(iv, 12);
fprintf(stderr, "salt: ");
dump_memory(salt, 12);
#endif
for (i = 0; i < 12; i++)
iv[i] ^= salt[i];
#ifdef TEST_SRTP_AEAD
fprintf(stderr, "iv: ");
dump_memory(iv, 12);
#endif
}
int srtp_aead_decrypt(struct SRTPContext *s, uint8_t *buf, int *lenptr)
{
uint8_t iv[12];
uint8_t *in = buf;
int len = *lenptr;
#warning seq_largest and roc may be unitialized
int av_uninit(seq_largest);
uint32_t ssrc, av_uninit(roc);
uint64_t index;
int rtcp, seq;
size_t cipher_len;
// TODO: Missing replay protection
if (len < 12)
return AVERROR_INVALIDDATA;
rtcp = RTP_PT_IS_RTCP(buf[1]);
if (!rtcp) {
seq = AV_RB16(buf + 2);
uint32_t v;
// RFC 3711 section 3.3.1, appendix A
seq_largest = s->seq_initialized ? s->seq_largest : seq;
v = roc = s->roc;
if (seq_largest < 32768) {
if (seq - seq_largest > 32768)
v = roc - 1;
} else {
if (seq_largest - 32768 > seq)
v = roc + 1;
}
if (v == roc) {
seq_largest = FFMAX(seq_largest, seq);
} else if (v == roc + 1) {
seq_largest = seq;
roc = v;
}
index = seq + (((uint64_t)v) << 16);
}
if (rtcp) {
uint32_t srtcp_index = AV_RB32(buf + len - 4);
len -= 4;
*lenptr = len;
ssrc = AV_RB32(buf + 4);
index = srtcp_index & 0x7fffffff;
buf += 8;
len -= 8;
if (!(srtcp_index & 0x80000000))
cipher_len = 0;
else
cipher_len = len - 16;
} else {
int ext, csrc;
s->seq_initialized = 1;
s->seq_largest = seq_largest;
s->roc = roc;
csrc = buf[0] & 0x0f;
ext = buf[0] & 0x10;
ssrc = AV_RB32(buf + 8);
*lenptr = len;
buf += 12;
len -= 12;
buf += 4 * csrc;
len -= 4 * csrc;
if (len < 0)
return AVERROR_INVALIDDATA;
if (ext) {
if (len < 4)
return AVERROR_INVALIDDATA;
ext = (AV_RB16(buf + 2) + 1) * 4;
if (len < ext)
return AVERROR_INVALIDDATA;
len -= ext;
buf += ext;
}
cipher_len = len - 16; // remove auth tag length
}
create_eaed_iv(iv, rtcp, rtcp ? s->rtcp_salt : s->rtp_salt, index, ssrc, s->roc, seq);
gcm_aes256_set_iv(rtcp ? &s->aead_rtcp_ctx : &s->aead_rtp_ctx, 12, iv);
size_t ptext_len = *lenptr - (buf - in);
uint8_t auth_tag[16];
if (rtcp) {
uint8_t auth_data[12]; // need to use a temporary buffer for AAD as update need to be called only one time
*(uint64_t*)auth_data = *(uint64_t*)in;
((uint32_t*)auth_data)[2] = *(uint32_t*)(buf + ptext_len);
gcm_aes256_update(&s->aead_rtcp_ctx, sizeof(auth_data), auth_data);
gcm_aes256_decrypt(&s->aead_rtcp_ctx, cipher_len, buf, buf);
gcm_aes256_digest(&s->aead_rtcp_ctx, 16, auth_tag);
ptext_len = cipher_len;
} else {
gcm_aes256_update(&s->aead_rtp_ctx, buf - in, in);
gcm_aes256_decrypt(&s->aead_rtp_ctx, cipher_len, buf, buf);
gcm_aes256_digest(&s->aead_rtp_ctx, 16, auth_tag);
ptext_len = cipher_len;
}
if (memcmp(buf + cipher_len, auth_tag, 16) != 0) {
av_log(NULL, AV_LOG_WARNING, "Authentification failed\n");
return AVERROR_INVALIDDATA;
}
*lenptr = ptext_len + (buf - in); // plus AAD
return 0;
}
int ff_srtp_decrypt(struct SRTPContext *s, uint8_t *buf, int *lenptr)
{
/* AEAD algorithm contains MAC algorithm, has it's own IV and remove usage of some SRTP field */
if (s->aead == 1)
return srtp_aead_decrypt(s, buf, lenptr);
uint8_t iv[16] = { 0 }, hmac[20];
int len = *lenptr;
#warning seq_largest and roc may be unitialized
......@@ -236,9 +448,98 @@ int ff_srtp_decrypt(struct SRTPContext *s, uint8_t *buf, int *lenptr)
return 0;
}
int srtp_aead_encrypt(struct SRTPContext *s, const uint8_t *in, int len,
uint8_t *out, int outlen)
{
uint8_t iv[12];
int i, rtcp;
uint32_t ssrc, index=0;
uint16_t seq;
uint8_t *buf, *salt;
if (len < 8)
return AVERROR_INVALIDDATA;
memcpy(out, in, len);
buf = out;
rtcp = RTP_PT_IS_RTCP(in[1]);
if (rtcp) {
salt = s->rtcp_salt;
ssrc = AV_RB32(buf + 4);
index = s->rtcp_index++;
index = 0x5d4;
} else {
salt = s->rtp_salt;
seq = AV_RB16(in + 2);
ssrc = AV_RB32(in + 8);
if (seq < s->seq_largest)
s->roc++;
s->seq_largest = seq;
index = seq + (((uint64_t)s->roc) << 16);
}
if (rtcp) {
buf += 8;
len -= 8;
} else {
// Compute auth and plaintext sizes
int csrc = buf[0] & 0x0f;
int ext = buf[0] & 0x10;
buf += 12;
len -= 12;
buf += 4 * csrc;
len -= 4 * csrc;
if (len < 0)
return AVERROR_INVALIDDATA;
if (ext) {
if (len < 4)
return AVERROR_INVALIDDATA;
ext = (AV_RB16(buf + 2) + 1) * 4;
if (len < ext)
return AVERROR_INVALIDDATA;
len -= ext;
buf += ext;
}
}
create_eaed_iv(iv, rtcp, rtcp ? s->rtcp_salt : s->rtp_salt, index, ssrc, s->roc, seq);
gcm_aes256_set_iv(rtcp ? &s->aead_rtcp_ctx : &s->aead_rtp_ctx, 12, iv);
size_t ctext_len = outlen - (buf - out);
uint8_t auth_tag[16];
if (rtcp) {
AV_WB32(buf + ctext_len - 4, 0x80000000 | index);
uint8_t auth_data[12]; // need to use a temporary buffer for AAD as update need to be called only one time
*(uint64_t*)auth_data = *(uint64_t*)out;
((uint32_t*)auth_data)[2] = *(uint32_t*)(buf + ctext_len - 4);
gcm_aes256_update(&s->aead_rtcp_ctx, sizeof(auth_data), auth_data);
gcm_aes256_encrypt(&s->aead_rtcp_ctx, len, buf, buf);
gcm_aes256_digest(&s->aead_rtcp_ctx, 16, auth_tag);
memcpy(buf+len, auth_tag, 16);
ctext_len = len + 16 + 4;
} else {
gcm_aes256_update(&s->aead_rtp_ctx, buf - out, out);
gcm_aes256_encrypt(&s->aead_rtp_ctx, len, buf, buf);
gcm_aes256_digest(&s->aead_rtp_ctx, 16, auth_tag);
memcpy(buf+len, auth_tag, 16);
ctext_len = len + 16;
}
return buf + ctext_len - out;
}
int ff_srtp_encrypt(struct SRTPContext *s, const uint8_t *in, int len,
uint8_t *out, int outlen)
{
/* AEAD algorithm contains MAC algorithm, has it's own IV and remove usage of some SRTP field */
if (s->aead == 1)
return srtp_aead_encrypt(s, in, len, out, outlen);
uint8_t iv[16] = { 0 }, hmac[20];
uint64_t index;
uint32_t ssrc;
......@@ -324,3 +625,211 @@ int ff_srtp_encrypt(struct SRTPContext *s, const uint8_t *in, int len,
len += hmac_size;
return buf + len - out;
}
#ifdef TEST_SRTP_AEAD
int test_gcm256(const uint8_t* plain, int plain_len, const uint8_t* cipher, int cipher_len, int rtcp)
{
uint8_t master_salt[32 + 12];
char params[sizeof(master_salt)*2];
uint8_t tmp_output[FFMAX(plain_len, cipher_len)];
int i, res;
struct SRTPContext ctx;
memset(&ctx, 0, sizeof(ctx));
memset(&params, 0, sizeof(params));
memset(&tmp_output, 0, sizeof(tmp_output));
/* master key */
for (i=0; i < 32; i++)
master_salt[i] = i;
/* append master salt */
strncpy((char*)&master_salt[32], "Quid pro quo", 12);
/* base64 encode master key|salt */
if (!av_base64_encode(params, sizeof(params), master_salt, sizeof(master_salt))) {
fprintf(stderr, "!! Base64 encoding failed\n");
return -1;
}
if (1) {
/* init crypto */
printf("[encryption]\n");
if (ff_srtp_set_crypto(&ctx, "SRTP_AEAD_AES_256_GCM", params) < 0) {
fprintf(stderr, "!! Initialisation failed\n");
return -1;
}
/* test encrypt */
res = ff_srtp_encrypt(&ctx, plain, plain_len, tmp_output, sizeof(tmp_output));
if (res < 0){
fprintf(stderr, "!! Encryption failed\n");
return -1;
}
printf("result:\n");
dump_memory(tmp_output, res);
printf("waited:\n");
dump_memory(cipher, cipher_len);
if (res != cipher_len) {
fprintf(stderr, "!! Encryption failed: lengths differ (get %d, waited %d)\n",
res, cipher_len);
return -1;
}
if (memcmp(tmp_output, cipher, cipher_len)) {
fprintf(stderr, "!! Encryption failed: wrong result\n");
return -1;
}
ff_srtp_free(&ctx);
}
/* Reset data for decryption */
memset(&ctx, 0, sizeof(ctx));
memset(&tmp_output, 0, sizeof(tmp_output));
/* init crypto */
printf("\n[decryption]\n");
if (ff_srtp_set_crypto(&ctx, "SRTP_AEAD_AES_256_GCM", params) < 0) {
fprintf(stderr, "!! Initialisation failed\n");
return -1;
}
/* test decrypt */
memcpy(tmp_output, cipher, cipher_len);
res = ff_srtp_decrypt(&ctx, tmp_output, &cipher_len);
if (res < 0) {
fprintf(stderr, "!! Decryption failed\n");
return -1;
}
printf("result:\n");
dump_memory(tmp_output, cipher_len);
printf("waited:\n");
dump_memory(plain, plain_len);
if (cipher_len != plain_len) {
fprintf(stderr, "!! Decryption failed: lengths differ (get %d, waited %d)\n",
cipher_len, plain_len);
return -1;
}
if (memcmp(tmp_output, plain, plain_len)) {
fprintf(stderr, "!! Decryption failed: wrong result\n");
return -1;
}
ff_srtp_free(&ctx);
return 0;
}
int main(int argc, char** argv)
{
/* Following test vectors are comming from RFC 7714, Ch. 16.2.x and 17.2.x
* Notes: only 256bits version is tested.
*/
uint8_t rtp_plain[] = {
0x80, 0x40, 0xf1, 0x7b,
0x80, 0x41, 0xf8, 0xd3,
0x55, 0x01, 0xa0, 0xb2,
0x47, 0x61, 0x6c, 0x6c,
0x69, 0x61, 0x20, 0x65,
0x73, 0x74, 0x20, 0x6f,
0x6d, 0x6e, 0x69, 0x73,
0x20, 0x64, 0x69, 0x76,
0x69, 0x73, 0x61, 0x20,
0x69, 0x6e, 0x20, 0x70,
0x61, 0x72, 0x74, 0x65,
0x73, 0x20, 0x74, 0x72,
0x65, 0x73
};
uint8_t rtp_cipher[] = {
0x80, 0x40, 0xf1, 0x7b,
0x80, 0x41, 0xf8, 0xd3,
0x55, 0x01, 0xa0, 0xb2,
0x32, 0xb1, 0xde, 0x78,
0xa8, 0x22, 0xfe, 0x12,
0xef, 0x9f, 0x78, 0xfa,
0x33, 0x2e, 0x33, 0xaa,
0xb1, 0x80, 0x12, 0x38,
0x9a, 0x58, 0xe2, 0xf3,
0xb5, 0x0b, 0x2a, 0x02,
0x76, 0xff, 0xae, 0x0f,
0x1b, 0xa6, 0x37, 0x99,
0xb8, 0x7b, 0x7a, 0xa3,
0xdb, 0x36, 0xdf, 0xff,
0xd6, 0xb0, 0xf9, 0xbb,
0x78, 0x78, 0xd7, 0xa7,
0x6c, 0x13
};
uint8_t rtcp_plain[] = {
0x81, 0xc8, 0x00, 0x0d,
0x4d, 0x61, 0x72, 0x73,
0x4e, 0x54, 0x50, 0x31,
0x4e, 0x54, 0x50, 0x32,
0x52, 0x54, 0x50, 0x20,
0x00, 0x00, 0x04, 0x2a,
0x00, 0x00, 0xe9, 0x30,
0x4c, 0x75, 0x6e, 0x61,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef
};
uint8_t rtcp_cipher[] = {
0x81, 0xc8, 0x00, 0x0d,
0x4d, 0x61, 0x72, 0x73,
0xd5, 0x0a, 0xe4, 0xd1,
0xf5, 0xce, 0x5d, 0x30,
0x4b, 0xa2, 0x97, 0xe4,
0x7d, 0x47, 0x0c, 0x28,
0x2c, 0x3e, 0xce, 0x5d,
0xbf, 0xfe, 0x0a, 0x50,
0xa2, 0xea, 0xa5, 0xc1,
0x11, 0x05, 0x55, 0xbe,
0x84, 0x15, 0xf6, 0x58,
0xc6, 0x1d, 0xe0, 0x47,
0x6f, 0x1b, 0x6f, 0xad,
0x1d, 0x1e, 0xb3, 0x0c,
0x44, 0x46, 0x83, 0x9f,
0x57, 0xff, 0x6f, 0x6c,
0xb2, 0x6a, 0xc3, 0xbe,
0x80, 0x00, 0x05, 0xd4
};
int res, failures = 0;
printf("**** Testing RTP packet ****\n");
res = test_gcm256(rtp_plain, sizeof(rtp_plain),
rtp_cipher, sizeof(rtp_cipher), 0);
if (res < 0) {
fprintf(stderr, "!!!! RTP test failed !!!!\n");
failures++;
}
printf("\n**** Testing RTCP packet ****\n");
res = test_gcm256(rtcp_plain, sizeof(rtcp_plain),
rtcp_cipher, sizeof(rtcp_cipher), 1);
if (res < 0) {
fprintf(stderr, "!!!! RTP test failed !!!!\n");
failures++;
}
if (failures) {
fprintf(stderr, "\n==== %u test(s) FAILED ====\n", failures);
return -1;
}
fprintf(stderr, "\n==== All tests PASSED ====\n");
return 0;
}
#endif
......@@ -23,6 +23,8 @@
#define SRTP_H
#include <stdint.h>
#include <gnutls/gnutls.h>
#include <nettle/gcm.h>
struct AVAES;
struct AVHMAC;
......@@ -31,15 +33,19 @@ struct SRTPContext {
struct AVAES *aes;
struct AVHMAC *hmac;
int rtp_hmac_size, rtcp_hmac_size;
uint8_t master_key[16];
uint8_t master_key[32];
uint8_t master_salt[14];
uint8_t rtp_key[16], rtcp_key[16];
uint8_t rtp_key[32], rtcp_key[32];
uint8_t rtp_salt[14], rtcp_salt[14];
uint8_t rtp_auth[20], rtcp_auth[20];
int seq_largest, seq_initialized;
uint32_t roc;
uint32_t rtcp_index;
int aead;
struct gcm_aes256_ctx aead_rtp_ctx;
struct gcm_aes256_ctx aead_rtcp_ctx;
};
int ff_srtp_set_crypto(struct SRTPContext *s, const char *suite,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment