]> git.sesse.net Git - vlc/blob - libs/srtp/srtp.c
RTP HMAC-SHA1 authentication
[vlc] / libs / srtp / srtp.c
1 /*
2  * Secure RTP with libgcrypt
3  * Copyright (C) 2007  RĂ©mi Denis-Courmont <rdenis # simphalempin , com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdint.h>
25 #include <stddef.h>
26
27 #include "srtp.h"
28
29 #include <stdbool.h>
30 #include <stdlib.h>
31 #include <assert.h>
32 #include <errno.h>
33
34 #include <gcrypt.h>
35
36 #define debug( ... ) (void)0
37
38 /* TODO:
39  * Useful stuff:
40  * - ROC profil thingy (multicast really needs this)
41  * - replay protection
42  *
43  * Requirements for conformance:
44  * - suites with NULL cipher
45  * - SRTCP
46  *
47  * Useless stuff (because nothing depends on it):
48  * - non-nul key derivation rate
49  * - MKI payload
50  */
51
52 typedef struct srtp_proto_t
53 {
54     gcry_cipher_hd_t cipher;
55     gcry_md_hd_t     mac;
56     uint32_t         salt[4];
57 } srtp_proto_t;
58
59 struct srtp_session_t
60 {
61     srtp_proto_t rtp;
62     srtp_proto_t rtcp;
63     unsigned flags;
64     unsigned kdr;
65     uint32_t rtcp_index;
66     uint32_t rtp_roc;
67     uint16_t rtp_seq;
68     uint8_t  tag_len;
69 };
70
71 enum
72 {
73     SRTP_CRYPT,
74     SRTP_AUTH,
75     SRTP_SALT,
76     SRTCP_CRYPT,
77     SRTCP_AUTH,
78     SRTCP_SALT
79 };
80
81 #ifdef WIN32
82 # include <winsock2.h>
83 #else
84 # include <netinet/in.h>
85 # include <pthread.h>
86 GCRY_THREAD_OPTION_PTHREAD_IMPL;
87 #endif
88
89 static bool libgcrypt_usable = false;
90
91 static void initonce_libgcrypt (void)
92 {
93     if ((gcry_check_version ("1.1.94") == NULL)
94      || gcry_control (GCRYCTL_DISABLE_SECMEM, 0)
95      || gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0)
96 #ifndef WIN32
97      || gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread)
98 #endif
99        )
100         return;
101
102     libgcrypt_usable = true;
103 }
104
105 static int init_libgcrypt (void)
106 {
107     int retval;
108 #ifndef WIN32
109     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
110     static pthread_once_t once = PTHREAD_ONCE_INIT;
111
112     pthread_mutex_lock (&mutex);
113     pthread_once (&once, initonce_libgcrypt);
114     retval = -libgcrypt_usable;
115     pthread_mutex_unlock (&mutex);
116 #else
117 # warning FIXME: This is not thread-safe.
118     if (!libgcrypt_usable)
119         initonce_libgcrypt ();
120     retval = -libgcrypt_usable;
121 #endif
122
123     return retval;
124
125 }
126
127
128 static void proto_destroy (srtp_proto_t *p)
129 {
130     gcry_md_close (p->mac);
131     gcry_cipher_close (p->cipher);
132 }
133
134
135 /**
136  * Releases all resources associated with a Secure RTP session.
137  */
138 void srtp_destroy (srtp_session_t *s)
139 {
140     assert (s != NULL);
141
142     proto_destroy (&s->rtcp);
143     proto_destroy (&s->rtp);
144     free (s);
145 }
146
147
148 static int proto_create (srtp_proto_t *p, int gcipher, int gmd)
149 {
150     if (gcry_cipher_open (&p->cipher, gcipher, GCRY_CIPHER_MODE_CTR, 0) == 0)
151     {
152         if (gcry_md_open (&p->mac, gmd, GCRY_MD_FLAG_HMAC) == 0)
153             return 0;
154         gcry_cipher_close (p->cipher);
155     }
156     return -1;
157 }
158
159
160 /**
161  * Allocates a Secure RTP session.
162  *
163  * @param name cipher-suite name
164  * @param kdr key derivation rate
165  * @param winsize anti-replay windows size (between 64 and 32767 inclusive)
166  *                0 disable replay attack protection (OK for send only)
167  * @param flags OR'ed optional flags.
168  *
169  * @return NULL in case of error
170  */
171 srtp_session_t *
172 srtp_create (const char *name, unsigned flags, unsigned kdr, uint16_t winsize)
173 {
174     assert (name != NULL);
175
176     if (kdr != 0)
177         return NULL; // FIXME: KDR not implemented yet
178     if (winsize != 0)
179         return NULL; // FIXME: replay protection not implemented yet
180
181     uint8_t tag_len;
182     int cipher = GCRY_CIPHER_AES, md = GCRY_MD_SHA1;
183
184     if (strcmp (name, "AES_CM_128_HMAC_SHA1_80") == 0)
185         tag_len = 10;
186     else
187     if (strcmp (name, "AES_CM_128_HMAC_SHA1_32") == 0)
188         tag_len = 4;
189     else
190     // F8_128_HMAC_SHA1_80 is not implemented
191         return NULL;
192
193     if ((flags & ~SRTP_FLAGS_MASK) || (winsize > 32767) || init_libgcrypt ())
194         return NULL;
195
196     srtp_session_t *s = malloc (sizeof (*s));
197     if (s == NULL)
198         return NULL;
199
200     memset (s, 0, sizeof (*s));
201     s->flags = flags;
202     s->kdr = kdr;
203     s->tag_len = tag_len;
204
205     if (proto_create (&s->rtp, cipher, md) == 0)
206     {
207         if (proto_create (&s->rtcp, cipher, md) == 0)
208             return s;
209         proto_destroy (&s->rtp);
210     }
211
212     free (s);
213     return NULL;
214 }
215
216
217 /**
218  * AES-CM key derivation (saltlen = 14 bytes)
219  */
220 static int
221 derive (gcry_cipher_hd_t prf, const void *salt,
222         const uint8_t *r, size_t rlen, uint8_t label,
223         void *out, size_t outlen)
224 {
225     uint8_t iv[16];
226
227     memcpy (iv, salt, 14);
228     iv[14] = iv[15] = 0;
229
230     assert (rlen < 14);
231     iv[13 - rlen] ^= label;
232     for (size_t i = 0; i < rlen; i++)
233         iv[sizeof (iv) - rlen + i] ^= r[i];
234
235     /* TODO: retry with CTR mode */
236     while (outlen >= sizeof (iv))
237     {
238         /* AES */
239         if (gcry_cipher_encrypt (prf, out, sizeof (iv), iv, sizeof (iv)))
240             return EINVAL;
241         outlen -= sizeof (iv);
242         out = ((uint8_t *)out) + sizeof (iv);
243
244         /* Increment IV in network byte order */
245         if (++iv[sizeof (iv) - 1] == 0)
246             ++iv[sizeof (iv) -2];
247     }
248
249     if (outlen > 0)
250     {
251         /* Truncated last AES output block */
252         if (gcry_cipher_encrypt (prf, iv, sizeof (iv), NULL, 0))
253             return -1;
254         memcpy (out, iv, outlen);
255     }
256
257     return 0;
258 }
259
260 #include <stdio.h>
261
262 static int
263 proto_derive (srtp_proto_t *p, gcry_cipher_hd_t prf,
264               const void *salt, size_t saltlen,
265               const uint8_t *r, size_t rlen, bool rtcp)
266 {
267     if (saltlen != 14)
268         return -1;
269
270     uint32_t cipherkey[4], authkey[5];
271     uint8_t label = rtcp ? SRTCP_CRYPT : SRTP_CRYPT;
272
273     if (derive (prf, salt, r, rlen, label++, cipherkey, 16)
274      || gcry_cipher_setkey (p->cipher, cipherkey, 16)
275      || derive (prf, salt, r, rlen, label++, authkey, 20)
276      || gcry_md_setkey (p->mac, authkey, 20)
277      || derive (prf, salt, r, rlen, label++, p->salt, 14))
278         return -1;
279
280     debug (" cipher key: %08x%08x%08x%08x\n auth key: %08x%08x%08x%08x%08x\n",
281             ntohl (cipherkey[0]), ntohl (cipherkey[1]), ntohl (cipherkey[2]),
282             ntohl (cipherkey[3]), ntohl (authkey[0]), ntohl (authkey[1]),
283             ntohl (authkey[2]), ntohl (authkey[3]), ntohl (authkey[4]));
284
285     return 0;
286 }
287
288
289 /**
290  * SRTP/SRTCP cipher/salt/MAC keys derivation.
291  */
292 static int
293 srtp_derive (srtp_session_t *s, const void *key, size_t keylen,
294              const void *salt, size_t saltlen)
295 {
296     gcry_cipher_hd_t prf;
297     uint8_t r[6];
298
299     /* TODO: retry with CTR mode */
300     if (gcry_cipher_open (&prf, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0)
301      || gcry_cipher_setkey (prf, key, keylen))
302         return -1;
303
304     /* RTP key derivation */
305     if (s->kdr != 0)
306     {
307         uint64_t index = (((uint64_t)s->rtp_roc) << 16) | s->rtp_seq;
308         index /= s->kdr;
309
310         for (int i = sizeof (r) - 1; i >= 0; i--)
311         {
312             r[i] = index & 0xff;
313             index = index >> 8;
314         }
315     }
316     else
317         memset (r, 0, sizeof (r));
318
319     if (proto_derive (&s->rtp, prf, salt, saltlen, r, 6, false))
320         return -1;
321
322     /* RTCP key derivation */
323     memcpy (r, &(uint32_t){ htonl (s->rtcp_index) }, 4);
324     if (proto_derive (&s->rtcp, prf, salt, saltlen, r, 4, true))
325         return -1;
326
327     (void)gcry_cipher_close (prf);
328     return 0;
329 }
330
331
332
333 /**
334  * Sets (or resets) the master key and master salt for a SRTP session.
335  * This must be done at least once before using rtp_send(), rtp_recv(),
336  * rtcp_send() or rtcp_recv(). Also, rekeying is required every
337  * 2^48 RTP packets or 2^31 RTCP packets (whichever comes first),
338  * otherwise the protocol security might be broken.
339  *
340  * @return 0 on success, in case of error:
341  *  EINVAL  invalid or unsupported key/salt sizes combination
342  */
343 int
344 srtp_setkey (srtp_session_t *s, const void *key, size_t keylen,
345              const void *salt, size_t saltlen)
346 {
347     return srtp_derive (s, key, keylen, salt, saltlen) ? EINVAL : 0;
348 }
349
350
351 /** AES-CM encryption/decryption (ctr length = 16 bytes) */
352 static int
353 encrypt (gcry_cipher_hd_t hd, uint32_t *ctr, uint8_t *data, size_t len)
354 {
355     const size_t ctrlen = 16;
356     while (len >= ctrlen)
357     {
358         if (gcry_cipher_setctr (hd, ctr, ctrlen)
359          || gcry_cipher_encrypt (hd, data, ctrlen, NULL, 0))
360             return -1;
361
362         data += ctrlen;
363         len -= ctrlen;
364         ctr[3] = htonl (ntohl (ctr[3]) + 1);
365     }
366
367     if (len > 0)
368     {
369         /* Truncated last block */
370         uint8_t dummy[ctrlen];
371         memcpy (dummy, data, len);
372         memset (dummy + len, 0, ctrlen - len);
373
374         if (gcry_cipher_setctr (hd, ctr, ctrlen)
375          || gcry_cipher_encrypt (hd, dummy, ctrlen, data, ctrlen))
376             return -1;
377         memcpy (data, dummy, len);
378     }
379
380     return 0;
381 }
382
383
384 /** AES-CM for RTP (salt = 14 bytes + 2 nul bytes) */
385 static inline int
386 rtp_encrypt (gcry_cipher_hd_t hd, uint32_t ssrc, uint32_t roc, uint16_t seq,
387              const uint32_t *salt, uint8_t *data, size_t len)
388 {
389     /* Determines cryptographic counter (IV) */
390     uint32_t counter[4];
391     counter[0] = salt[0];
392     counter[1] = salt[1] ^ ssrc;
393     counter[2] = salt[2] ^ htonl (roc);
394     counter[3] = salt[3] ^ htonl (seq << 16);
395
396     /* Encryption */
397     return encrypt (hd, counter, data, len);
398 }
399
400
401 /** Message Authentication and Integrity for RTP */
402 static const uint8_t *
403 rtp_digest (gcry_md_hd_t md, const void *data, size_t len, uint32_t roc)
404 {
405     gcry_md_reset (md);
406     gcry_md_write (md, data, len);
407     gcry_md_write (md, &(uint32_t){ htonl (roc) }, 4);
408     return gcry_md_read (md, 0);
409 }
410
411
412 /**
413  * Encrypts/decrypts a RTP packet and updates SRTP context
414  * (CTR block cypher mode of operation has identical encryption and
415  * decryption function).
416  *
417  * @param buf RTP packet to be encrypted/digested
418  * @param len RTP packet length
419  *
420  * @return 0 on success, in case of error:
421  *  EINVAL  malformatted RTP packet
422  */
423 static int srtp_crypt (srtp_session_t *s, uint8_t *buf, size_t len)
424 {
425     assert (s != NULL);
426
427     if ((len < 12) || ((buf[0] >> 6) != 2))
428         return EINVAL;
429
430     /* Computes encryption offset */
431     uint16_t offset = 12;
432     offset += (buf[0] & 0xf) * 4; // skips CSRC
433
434     if (buf[0] & 0x10)
435     {
436         uint16_t extlen;
437
438         offset += 4;
439         if (len < offset)
440             return EINVAL;
441
442         memcpy (&extlen, buf + offset - 2, 2);
443         offset += htons (extlen); // skips RTP extension header
444     }
445
446     if (len < offset)
447         return EINVAL;
448
449     /* Determines RTP 48-bits counter and SSRC */
450     uint32_t ssrc;
451     memcpy (&ssrc, buf + 8, 4);
452
453     uint16_t seq = (buf[2] << 8) | buf[3];
454     if (((seq - s->rtp_seq) & 0xffff) < 32768)
455     {
456         if (seq < s->rtp_seq)
457             s->rtp_roc++; /* Sequence number wrap */
458     }
459     else
460     {
461         if (seq > s->rtp_seq)
462             s->rtp_roc--;
463     }
464
465     s->rtp_seq = seq;
466
467     if (s->flags & SRTP_UNENCRYPTED)
468         return 0;
469
470     if (rtp_encrypt (s->rtp.cipher, ssrc, s->rtp_roc, seq, s->rtp.salt,
471                      buf + offset, len - offset))
472         return EINVAL;
473
474     return 0;
475 }
476
477
478 /**
479  * Turns a RTP packet into a SRTP packet: encrypt it, then computes
480  * the authentication tag and appends it.
481  * Note that you can encrypt packet in disorder.
482  *
483  * @param buf RTP packet to be encrypted/digested
484  * @param lenp pointer to the RTP packet length on entry,
485  *             set to the SRTP length on exit (undefined in case of error)
486  * @param bufsize size (bytes) of the packet buffer
487  *
488  * @return 0 on success, in case of error:
489  *  EINVAL  malformatted RTP packet or internal error
490  *  ENOSPC  bufsize is too small (to add authentication tag)
491  */
492 int
493 srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize)
494 {
495     size_t len = *lenp;
496     int val = srtp_crypt (s, buf, len);
497     if (val)
498         return val;
499
500     if (s->flags & SRTP_UNAUTHENTICATED)
501         return 0;
502
503     if (bufsize < (len + s->tag_len))
504         return ENOSPC;
505
506     const uint8_t *tag = rtp_digest (s->rtp.mac, buf, len, s->rtp_roc);
507     memcpy (buf + len, tag, s->tag_len);
508     *lenp = len + s->tag_len;
509
510     return 0;
511 }
512
513
514 /**
515  * Turns a SRTP packet into a RTP packet: authenticates the packet,
516  * then decrypts it.
517  *
518  * @param buf RTP packet to be digested/decrypted
519  * @param lenp pointer to the RTP packet length on entry,
520  *             set to the SRTP length on exit (undefined in case of error)
521  *
522  * @return 0 on success, in case of error:
523  *  EINVAL  malformatted SRTP packet
524  *  EACCES  authentication failed (spoofed packet or out-of-sync)
525  */
526 int
527 srtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp)
528 {
529     size_t len = *lenp;
530
531     if (!(s->flags & SRTP_UNAUTHENTICATED))
532     {
533         if (len < s->tag_len)
534             return EINVAL;
535         len -= s->tag_len;
536         *lenp = len;
537
538         const uint8_t *tag = rtp_digest (s->rtp.mac, buf, len, s->rtp_roc);
539         debug  (" Auth tag: %08x%08x%04x (wanted)\n"
540                 " Auth tag: %08x%08x%04x (recv'd)\n",
541                 ntohl (((uint32_t *)tag)[0]), ntohl (((uint32_t *)tag)[1]),
542                 ntohs (((uint16_t *)tag)[4]),
543                 ntohl (((uint32_t *)w)[0]), ntohl (((uint32_t *)w)[1]),
544                 ntohs (((uint16_t *)w)[4]));
545         if (memcmp (buf + len, tag, s->tag_len))
546             return EACCES;
547     }
548
549     /* FIXME: anti-replay */
550
551     return srtp_crypt (s, buf, len);
552 }
553