Commit eb7428c1 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

RTP HMAC-SHA1 authentication

parent fb8aac67
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <gcrypt.h> #include <gcrypt.h>
#define debug( ... ) (void)0
/* TODO: /* TODO:
* Useful stuff: * Useful stuff:
* - ROC profil thingy (multicast really needs this) * - ROC profil thingy (multicast really needs this)
...@@ -52,7 +54,6 @@ typedef struct srtp_proto_t ...@@ -52,7 +54,6 @@ typedef struct srtp_proto_t
gcry_cipher_hd_t cipher; gcry_cipher_hd_t cipher;
gcry_md_hd_t mac; gcry_md_hd_t mac;
uint32_t salt[4]; uint32_t salt[4];
uint8_t mac_len;
} srtp_proto_t; } srtp_proto_t;
struct srtp_session_t struct srtp_session_t
...@@ -64,6 +65,7 @@ struct srtp_session_t ...@@ -64,6 +65,7 @@ struct srtp_session_t
uint32_t rtcp_index; uint32_t rtcp_index;
uint32_t rtp_roc; uint32_t rtp_roc;
uint16_t rtp_seq; uint16_t rtp_seq;
uint8_t tag_len;
}; };
enum enum
...@@ -176,14 +178,14 @@ srtp_create (const char *name, unsigned flags, unsigned kdr, uint16_t winsize) ...@@ -176,14 +178,14 @@ srtp_create (const char *name, unsigned flags, unsigned kdr, uint16_t winsize)
if (winsize != 0) if (winsize != 0)
return NULL; // FIXME: replay protection not implemented yet return NULL; // FIXME: replay protection not implemented yet
uint8_t mac_len; uint8_t tag_len;
int cipher = GCRY_CIPHER_AES, md = GCRY_MD_SHA1; int cipher = GCRY_CIPHER_AES, md = GCRY_MD_SHA1;
if (strcmp (name, "AES_CM_128_HMAC_SHA1_80") == 0) if (strcmp (name, "AES_CM_128_HMAC_SHA1_80") == 0)
mac_len = 80; tag_len = 10;
else else
if (strcmp (name, "AES_CM_128_HMAC_SHA1_32") == 0) if (strcmp (name, "AES_CM_128_HMAC_SHA1_32") == 0)
mac_len = 32; tag_len = 4;
else else
// F8_128_HMAC_SHA1_80 is not implemented // F8_128_HMAC_SHA1_80 is not implemented
return NULL; return NULL;
...@@ -198,6 +200,7 @@ srtp_create (const char *name, unsigned flags, unsigned kdr, uint16_t winsize) ...@@ -198,6 +200,7 @@ srtp_create (const char *name, unsigned flags, unsigned kdr, uint16_t winsize)
memset (s, 0, sizeof (*s)); memset (s, 0, sizeof (*s));
s->flags = flags; s->flags = flags;
s->kdr = kdr; s->kdr = kdr;
s->tag_len = tag_len;
if (proto_create (&s->rtp, cipher, md) == 0) if (proto_create (&s->rtp, cipher, md) == 0)
{ {
...@@ -254,6 +257,7 @@ derive (gcry_cipher_hd_t prf, const void *salt, ...@@ -254,6 +257,7 @@ derive (gcry_cipher_hd_t prf, const void *salt,
return 0; return 0;
} }
#include <stdio.h>
static int static int
proto_derive (srtp_proto_t *p, gcry_cipher_hd_t prf, proto_derive (srtp_proto_t *p, gcry_cipher_hd_t prf,
...@@ -263,15 +267,21 @@ proto_derive (srtp_proto_t *p, gcry_cipher_hd_t prf, ...@@ -263,15 +267,21 @@ proto_derive (srtp_proto_t *p, gcry_cipher_hd_t prf,
if (saltlen != 14) if (saltlen != 14)
return -1; return -1;
uint8_t cipherkey[16]; uint32_t cipherkey[4], authkey[5];
uint8_t label = rtcp ? SRTCP_CRYPT : SRTP_CRYPT; uint8_t label = rtcp ? SRTCP_CRYPT : SRTP_CRYPT;
if (derive (prf, salt, r, rlen, label++, cipherkey, 16) if (derive (prf, salt, r, rlen, label++, cipherkey, 16)
|| gcry_cipher_setkey (p->cipher, cipherkey, 16) || gcry_cipher_setkey (p->cipher, cipherkey, 16)
|| derive (prf, salt, r, rlen, label++, NULL, 0) /* FIXME HMAC */ || derive (prf, salt, r, rlen, label++, authkey, 20)
|| gcry_md_setkey (p->mac, authkey, 20)
|| derive (prf, salt, r, rlen, label++, p->salt, 14)) || derive (prf, salt, r, rlen, label++, p->salt, 14))
return -1; return -1;
debug (" cipher key: %08x%08x%08x%08x\n auth key: %08x%08x%08x%08x%08x\n",
ntohl (cipherkey[0]), ntohl (cipherkey[1]), ntohl (cipherkey[2]),
ntohl (cipherkey[3]), ntohl (authkey[0]), ntohl (authkey[1]),
ntohl (authkey[2]), ntohl (authkey[3]), ntohl (authkey[4]));
return 0; return 0;
} }
...@@ -388,6 +398,17 @@ rtp_encrypt (gcry_cipher_hd_t hd, uint32_t ssrc, uint32_t roc, uint16_t seq, ...@@ -388,6 +398,17 @@ rtp_encrypt (gcry_cipher_hd_t hd, uint32_t ssrc, uint32_t roc, uint16_t seq,
} }
/** Message Authentication and Integrity for RTP */
static const uint8_t *
rtp_digest (gcry_md_hd_t md, const void *data, size_t len, uint32_t roc)
{
gcry_md_reset (md);
gcry_md_write (md, data, len);
gcry_md_write (md, &(uint32_t){ htonl (roc) }, 4);
return gcry_md_read (md, 0);
}
/** /**
* Encrypts/decrypts a RTP packet and updates SRTP context * Encrypts/decrypts a RTP packet and updates SRTP context
* (CTR block cypher mode of operation has identical encryption and * (CTR block cypher mode of operation has identical encryption and
...@@ -399,7 +420,7 @@ rtp_encrypt (gcry_cipher_hd_t hd, uint32_t ssrc, uint32_t roc, uint16_t seq, ...@@ -399,7 +420,7 @@ rtp_encrypt (gcry_cipher_hd_t hd, uint32_t ssrc, uint32_t roc, uint16_t seq,
* @return 0 on success, in case of error: * @return 0 on success, in case of error:
* EINVAL malformatted RTP packet * EINVAL malformatted RTP packet
*/ */
static int srtp_encrypt (srtp_session_t *s, uint8_t *buf, size_t len) static int srtp_crypt (srtp_session_t *s, uint8_t *buf, size_t len)
{ {
assert (s != NULL); assert (s != NULL);
...@@ -419,7 +440,7 @@ static int srtp_encrypt (srtp_session_t *s, uint8_t *buf, size_t len) ...@@ -419,7 +440,7 @@ static int srtp_encrypt (srtp_session_t *s, uint8_t *buf, size_t len)
return EINVAL; return EINVAL;
memcpy (&extlen, buf + offset - 2, 2); memcpy (&extlen, buf + offset - 2, 2);
offset += htons (extlen); offset += htons (extlen); // skips RTP extension header
} }
if (len < offset) if (len < offset)
...@@ -465,47 +486,68 @@ static int srtp_encrypt (srtp_session_t *s, uint8_t *buf, size_t len) ...@@ -465,47 +486,68 @@ static int srtp_encrypt (srtp_session_t *s, uint8_t *buf, size_t len)
* @param bufsize size (bytes) of the packet buffer * @param bufsize size (bytes) of the packet buffer
* *
* @return 0 on success, in case of error: * @return 0 on success, in case of error:
* EINVAL malformatted RTP packet * EINVAL malformatted RTP packet or internal error
* ENOSPC bufsize is too small (to add authentication tag) * ENOSPC bufsize is too small (to add authentication tag)
*/ */
int int
srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize) srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize)
{ {
size_t len = *lenp; size_t len = *lenp;
int val = srtp_encrypt (s, buf, len); int val = srtp_crypt (s, buf, len);
if (val) if (val)
return val; return val;
if (bufsize < (len + s->rtp.mac_len)) if (s->flags & SRTP_UNAUTHENTICATED)
return 0;
if (bufsize < (len + s->tag_len))
return ENOSPC; return ENOSPC;
/* FIXME: HMAC and anti-replay */ const uint8_t *tag = rtp_digest (s->rtp.mac, buf, len, s->rtp_roc);
memcpy (buf + len, tag, s->tag_len);
*lenp = len + s->tag_len;
return 0; return 0;
} }
/** /**
* Turns a RTP packet into a SRTP packet: encrypt it, then computes * Turns a SRTP packet into a RTP packet: authenticates the packet,
* the authentication tag and appends it. * then decrypts it.
* Note that you can encrypt packet in disorder.
* *
* @param buf RTP packet to be decrypted/digested * @param buf RTP packet to be digested/decrypted
* @param lenp pointer to the RTP packet length on entry, * @param lenp pointer to the RTP packet length on entry,
* set to the SRTP length on exit (undefined in case of error) * set to the SRTP length on exit (undefined in case of error)
* *
* @return 0 on success, in case of error: * @return 0 on success, in case of error:
* EINVAL malformatted RTP packet * EINVAL malformatted SRTP packet
* EACCES authentication failed (spoofed packet or out-of-sync) * EACCES authentication failed (spoofed packet or out-of-sync)
*/ */
int int
srtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp) srtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp)
{ {
size_t len = *lenp; size_t len = *lenp;
int val = srtp_encrypt (s, buf, len);
if (val)
return val;
/* FIXME: HMAC and anti-replay */ if (!(s->flags & SRTP_UNAUTHENTICATED))
return 0; {
if (len < s->tag_len)
return EINVAL;
len -= s->tag_len;
*lenp = len;
const uint8_t *tag = rtp_digest (s->rtp.mac, buf, len, s->rtp_roc);
debug (" Auth tag: %08x%08x%04x (wanted)\n"
" Auth tag: %08x%08x%04x (recv'd)\n",
ntohl (((uint32_t *)tag)[0]), ntohl (((uint32_t *)tag)[1]),
ntohs (((uint16_t *)tag)[4]),
ntohl (((uint32_t *)w)[0]), ntohl (((uint32_t *)w)[1]),
ntohs (((uint16_t *)w)[4]));
if (memcmp (buf + len, tag, s->tag_len))
return EACCES;
}
/* FIXME: anti-replay */
return srtp_crypt (s, buf, len);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment