From eb7428c1d38b3b70af04184f4f0562a50db12931 Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9mi=20Denis-Courmont?= Date: Sat, 10 Mar 2007 16:28:20 +0000 Subject: [PATCH] RTP HMAC-SHA1 authentication --- libs/srtp/srtp.c | 86 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/libs/srtp/srtp.c b/libs/srtp/srtp.c index bb6fd5ebf4..2adef546f1 100644 --- a/libs/srtp/srtp.c +++ b/libs/srtp/srtp.c @@ -33,6 +33,8 @@ #include +#define debug( ... ) (void)0 + /* TODO: * Useful stuff: * - ROC profil thingy (multicast really needs this) @@ -52,7 +54,6 @@ typedef struct srtp_proto_t gcry_cipher_hd_t cipher; gcry_md_hd_t mac; uint32_t salt[4]; - uint8_t mac_len; } srtp_proto_t; struct srtp_session_t @@ -64,6 +65,7 @@ struct srtp_session_t uint32_t rtcp_index; uint32_t rtp_roc; uint16_t rtp_seq; + uint8_t tag_len; }; enum @@ -176,14 +178,14 @@ srtp_create (const char *name, unsigned flags, unsigned kdr, uint16_t winsize) if (winsize != 0) 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; if (strcmp (name, "AES_CM_128_HMAC_SHA1_80") == 0) - mac_len = 80; + tag_len = 10; else if (strcmp (name, "AES_CM_128_HMAC_SHA1_32") == 0) - mac_len = 32; + tag_len = 4; else // F8_128_HMAC_SHA1_80 is not implemented return NULL; @@ -198,6 +200,7 @@ srtp_create (const char *name, unsigned flags, unsigned kdr, uint16_t winsize) memset (s, 0, sizeof (*s)); s->flags = flags; s->kdr = kdr; + s->tag_len = tag_len; if (proto_create (&s->rtp, cipher, md) == 0) { @@ -254,6 +257,7 @@ derive (gcry_cipher_hd_t prf, const void *salt, return 0; } +#include static int 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) return -1; - uint8_t cipherkey[16]; + uint32_t cipherkey[4], authkey[5]; uint8_t label = rtcp ? SRTCP_CRYPT : SRTP_CRYPT; if (derive (prf, salt, r, rlen, label++, 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)) 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; } @@ -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 * (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, * @return 0 on success, in case of error: * 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); @@ -419,7 +440,7 @@ static int srtp_encrypt (srtp_session_t *s, uint8_t *buf, size_t len) return EINVAL; memcpy (&extlen, buf + offset - 2, 2); - offset += htons (extlen); + offset += htons (extlen); // skips RTP extension header } if (len < offset) @@ -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 * * @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) */ int srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize) { size_t len = *lenp; - int val = srtp_encrypt (s, buf, len); + int val = srtp_crypt (s, buf, len); if (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; - /* 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; } /** - * Turns a RTP packet into a SRTP packet: encrypt it, then computes - * the authentication tag and appends it. - * Note that you can encrypt packet in disorder. + * Turns a SRTP packet into a RTP packet: authenticates the packet, + * then decrypts it. * - * @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, * set to the SRTP length on exit (undefined 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) */ int srtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp) { size_t len = *lenp; - int val = srtp_encrypt (s, buf, len); - if (val) - return val; - /* FIXME: HMAC and anti-replay */ - return 0; + if (!(s->flags & SRTP_UNAUTHENTICATED)) + { + 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); } -- 2.39.2