Browse Source

implement srtp session key generation algorithm

git.mgm/mediaproxy-ng/github/master
Richard Fuchs 13 years ago
parent
commit
7877b7ce14
9 changed files with 285 additions and 22 deletions
  1. +1
    -1
      daemon/Makefile
  2. +44
    -15
      daemon/call.c
  3. +5
    -2
      daemon/call.h
  4. +104
    -0
      daemon/crypto.c
  5. +28
    -2
      daemon/crypto.h
  6. +62
    -0
      daemon/rtp.c
  7. +18
    -0
      daemon/rtp.h
  8. +17
    -2
      daemon/sdp.c
  9. +6
    -0
      daemon/str.h

+ 1
- 1
daemon/Makefile View File

@ -33,7 +33,7 @@ endif
SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \
bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \
crypto.c
crypto.c rtp.c
OBJS= $(SRCS:.c=.o)


+ 44
- 15
daemon/call.c View File

@ -28,6 +28,7 @@
#include "str.h"
#include "stun.h"
#include "rtcp.h"
#include "rtp.h"
@ -215,28 +216,58 @@ void kernelize(struct callstream *c) {
int __dummy_stream_handler(str *s) {
static int __dummy_stream_handler(str *s, struct streamrelay *r) {
abort();
return 0;
}
static int call_avpf2avp(str *s, struct streamrelay *r) {
return rtcp_avpf2avp(s);
}
static int call_avp2savp_rtp(str *s, struct streamrelay *r) {
return rtp_savp2avp(s, &r->peer.crypto);
}
static int call_avp2savp_rtcp(str *s, struct streamrelay *r) {
return 0;
}
static stream_handler determine_handler(struct streamrelay *in) {
if (in->peer.protocol == in->peer_advertised.protocol)
goto dummy;
if (in->peer.protocol == PROTO_UNKNOWN)
goto dummy;
if (in->peer_advertised.protocol == PROTO_UNKNOWN)
goto dummy;
if (in->peer.protocol == PROTO_RTP_AVPF && in->peer_advertised.protocol == PROTO_RTP_AVP) {
if (!in->rtcp)
switch (in->peer.protocol) {
case PROTO_UNKNOWN:
goto dummy;
return rtcp_avpf2avp;
}
if (in->peer.protocol == PROTO_RTP_AVP && in->peer_advertised.protocol == PROTO_RTP_AVPF)
goto dummy;
/* XXX warn? */
case PROTO_RTP_AVP:
switch (in->peer_advertised.protocol) {
case PROTO_RTP_AVPF:
goto dummy;
case PROTO_RTP_SAVP:
return in->rtcp ? call_avp2savp_rtcp
: call_avp2savp_rtp;
default:
abort();
}
case PROTO_RTP_AVPF:
switch (in->peer_advertised.protocol) {
case PROTO_RTP_AVP:
if (!in->rtcp)
goto dummy;
return call_avpf2avp;
default:
abort();
}
default:
abort();
}
dummy:
return __dummy_stream_handler;
@ -292,7 +323,7 @@ static int stream_packet(struct streamrelay *sr_incoming, str *s, struct sockadd
if (!sr_incoming->handler)
sr_incoming->handler = determine_handler(sr_incoming);
if (sr_incoming->handler != __dummy_stream_handler)
handler_ret = sr_incoming->handler(s);
handler_ret = sr_incoming->handler(s, sr_incoming);
use_cand:
if (p_incoming->confirmed || !p_incoming->filled || sr_incoming->idx != 0)
@ -1054,12 +1085,10 @@ static int setup_peer(struct peer *p, struct stream_input *s, const str *tag) {
unkernelize(&cs->peers[1]);
}
a->peer.ip46 = s->stream.ip46;
b->peer.ip46 = s->stream.ip46;
a->peer.port = b->peer.port = s->stream.port;
a->peer = s->stream;
b->peer = s->stream;
if (b->peer.port)
b->peer.port++;
a->peer.protocol = b->peer.protocol = s->stream.protocol;
a->peer_advertised = a->peer;
b->peer_advertised = b->peer;
a->rtcp = s->is_rtcp;


+ 5
- 2
daemon/call.h View File

@ -15,6 +15,7 @@
#include "aux.h"
#include "bencode.h"
#include "str.h"
#include "crypto.h"
struct poller;
struct control_stream;
@ -26,6 +27,7 @@ struct callstream;
struct call;
struct callmaster;
struct redis;
struct crypto_suite;
@ -69,6 +71,7 @@ struct stream {
u_int16_t port;
int num;
enum transport_protocol protocol;
struct crypto_context crypto;
};
struct stream_input {
struct stream stream;
@ -83,8 +86,8 @@ struct udp_fd {
u_int16_t localport;
};
typedef int (*stream_handler)(str *);
int __dummy_stream_handler(str *);
struct streamrelay;
typedef int (*stream_handler)(str *, struct streamrelay *);
struct streamrelay {
struct udp_fd fd;


+ 104
- 0
daemon/crypto.c View File

@ -1,6 +1,7 @@
#include "crypto.h"
#include <string.h>
#include <openssl/evp.h>
#include "str.h"
#include "aux.h"
@ -13,6 +14,8 @@ const struct crypto_suite crypto_suites[] = {
.name = "AES_CM_128_HMAC_SHA1_80",
.master_key_len = 128,
.master_salt_len = 112,
.session_key_len = 128,
.session_salt_len = 112,
.srtp_lifetime = 1ULL << 48,
.srtcp_lifetime = 1ULL << 31,
.cipher = CIPHER_AES_CM,
@ -27,6 +30,8 @@ const struct crypto_suite crypto_suites[] = {
.name = "AES_CM_128_HMAC_SHA1_32",
.master_key_len = 128,
.master_salt_len = 112,
.session_key_len = 128,
.session_salt_len = 112,
.srtp_lifetime = 1ULL << 48,
.srtcp_lifetime = 1ULL << 31,
.cipher = CIPHER_AES_CM,
@ -41,6 +46,8 @@ const struct crypto_suite crypto_suites[] = {
.name = "F8_128_HMAC_SHA1_80",
.master_key_len = 128,
.master_salt_len = 112,
.session_key_len = 128,
.session_salt_len = 112,
.srtp_lifetime = 1ULL << 48,
.srtcp_lifetime = 1ULL << 31,
.cipher = CIPHER_AES_F8,
@ -79,3 +86,100 @@ const struct crypto_suite *crypto_find_suite(const str *s) {
return NULL;
}
/* rfc 3711 section 4.1 and 4.1.1 */
static void aes_ctr_128(char *out, str *in, char *key, char *iv) {
EVP_CIPHER_CTX ecc;
unsigned char ivx[16];
unsigned char key_block[16];
unsigned char *p, *q;
unsigned int left;
int outlen, i;
memcpy(ivx, iv, 16);
p = (unsigned char *) in->s;
q = (unsigned char *) out;
left = in->len;
/* XXX do this only once per thread? */
EVP_CIPHER_CTX_init(&ecc);
EVP_EncryptInit_ex(&ecc, EVP_aes_128_ecb(), NULL, (unsigned char *) key, NULL);
while (left) {
EVP_EncryptUpdate(&ecc, key_block, &outlen, ivx, 16);
assert(outlen == 16);
for (i = 0; i < 16; i++) {
*q = *p ^ key_block[i];
q++;
p++;
left--;
if (!left)
goto done;
}
for (i = 15; i >= 0; i--) {
ivx[i]++;
if (G_LIKELY(ivx[i]))
break;
}
}
done:
EVP_EncryptFinal_ex(&ecc, key_block, &outlen);
/* assert(outlen == 0); */
EVP_CIPHER_CTX_cleanup(&ecc);
}
/* rfc 3711 section 4.3.1 and 4.3.3
* key: 128 bits
* x: 112 bits
* n <= 256
* out->len := n / 8 */
static void prf_n(str *out, char *key, char *x) {
char iv[16];
char o[32];
char in[32];
str in_s;
assert(sizeof(o) >= out->len);
ZERO(iv);
memcpy(iv, x, 14);
/* iv[14] = iv[15] = 0; := x << 16 */
ZERO(in); /* outputs the key stream */
str_init_len(&in_s, in, 16);
aes_ctr_128(o, &in_s, key, iv);
memcpy(out->s, o, out->len);
}
/* rfc 3711 section 4.3.1 */
int crypto_gen_session_key(struct crypto_context *c, str *out, unsigned char label) {
unsigned char key_id[7]; /* [ label, 48-bit ROC || SEQ ] */
unsigned char x[14];
int i;
if (!c->crypto_suite)
return -1;
ZERO(key_id);
/* key_id[1..6] := r
* key_derivation_rate == 0 --> r == 0 */
key_id[0] = label;
memcpy(x, c->master_salt, 14);
for (i = 7; i < 14; i++)
x[i] = key_id[i - 7] ^ x[i];
prf_n(out, c->master_key, (char *) x);
return 0;
}

+ 28
- 2
daemon/crypto.h View File

@ -3,6 +3,7 @@
#include <sys/types.h>
#include "str.h"
@ -28,10 +29,12 @@ struct crypto_suite {
unsigned int
master_key_len,
master_salt_len,
session_key_len, /* n_e */
session_salt_len, /* n_s */
encryption_key,
srtp_auth_tag,
srtp_auth_tag, /* n_a */
srtcp_auth_tag,
srtp_auth_key_len,
srtp_auth_key_len, /* n_a */
srtcp_auth_key_len;
unsigned long long int
srtp_lifetime,
@ -40,6 +43,28 @@ struct crypto_suite {
enum mac mac;
};
struct crypto_context {
const struct crypto_suite *crypto_suite;
/* we only support one master key for now */
char master_key[16];
char master_salt[14];
u_int64_t mki;
unsigned int mki_len;
/* from rfc 3711 */
u_int32_t roc;
u_int16_t s_l;
/* XXX replay list */
u_int64_t num_packets;
/* <from, to>? */
char session_key[16];
char session_salt[14];
char session_auth_key[20];
int have_session_key:1;
};
@ -49,6 +74,7 @@ extern const int num_crypto_suites;
const struct crypto_suite *crypto_find_suite(const str *);
int crypto_gen_session_key(struct crypto_context *, str *, unsigned char);


+ 62
- 0
daemon/rtp.c View File

@ -0,0 +1,62 @@
#include "rtp.h"
#include <sys/types.h>
#include "str.h"
#include "crypto.h"
struct rtp_header {
unsigned char v_p_x_cc;
unsigned char m_pt;
u_int16_t seq_num;
u_int32_t timestamp;
u_int32_t ssrc;
u_int32_t csrc[];
} __attribute__ ((packed));
static inline int check_session_key(struct crypto_context *c) {
str s;
if (c->have_session_key)
return 0;
str_init_len(&s, c->session_key, c->crypto_suite->session_key_len);
if (crypto_gen_session_key(c, &s, 0x00))
return -1;
str_init_len(&s, c->session_auth_key, c->crypto_suite->srtp_auth_key_len);
if (crypto_gen_session_key(c, &s, 0x01))
return -1;
str_init_len(&s, c->session_salt, c->crypto_suite->session_salt_len);
if (crypto_gen_session_key(c, &s, 0x02))
return -1;
c->have_session_key = 1;
return 0;
}
/* XXX some error handling/logging here */
int rtp_avp2savp(str *s, struct crypto_context *c) {
struct rtp_header *rtp;
if (s->len < sizeof(*rtp))
return -1;
rtp = (void *) s->s;
if (check_session_key(c))
return -1;
return 0;
}
int rtp_savp2avp(str *s, struct crypto_context *c) {
if (check_session_key(c))
return -1;
return 0;
}

+ 18
- 0
daemon/rtp.h View File

@ -0,0 +1,18 @@
#ifndef _RTP_H_
#define _RTP_H_
#include "str.h"
struct crypto_context;
int rtp_avp2savp(str *, struct crypto_context *);
int rtp_savp2avp(str *, struct crypto_context *);
#endif

+ 17
- 2
daemon/sdp.c View File

@ -97,7 +97,7 @@ struct attribute_crypto {
str master_key;
str salt;
char key_salt_buf[30];
unsigned long long lifetime;
u_int64_t lifetime;
unsigned int mki,
mki_len;
};
@ -306,6 +306,7 @@ static int parse_attribute_ssrc(struct sdp_attribute *output) {
return 0;
}
/* XXX error handling/logging */
static int parse_attribute_crypto(struct sdp_attribute *output) {
char *start, *end;
struct attribute_crypto *c;
@ -393,7 +394,7 @@ static int parse_attribute_crypto(struct sdp_attribute *output) {
return -1;
c->mki = strtoul(c->mki_str.s, NULL, 10);
c->mki_len = strtoul(s.s + 1, NULL, 10);
if (!c->mki || !c->mki_len)
if (!c->mki || !c->mki_len || c->mki_len > 128)
return -1;
}
@ -791,6 +792,20 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *streamhash,
si->is_rtcp = 1;
si->stream.protocol = tp;
id = ATTR_CRYPTO;
attr = g_hash_table_lookup(media->attributes.id_hash, &id);
if (attr) {
si->stream.crypto.crypto_suite = attr->u.crypto.crypto_suite;
si->stream.crypto.mki = attr->u.crypto.mki;
si->stream.crypto.mki_len = attr->u.crypto.mki_len;
assert(sizeof(si->stream.crypto.master_key) >= attr->u.crypto.master_key.len);
assert(sizeof(si->stream.crypto.master_salt) >= attr->u.crypto.salt.len);
memcpy(si->stream.crypto.master_key, attr->u.crypto.master_key.s, attr->u.crypto.master_key.len);
memcpy(si->stream.crypto.master_salt, attr->u.crypto.salt.s, attr->u.crypto.salt.len);
assert(sizeof(si->stream.crypto.session_key) >= attr->u.crypto.crypto_suite->session_key_len);
assert(sizeof(si->stream.crypto.session_salt) >= attr->u.crypto.crypto_suite->session_salt_len);
}
g_hash_table_insert(streamhash, si, si);
g_queue_push_tail(streams, si);
}


+ 6
- 0
daemon/str.h View File

@ -43,6 +43,8 @@ static inline int str_cmp_str(const str *a, const str *b);
static inline int str_cmp_str0(const str *a, const str *b);
/* inits a str object from a regular string */
static inline void str_init(str *out, char *s);
/* inits a str object from any binary string */
static inline void str_init_len(str *out, char *s, int len);
/* returns new str object allocated with malloc, including buffer */
static inline str *str_dup(const str *s);
/* returns new str object allocated from chunk, including buffer */
@ -131,6 +133,10 @@ static inline void str_init(str *out, char *s) {
out->s = s;
out->len = s ? strlen(s) : 0;
}
static inline void str_init_len(str *out, char *s, int len) {
out->s = s;
out->len = len;
}
static inline str *str_dup(const str *s) {
str *r;
r = malloc(sizeof(*r) + s->len + 1);


Loading…
Cancel
Save