added support for SRTP
RFC 3711 -- Secure Real-time Transport Protocol (SRTP)
This commit is contained in:
parent
edd8b3c06e
commit
edf3cf5430
12 changed files with 918 additions and 1 deletions
2
Makefile
2
Makefile
|
@ -29,7 +29,7 @@ MODULES += list mbuf hash
|
|||
MODULES += fmt tmr main mem dbg sys lock mqueue
|
||||
MODULES += mod conf
|
||||
MODULES += bfcp
|
||||
MODULES += aes
|
||||
MODULES += aes srtp
|
||||
|
||||
INSTALL := install
|
||||
ifeq ($(DESTDIR),)
|
||||
|
|
|
@ -52,6 +52,7 @@ Modules:
|
|||
* sipevent unstable SIP Event framework
|
||||
* sipreg testing SIP register client
|
||||
* sipsess unstable SIP Sessions
|
||||
* srtp unstable Secure Real-time Transport Protocol (SRTP)
|
||||
* stun unstable Session Traversal Utilities for NAT (STUN)
|
||||
* sys testing System information
|
||||
* tcp testing TCP transport
|
||||
|
@ -93,6 +94,7 @@ Features:
|
|||
* RFC 3556 - SDP Bandwidth Modifiers for RTCP Bandwidth
|
||||
* RFC 3581 - An Extension to SIP for Symmetric Response Routing
|
||||
* RFC 3605 - RTCP attribute in SDP
|
||||
* RFC 3711 - The Secure Real-time Transport Protocol (SRTP)
|
||||
* RFC 3969 - The IANA URI Parameter Registry for SIP
|
||||
* RFC 3994 - Indication of Message Composition for Instant Messaging
|
||||
* RFC 4346 - The TLS Protocol Version 1.1
|
||||
|
@ -112,6 +114,7 @@ Features:
|
|||
* RFC 5780 - NAT Behaviour Discovery Using STUN
|
||||
* RFC 6026 - Correct Transaction Handling for 2xx Resp. to SIP INVITE Requests
|
||||
* RFC 6156 - TURN Extension for IPv6
|
||||
* RFC 6188 - The Use of AES-192 and AES-256 in Secure RTP
|
||||
* RFC 6455 - The WebSocket Protocol
|
||||
* Symmetric RTP
|
||||
* ITU-T G.711 Appendix I and Appendix II
|
||||
|
|
|
@ -48,6 +48,7 @@ extern "C" {
|
|||
#include "re_sipsess.h"
|
||||
#include "re_stun.h"
|
||||
#include "re_natbd.h"
|
||||
#include "re_srtp.h"
|
||||
#include "re_sys.h"
|
||||
#include "re_tcp.h"
|
||||
#include "re_telev.h"
|
||||
|
|
26
include/re_srtp.h
Normal file
26
include/re_srtp.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @file re_srtp.h Secure Real-time Transport Protocol (SRTP)
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
|
||||
|
||||
enum srtp_suite {
|
||||
SRTP_AES_CM_128_HMAC_SHA1_32,
|
||||
SRTP_AES_CM_128_HMAC_SHA1_80,
|
||||
SRTP_AES_256_CM_HMAC_SHA1_32,
|
||||
SRTP_AES_256_CM_HMAC_SHA1_80,
|
||||
};
|
||||
|
||||
enum srtp_flags {
|
||||
SRTP_UNENCRYPTED_SRTCP = 1<<1,
|
||||
};
|
||||
|
||||
struct srtp;
|
||||
|
||||
int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,
|
||||
const uint8_t *key, size_t key_bytes, int flags);
|
||||
int srtp_encrypt(struct srtp *srtp, struct mbuf *mb);
|
||||
int srtp_decrypt(struct srtp *srtp, struct mbuf *mb);
|
||||
int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb);
|
||||
int srtcp_decrypt(struct srtp *srtp, struct mbuf *mb);
|
42
src/srtp/README
Normal file
42
src/srtp/README
Normal file
|
@ -0,0 +1,42 @@
|
|||
SRTP module
|
||||
-----------
|
||||
|
||||
The SRTP module implements Secure RTP as defined in RFC 3711.
|
||||
It provides a clean and user friendly API and can be used
|
||||
as a standalone module.
|
||||
|
||||
|
||||
|
||||
|
||||
Requirements and features:
|
||||
|
||||
RFC 3711 yes
|
||||
RFC 6188 yes
|
||||
Multiple Master keys: no
|
||||
Key derivation rate: 0 (zero)
|
||||
Salting keys: yes
|
||||
SRTP protection: yes
|
||||
SRTCP protection: yes
|
||||
Replay protection: yes
|
||||
Encryption: yes
|
||||
Authentication: yes
|
||||
MKI (Master Key Identifier): no
|
||||
Authentication tag length: 32-bit and 80-bit
|
||||
ROC (Roll Over Counter): yes
|
||||
Master key lifetime: no
|
||||
Multiple SSRCs: yes
|
||||
Performance: better than libsrtp
|
||||
|
||||
Cryptographic transforms:
|
||||
- AES in Counter mode: yes
|
||||
- AES in f8-mode: no
|
||||
- NULL Cipher: no
|
||||
|
||||
Authentication transform:
|
||||
- HMAC-SHA1: yes
|
||||
- NULL auth: no
|
||||
|
||||
master key lengths:
|
||||
- 128 bits yes
|
||||
- 192 bits no
|
||||
- 256 bits yes
|
99
src/srtp/misc.c
Normal file
99
src/srtp/misc.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* @file srtp/misc.c SRTP functions
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <re_types.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_list.h>
|
||||
#include <re_aes.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_srtp.h>
|
||||
#include "srtp.h"
|
||||
|
||||
|
||||
/*
|
||||
* Appendix A: Pseudocode for Index Determination
|
||||
*
|
||||
* In the following, signed arithmetic is assumed.
|
||||
*/
|
||||
uint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq)
|
||||
{
|
||||
int v;
|
||||
|
||||
if (s_l < 32768) {
|
||||
|
||||
if ((int)seq - (int)s_l > 32768)
|
||||
v = (roc-1) & 0xffffffffu;
|
||||
else
|
||||
v = roc;
|
||||
}
|
||||
else {
|
||||
if ((int)s_l - 32768 > seq)
|
||||
v = (roc+1) & 0xffffffffu;
|
||||
else
|
||||
v = roc;
|
||||
}
|
||||
|
||||
return seq + v*65536;
|
||||
}
|
||||
|
||||
|
||||
int srtp_derive(uint8_t *out, size_t out_len, uint8_t label,
|
||||
const uint8_t *master_key, size_t key_bytes,
|
||||
const uint8_t *master_salt, size_t salt_bytes)
|
||||
{
|
||||
uint8_t x[AES_BLOCK_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
static const uint8_t null[AES_BLOCK_SIZE * 2];
|
||||
struct aes *aes;
|
||||
int err;
|
||||
|
||||
if (!out || !master_key || !master_salt)
|
||||
return EINVAL;
|
||||
|
||||
if (out_len > sizeof(null) || salt_bytes > sizeof(x))
|
||||
return EINVAL;
|
||||
|
||||
memcpy(x, master_salt, salt_bytes);
|
||||
x[7] ^= label;
|
||||
|
||||
err = aes_alloc(&aes, AES_MODE_CTR, master_key, key_bytes*8, x);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = aes_encr(aes, out, null, out_len);
|
||||
|
||||
mem_deref(aes);
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void srtp_iv_calc(union vect128 *iv, const union vect128 *k_s,
|
||||
uint32_t ssrc, uint64_t ix)
|
||||
{
|
||||
if (!iv || !k_s)
|
||||
return;
|
||||
|
||||
iv->u32[0] = k_s->u32[0];
|
||||
iv->u32[1] = k_s->u32[1] ^ htonl(ssrc);
|
||||
iv->u32[2] = k_s->u32[2] ^ htonl((uint32_t)(ix>>16));
|
||||
iv->u16[6] = k_s->u16[6] ^ htons((uint16_t)ix);
|
||||
iv->u16[7] = 0;
|
||||
}
|
||||
|
||||
|
||||
const char *srtp_suite_name(enum srtp_suite suite)
|
||||
{
|
||||
switch (suite) {
|
||||
|
||||
case SRTP_AES_CM_128_HMAC_SHA1_32: return "AES_CM_128_HMAC_SHA1_32";
|
||||
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";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
11
src/srtp/mod.mk
Normal file
11
src/srtp/mod.mk
Normal file
|
@ -0,0 +1,11 @@
|
|||
#
|
||||
# mod.mk
|
||||
#
|
||||
# Copyright (C) 2010 Creytiv.com
|
||||
#
|
||||
|
||||
SRCS += srtp/misc.c
|
||||
SRCS += srtp/replay.c
|
||||
SRCS += srtp/srtcp.c
|
||||
SRCS += srtp/srtp.c
|
||||
SRCS += srtp/stream.c
|
63
src/srtp/replay.c
Normal file
63
src/srtp/replay.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @file srtp/replay.c SRTP replay protection
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <re_types.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_list.h>
|
||||
#include <re_srtp.h>
|
||||
#include "srtp.h"
|
||||
|
||||
|
||||
enum {
|
||||
SRTP_WINDOW_SIZE = 64
|
||||
};
|
||||
|
||||
|
||||
void srtp_replay_init(struct replay *replay)
|
||||
{
|
||||
if (!replay)
|
||||
return;
|
||||
|
||||
replay->bitmap = 0;
|
||||
replay->lix = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns false if packet disallowed, true if packet permitted
|
||||
*/
|
||||
bool srtp_replay_check(struct replay *replay, uint64_t ix)
|
||||
{
|
||||
uint64_t diff;
|
||||
|
||||
if (!replay)
|
||||
return false;
|
||||
|
||||
if (ix > replay->lix) {
|
||||
diff = ix - replay->lix;
|
||||
|
||||
if (diff < SRTP_WINDOW_SIZE) { /* In window */
|
||||
replay->bitmap <<= diff;
|
||||
replay->bitmap |= 1; /* set bit for this packet */
|
||||
}
|
||||
else
|
||||
replay->bitmap = 1;
|
||||
|
||||
replay->lix = ix;
|
||||
return true;
|
||||
}
|
||||
|
||||
diff = replay->lix - ix;
|
||||
if (diff >= SRTP_WINDOW_SIZE)
|
||||
return false;
|
||||
|
||||
if (replay->bitmap & (1ULL << diff))
|
||||
return false; /* already seen */
|
||||
|
||||
/* mark as seen */
|
||||
replay->bitmap |= (1ULL << diff);
|
||||
|
||||
return true;
|
||||
}
|
184
src/srtp/srtcp.c
Normal file
184
src/srtp/srtcp.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
/**
|
||||
* @file srtcp.c Secure Real-time Transport Control Protocol (SRTCP)
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <re_types.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_list.h>
|
||||
#include <re_hmac.h>
|
||||
#include <re_sha.h>
|
||||
#include <re_aes.h>
|
||||
#include <re_net.h>
|
||||
#include <re_srtp.h>
|
||||
#include "srtp.h"
|
||||
|
||||
|
||||
static int get_rtcp_ssrc(uint32_t *ssrc, struct mbuf *mb)
|
||||
{
|
||||
if (mbuf_get_left(mb) < 8)
|
||||
return EBADMSG;
|
||||
|
||||
mbuf_advance(mb, 4);
|
||||
*ssrc = ntohl(mbuf_read_u32(mb));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb)
|
||||
{
|
||||
struct srtp_stream *strm;
|
||||
struct comp *rtcp;
|
||||
uint32_t ssrc;
|
||||
size_t start;
|
||||
int ep = 0;
|
||||
int err;
|
||||
|
||||
if (!srtp || !mb)
|
||||
return EINVAL;
|
||||
|
||||
rtcp = &srtp->rtcp;
|
||||
start = mb->pos;
|
||||
|
||||
err = get_rtcp_ssrc(&ssrc, mb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
strm = stream_get(srtp, ssrc);
|
||||
if (!strm)
|
||||
return ENOMEM;
|
||||
|
||||
strm->rtcp_index = (strm->rtcp_index+1) & 0x7fffffff;
|
||||
|
||||
if (rtcp->aes) {
|
||||
union vect128 iv;
|
||||
uint8_t *p = mbuf_buf(mb);
|
||||
|
||||
srtp_iv_calc(&iv, &rtcp->k_s, ssrc, strm->rtcp_index);
|
||||
|
||||
aes_set_iv(rtcp->aes, iv.u8);
|
||||
err = aes_encr(rtcp->aes, p, p, mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ep = 1;
|
||||
}
|
||||
|
||||
/* append E-bit and SRTCP-index */
|
||||
mb->pos = mb->end;
|
||||
err = mbuf_write_u32(mb, htonl(ep<<31 | strm->rtcp_index));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (rtcp->hmac) {
|
||||
uint8_t tag[SHA_DIGEST_LENGTH];
|
||||
|
||||
mb->pos = start;
|
||||
|
||||
err = hmac_digest(rtcp->hmac, tag, sizeof(tag),
|
||||
mbuf_buf(mb), mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mb->pos = mb->end;
|
||||
|
||||
err = mbuf_write_mem(mb, tag, rtcp->tag_len);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
mb->pos = start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int srtcp_decrypt(struct srtp *srtp, struct mbuf *mb)
|
||||
{
|
||||
size_t start, eix_start, pld_start;
|
||||
struct srtp_stream *strm;
|
||||
struct comp *rtcp;
|
||||
uint32_t v, ix;
|
||||
uint32_t ssrc;
|
||||
bool ep;
|
||||
int err;
|
||||
|
||||
if (!srtp || !mb)
|
||||
return EINVAL;
|
||||
|
||||
rtcp = &srtp->rtcp;
|
||||
start = mb->pos;
|
||||
|
||||
err = get_rtcp_ssrc(&ssrc, mb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
strm = stream_get(srtp, ssrc);
|
||||
if (!strm)
|
||||
return ENOMEM;
|
||||
|
||||
pld_start = mb->pos;
|
||||
|
||||
if (mbuf_get_left(mb) < (4 + rtcp->tag_len))
|
||||
return EBADMSG;
|
||||
|
||||
/* Read out E-Bit, SRTCP-index and Authentication Tag */
|
||||
eix_start = mb->end - (4 + rtcp->tag_len);
|
||||
mb->pos = eix_start;
|
||||
v = ntohl(mbuf_read_u32(mb));
|
||||
|
||||
ep = (v >> 31) & 1;
|
||||
ix = v & 0x7fffffff;
|
||||
|
||||
if (rtcp->hmac) {
|
||||
uint8_t tag[SHA_DIGEST_LENGTH], tag_pkt[SHA_DIGEST_LENGTH];
|
||||
const size_t tag_start = mb->pos;
|
||||
|
||||
err = mbuf_read_mem(mb, tag_pkt, rtcp->tag_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mb->pos = start;
|
||||
mb->end = tag_start;
|
||||
|
||||
err = hmac_digest(rtcp->hmac, tag, sizeof(tag),
|
||||
mbuf_buf(mb), mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (0 != memcmp(tag, tag_pkt, rtcp->tag_len))
|
||||
return EAUTH;
|
||||
|
||||
/*
|
||||
* SRTCP replay protection is as defined in Section 3.3.2,
|
||||
* but using the SRTCP index as the index i and a separate
|
||||
* Replay List that is specific to SRTCP.
|
||||
*/
|
||||
if (!srtp_replay_check(&strm->replay_rtcp, ix))
|
||||
return EALREADY;
|
||||
}
|
||||
|
||||
mb->end = eix_start;
|
||||
|
||||
if (rtcp->aes && ep) {
|
||||
union vect128 iv;
|
||||
uint8_t *p;
|
||||
|
||||
mb->pos = pld_start;
|
||||
p = mbuf_buf(mb);
|
||||
|
||||
srtp_iv_calc(&iv, &rtcp->k_s, ssrc, ix);
|
||||
|
||||
aes_set_iv(rtcp->aes, iv.u8);
|
||||
err = aes_decr(rtcp->aes, p, p, mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
mb->pos = start;
|
||||
|
||||
return 0;
|
||||
}
|
324
src/srtp/srtp.c
Normal file
324
src/srtp/srtp.c
Normal file
|
@ -0,0 +1,324 @@
|
|||
/**
|
||||
* @file srtp.c Secure Real-time Transport Protocol (SRTP)
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <re_types.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_list.h>
|
||||
#include <re_hmac.h>
|
||||
#include <re_sha.h>
|
||||
#include <re_aes.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_rtp.h>
|
||||
#include <re_srtp.h>
|
||||
#include "srtp.h"
|
||||
|
||||
|
||||
enum {
|
||||
MAX_KEYLEN = 32, /**< Maximum keylength in bytes */
|
||||
};
|
||||
|
||||
|
||||
static inline int seq_diff(uint16_t x, uint16_t y)
|
||||
{
|
||||
return (int)y - (int)x;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t k_e[MAX_KEYLEN], k_a[SHA_DIGEST_LENGTH];
|
||||
int err = 0;
|
||||
|
||||
if (key_b > sizeof(k_e))
|
||||
return EINVAL;
|
||||
|
||||
if (tag_len > SHA_DIGEST_LENGTH)
|
||||
return EINVAL;
|
||||
|
||||
c->tag_len = tag_len;
|
||||
|
||||
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);
|
||||
err |= srtp_derive(c->k_s.u8, 14, 0x02+offs, key, key_b, s, s_b);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (encrypted) {
|
||||
err = aes_alloc(&c->aes, AES_MODE_CTR, 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;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void destructor(void *arg)
|
||||
{
|
||||
struct srtp *srtp = arg;
|
||||
|
||||
mem_deref(srtp->rtp.aes);
|
||||
mem_deref(srtp->rtcp.aes);
|
||||
mem_deref(srtp->rtp.hmac);
|
||||
mem_deref(srtp->rtcp.hmac);
|
||||
|
||||
list_flush(&srtp->streaml);
|
||||
}
|
||||
|
||||
|
||||
int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,
|
||||
const uint8_t *key, size_t key_bytes, int flags)
|
||||
{
|
||||
struct srtp *srtp;
|
||||
const uint8_t *master_salt;
|
||||
size_t cipher_bytes, auth_bytes;
|
||||
int err = 0;
|
||||
|
||||
if (!srtpp || !key)
|
||||
return EINVAL;
|
||||
|
||||
switch (suite) {
|
||||
|
||||
case SRTP_AES_CM_128_HMAC_SHA1_80:
|
||||
cipher_bytes = 16;
|
||||
auth_bytes = 10;
|
||||
break;
|
||||
|
||||
case SRTP_AES_CM_128_HMAC_SHA1_32:
|
||||
cipher_bytes = 16;
|
||||
auth_bytes = 4;
|
||||
break;
|
||||
|
||||
case SRTP_AES_256_CM_HMAC_SHA1_80:
|
||||
cipher_bytes = 32;
|
||||
auth_bytes = 10;
|
||||
break;
|
||||
|
||||
case SRTP_AES_256_CM_HMAC_SHA1_32:
|
||||
cipher_bytes = 32;
|
||||
auth_bytes = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ENOTSUP;
|
||||
};
|
||||
|
||||
if ((cipher_bytes + SRTP_SALT_SIZE) != key_bytes)
|
||||
return EINVAL;
|
||||
|
||||
master_salt = &key[cipher_bytes];
|
||||
|
||||
srtp = mem_zalloc(sizeof(*srtp), destructor);
|
||||
if (!srtp)
|
||||
return ENOMEM;
|
||||
|
||||
err |= comp_init(&srtp->rtp, 0, key, cipher_bytes,
|
||||
master_salt, SRTP_SALT_SIZE, auth_bytes, true);
|
||||
err |= comp_init(&srtp->rtcp, 3, key, cipher_bytes,
|
||||
master_salt, SRTP_SALT_SIZE, auth_bytes,
|
||||
!(flags & SRTP_UNENCRYPTED_SRTCP));
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(srtp);
|
||||
else
|
||||
*srtpp = srtp;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int srtp_encrypt(struct srtp *srtp, struct mbuf *mb)
|
||||
{
|
||||
struct srtp_stream *strm;
|
||||
struct rtp_header hdr;
|
||||
struct comp *comp;
|
||||
size_t start;
|
||||
uint64_t ix;
|
||||
int err;
|
||||
|
||||
if (!srtp || !mb)
|
||||
return EINVAL;
|
||||
|
||||
comp = &srtp->rtp;
|
||||
|
||||
start = mb->pos;
|
||||
|
||||
err = rtp_hdr_decode(&hdr, mb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
strm = stream_get_seq(srtp, hdr.ssrc, hdr.seq);
|
||||
if (!strm)
|
||||
return ENOMEM;
|
||||
|
||||
/* Roll-Over Counter (ROC) */
|
||||
if (seq_diff(strm->s_l, hdr.seq) <= -32768) {
|
||||
strm->roc++;
|
||||
strm->s_l = 0;
|
||||
}
|
||||
|
||||
ix = 65536ULL * strm->roc + hdr.seq;
|
||||
|
||||
if (comp->aes) {
|
||||
union vect128 iv;
|
||||
uint8_t *p = mbuf_buf(mb);
|
||||
|
||||
srtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix);
|
||||
|
||||
aes_set_iv(comp->aes, iv.u8);
|
||||
err = aes_encr(comp->aes, p, p, mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (comp->hmac) {
|
||||
const size_t tag_start = mb->end;
|
||||
uint8_t tag[SHA_DIGEST_LENGTH];
|
||||
|
||||
mb->pos = tag_start;
|
||||
|
||||
err = mbuf_write_u32(mb, htonl(strm->roc));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mb->pos = start;
|
||||
|
||||
err = hmac_digest(comp->hmac, tag, sizeof(tag),
|
||||
mbuf_buf(mb), mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mb->pos = mb->end = tag_start;
|
||||
|
||||
err = mbuf_write_mem(mb, tag, comp->tag_len);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (hdr.seq > strm->s_l)
|
||||
strm->s_l = hdr.seq;
|
||||
|
||||
mb->pos = start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int srtp_decrypt(struct srtp *srtp, struct mbuf *mb)
|
||||
{
|
||||
struct srtp_stream *strm;
|
||||
struct rtp_header hdr;
|
||||
struct comp *comp;
|
||||
uint64_t ix;
|
||||
size_t start;
|
||||
int diff;
|
||||
int err;
|
||||
|
||||
if (!srtp || !mb)
|
||||
return EINVAL;
|
||||
|
||||
comp = &srtp->rtp;
|
||||
|
||||
start = mb->pos;
|
||||
|
||||
err = rtp_hdr_decode(&hdr, mb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
strm = stream_get_seq(srtp, hdr.ssrc, hdr.seq);
|
||||
if (!strm)
|
||||
return ENOMEM;
|
||||
|
||||
diff = seq_diff(strm->s_l, hdr.seq);
|
||||
if (diff > 32768)
|
||||
return ETIMEDOUT;
|
||||
|
||||
/* Roll-Over Counter (ROC) */
|
||||
if (diff <= -32768) {
|
||||
strm->roc++;
|
||||
strm->s_l = 0;
|
||||
}
|
||||
|
||||
ix = srtp_get_index(strm->roc, strm->s_l, hdr.seq);
|
||||
|
||||
if (comp->hmac) {
|
||||
uint8_t tag_calc[SHA_DIGEST_LENGTH];
|
||||
uint8_t tag_pkt[SHA_DIGEST_LENGTH];
|
||||
size_t pld_start, tag_start;
|
||||
|
||||
if (mbuf_get_left(mb) < comp->tag_len)
|
||||
return EBADMSG;
|
||||
|
||||
pld_start = mb->pos;
|
||||
tag_start = mb->end - comp->tag_len;
|
||||
|
||||
mb->pos = tag_start;
|
||||
|
||||
err = mbuf_read_mem(mb, tag_pkt, comp->tag_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mb->pos = mb->end = tag_start;
|
||||
|
||||
err = mbuf_write_u32(mb, htonl(strm->roc));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mb->pos = start;
|
||||
|
||||
err = hmac_digest(comp->hmac, tag_calc, sizeof(tag_calc),
|
||||
mbuf_buf(mb), mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mb->pos = pld_start;
|
||||
mb->end = tag_start;
|
||||
|
||||
if (0 != memcmp(tag_calc, tag_pkt, comp->tag_len))
|
||||
return EAUTH;
|
||||
|
||||
/*
|
||||
* 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 (comp->aes) {
|
||||
|
||||
union vect128 iv;
|
||||
uint8_t *p = mbuf_buf(mb);
|
||||
|
||||
srtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix);
|
||||
|
||||
aes_set_iv(comp->aes, iv.u8);
|
||||
err = aes_decr(comp->aes, p, p, mbuf_get_left(mb));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (hdr.seq > strm->s_l)
|
||||
strm->s_l = hdr.seq;
|
||||
|
||||
mb->pos = start;
|
||||
|
||||
return 0;
|
||||
}
|
69
src/srtp/srtp.h
Normal file
69
src/srtp/srtp.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* @file srtp.h Secure Real-time Transport Protocol (SRTP) -- internal
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
|
||||
|
||||
enum {
|
||||
SRTP_SALT_SIZE = 14
|
||||
};
|
||||
|
||||
|
||||
/** Defines a 128-bit vector in network order */
|
||||
union vect128 {
|
||||
uint64_t u64[ 2];
|
||||
uint32_t u32[ 4];
|
||||
uint16_t u16[ 8];
|
||||
uint8_t u8[16];
|
||||
};
|
||||
|
||||
/** Replay protection */
|
||||
struct replay {
|
||||
uint64_t bitmap; /**< Session state - must be 64 bits */
|
||||
uint64_t lix; /**< Last received index */
|
||||
};
|
||||
|
||||
/** SRTP stream/context -- shared state between RTP/RTCP */
|
||||
struct srtp_stream {
|
||||
struct le le; /**< Linked-list element */
|
||||
struct replay replay_rtp; /**< recv -- replay protection for RTP */
|
||||
struct replay replay_rtcp; /**< recv -- replay protection for RTCP */
|
||||
uint32_t ssrc; /**< SSRC -- lookup key */
|
||||
uint32_t roc; /**< send/recv Roll-Over Counter (ROC) */
|
||||
uint16_t s_l; /**< send/recv -- highest SEQ number */
|
||||
bool s_l_set; /**< True if s_l has been set */
|
||||
uint32_t rtcp_index; /**< RTCP-index for sending (31-bits) */
|
||||
};
|
||||
|
||||
/** SRTP Session */
|
||||
struct srtp {
|
||||
struct comp {
|
||||
struct aes *aes; /**< AES Context */
|
||||
struct hmac *hmac; /**< HMAC Context */
|
||||
union vect128 k_s; /**< Derived salting key (14 bytes) */
|
||||
size_t tag_len; /**< Authentication tag length [bytes] */
|
||||
} rtp, rtcp;
|
||||
|
||||
struct list streaml; /**< SRTP-streams (struct srtp_stream) */
|
||||
};
|
||||
|
||||
|
||||
struct srtp_stream *stream_get(struct srtp *srtp, uint32_t ssrc);
|
||||
struct srtp_stream *stream_get_seq(struct srtp *srtp, uint32_t ssrc,
|
||||
uint16_t seq);
|
||||
|
||||
|
||||
int srtp_derive(uint8_t *out, size_t out_len, uint8_t label,
|
||||
const uint8_t *master_key, size_t key_bytes,
|
||||
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);
|
||||
uint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq);
|
||||
const char *srtp_suite_name(enum srtp_suite suite);
|
||||
|
||||
|
||||
/* Replay protection */
|
||||
|
||||
void srtp_replay_init(struct replay *replay);
|
||||
bool srtp_replay_check(struct replay *replay, uint64_t ix);
|
95
src/srtp/stream.c
Normal file
95
src/srtp/stream.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @file srtp/stream.c Secure Real-time Transport Protocol (SRTP) -- stream
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <re_types.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_list.h>
|
||||
#include <re_srtp.h>
|
||||
#include "srtp.h"
|
||||
|
||||
|
||||
enum {
|
||||
MAX_STREAMS = 8, /**< Maximum number of SRTP streams */
|
||||
};
|
||||
|
||||
|
||||
static void stream_destructor(void *arg)
|
||||
{
|
||||
struct srtp_stream *strm = arg;
|
||||
|
||||
list_unlink(&strm->le);
|
||||
}
|
||||
|
||||
|
||||
static struct srtp_stream *stream_find(struct srtp *srtp, uint32_t ssrc)
|
||||
{
|
||||
struct le *le;
|
||||
|
||||
for (le = srtp->streaml.head; le; le = le->next) {
|
||||
|
||||
struct srtp_stream *strm = le->data;
|
||||
|
||||
if (strm->ssrc == ssrc)
|
||||
return strm;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct srtp_stream *stream_new(struct srtp *srtp, uint32_t ssrc)
|
||||
{
|
||||
struct srtp_stream *strm;
|
||||
|
||||
if (list_count(&srtp->streaml) >= MAX_STREAMS)
|
||||
return NULL;
|
||||
|
||||
strm = mem_zalloc(sizeof(*strm), stream_destructor);
|
||||
if (!strm)
|
||||
return NULL;
|
||||
|
||||
strm->ssrc = ssrc;
|
||||
srtp_replay_init(&strm->replay_rtp);
|
||||
srtp_replay_init(&strm->replay_rtcp);
|
||||
|
||||
list_append(&srtp->streaml, &strm->le, strm);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
|
||||
struct srtp_stream *stream_get(struct srtp *srtp, uint32_t ssrc)
|
||||
{
|
||||
struct srtp_stream *strm;
|
||||
|
||||
if (!srtp)
|
||||
return NULL;
|
||||
|
||||
strm = stream_find(srtp, ssrc);
|
||||
if (strm)
|
||||
return strm;
|
||||
|
||||
return stream_new(srtp, ssrc);
|
||||
}
|
||||
|
||||
|
||||
struct srtp_stream *stream_get_seq(struct srtp *srtp, uint32_t ssrc,
|
||||
uint16_t seq)
|
||||
{
|
||||
struct srtp_stream *strm;
|
||||
|
||||
strm = stream_get(srtp, ssrc);
|
||||
if (!strm)
|
||||
return NULL;
|
||||
|
||||
/* Set the initial sequence number once only */
|
||||
if (!strm->s_l_set) {
|
||||
strm->s_l = seq;
|
||||
strm->s_l_set = true;
|
||||
}
|
||||
|
||||
return strm;
|
||||
}
|
Loading…
Add table
Reference in a new issue