diff --git a/daemon/call.c b/daemon/call.c index d1265e3fe..75f511642 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -229,6 +229,12 @@ static int call_avp2savp_rtp(str *s, struct streamrelay *r) { static int call_avp2savp_rtcp(str *s, struct streamrelay *r) { return 0; } +static int call_savp2avp_rtp(str *s, struct streamrelay *r) { + return rtp_savp2avp(s, &r->peer.crypto.in); +} +static int call_savp2avp_rtcp(str *s, struct streamrelay *r) { + return 0; +} static stream_handler determine_handler(struct streamrelay *in) { @@ -254,6 +260,17 @@ static stream_handler determine_handler(struct streamrelay *in) { abort(); } + case PROTO_RTP_SAVP: + switch (in->peer_advertised.protocol) { + case PROTO_RTP_AVP: + case PROTO_RTP_AVPF: + return in->rtcp ? call_savp2avp_rtcp + : call_savp2avp_rtp; + + default: + abort(); + } + case PROTO_RTP_AVPF: switch (in->peer_advertised.protocol) { case PROTO_RTP_AVP: diff --git a/daemon/crypto.c b/daemon/crypto.c index 7b06ca39d..54b257657 100644 --- a/daemon/crypto.c +++ b/daemon/crypto.c @@ -11,7 +11,7 @@ static int aes_cm_encrypt_rtp(struct crypto_context *, struct rtp_header *, str *, u_int64_t); -static int hmac_sha1_rtp(struct crypto_context *, char *out, str *in); +static int hmac_sha1_rtp(struct crypto_context *, char *out, str *in, u_int64_t); /* all lengths are in bits, some code assumes everything to be multiples of 8 */ const struct crypto_suite crypto_suites[] = { @@ -31,6 +31,7 @@ const struct crypto_suite crypto_suites[] = { .srtp_auth_key_len = 160, .srtcp_auth_key_len = 160, .encrypt_rtp = aes_cm_encrypt_rtp, + .decrypt_rtp = aes_cm_encrypt_rtp, .hash_rtp = hmac_sha1_rtp, }, { @@ -49,6 +50,7 @@ const struct crypto_suite crypto_suites[] = { .srtp_auth_key_len = 160, .srtcp_auth_key_len = 160, .encrypt_rtp = aes_cm_encrypt_rtp, + .decrypt_rtp = aes_cm_encrypt_rtp, .hash_rtp = hmac_sha1_rtp, }, { @@ -216,19 +218,20 @@ static int aes_cm_encrypt_rtp(struct crypto_context *c, struct rtp_header *r, st } /* rfc 3711, sections 4.2 and 4.2.1 */ -static int hmac_sha1_rtp(struct crypto_context *c, char *out, str *in) { +static int hmac_sha1_rtp(struct crypto_context *c, char *out, str *in, u_int64_t index) { unsigned char hmac[20]; HMAC_CTX hc; u_int32_t roc; - HMAC_Init(&hc, c->session_auth_key, c->crypto_suite->srtp_auth_key_len, EVP_sha1()); + HMAC_Init(&hc, c->session_auth_key, c->crypto_suite->srtp_auth_key_len / 8, EVP_sha1()); HMAC_Update(&hc, (unsigned char *) in->s, in->len); - roc = htonl(c->roc); + roc = htonl((index & 0xffffffff0000ULL) >> 16); HMAC_Update(&hc, (unsigned char *) &roc, sizeof(roc)); HMAC_Final(&hc, hmac, NULL); HMAC_CTX_cleanup(&hc); - memcpy(out, hmac, c->crypto_suite->srtp_auth_tag); + assert(sizeof(hmac) >= c->crypto_suite->srtp_auth_tag / 8); + memcpy(out, hmac, c->crypto_suite->srtp_auth_tag / 8); return 0; } diff --git a/daemon/crypto.h b/daemon/crypto.h index 051379631..6ab9cd833 100644 --- a/daemon/crypto.h +++ b/daemon/crypto.h @@ -28,7 +28,7 @@ struct crypto_context; struct rtp_header; typedef int (*crypto_func)(struct crypto_context *, struct rtp_header *, str *, u_int64_t); -typedef int (*hash_func)(struct crypto_context *, char *out, str *in); +typedef int (*hash_func)(struct crypto_context *, char *out, str *in, u_int64_t); struct crypto_suite { const char *name; @@ -47,7 +47,8 @@ struct crypto_suite { srtcp_lifetime; enum cipher cipher; enum mac mac; - crypto_func encrypt_rtp; + crypto_func encrypt_rtp, + decrypt_rtp; hash_func hash_rtp; }; @@ -61,7 +62,7 @@ struct crypto_context { /* from rfc 3711 */ u_int32_t roc; - u_int16_t s_l; + u_int64_t s_l; /* XXX replay list */ u_int64_t num_packets; /* ? */ @@ -97,6 +98,14 @@ static inline int crypto_encrypt_rtp(struct crypto_context *c, struct rtp_header return c->crypto_suite->encrypt_rtp(c, rtp, payload, index); } +static inline int crypto_decrypt_rtp(struct crypto_context *c, struct rtp_header *rtp, + str *payload, u_int64_t index) +{ + if (!c || !c->crypto_suite) + return -1; + + return c->crypto_suite->decrypt_rtp(c, rtp, payload, index); +} diff --git a/daemon/rtp.c b/daemon/rtp.c index e9a649e18..0d7e08115 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -32,42 +32,41 @@ static inline int check_session_key(struct crypto_context *c) { return 0; } -/* XXX some error handling/logging here */ -int rtp_avp2savp(str *s, struct crypto_context *c) { +static int rtp_payload(str *p, str *s) { struct rtp_header *rtp; - str payload, to_auth; struct rtp_extension *ext; - u_int16_t seq; - u_int64_t index, s_l_index; - long long int diff; - char *pl_end; - u_int32_t mki_part; if (s->len < sizeof(*rtp)) return -1; - if (check_session_key(c)) - return -1; rtp = (void *) s->s; if ((rtp->v_p_x_cc & 0xc0) != 0x80) /* version 2 */ return -1; - payload = *s; + *p = *s; /* fixed header */ - str_shift(&payload, sizeof(*rtp)); + str_shift(p, sizeof(*rtp)); /* csrc list */ - if (str_shift(&payload, (rtp->v_p_x_cc & 0xf) * 4)) + if (str_shift(p, (rtp->v_p_x_cc & 0xf) * 4)) return -1; if ((rtp->v_p_x_cc & 0x10)) { /* extension */ - if (payload.len < sizeof(*ext)) + if (p->len < sizeof(*ext)) return -1; - ext = (void *) payload.s; - if (str_shift(&payload, 4 + ntohs(ext->length) * 4)) + ext = (void *) p->s; + if (str_shift(p, 4 + ntohs(ext->length) * 4)) return -1; } + return 0; +} + +static u_int64_t packet_index(struct crypto_context *c, struct rtp_header *rtp) { + u_int16_t seq; + u_int64_t index; + long long int diff; + seq = ntohs(rtp->seq_num); /* rfc 3711 section 3.3.1 */ if (G_UNLIKELY(!c->s_l)) @@ -75,11 +74,10 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { /* rfc 3711 appendix A, modified, and sections 3.3 and 3.3.1 */ index = ((u_int64_t) c->roc << 16) | seq; - s_l_index = ((u_int64_t) c->roc << 16) | c->s_l; - diff = index - s_l_index; + diff = index - c->s_l; if (diff >= 0) { if (diff < 0x8000) - ; + c->s_l = index; else if (index >= 0x10000) index -= 0x10000; } @@ -89,9 +87,30 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { else { index += 0x10000; c->roc++; + c->s_l = index; } } + return index; +} + +/* rfc 3711, section 3.3 */ +/* XXX some error handling/logging here */ +int rtp_avp2savp(str *s, struct crypto_context *c) { + struct rtp_header *rtp; + str payload, to_auth; + u_int64_t index; + char *pl_end; + u_int32_t mki_part; + + if (rtp_payload(&payload, s)) + return -1; + if (check_session_key(c)) + return -1; + + rtp = (void *) s->s; + index = packet_index(c, rtp); + /* rfc 3711 section 3.1 */ if (crypto_encrypt_rtp(c, rtp, &payload, index)) @@ -123,8 +142,8 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { } if (c->crypto_suite->srtp_auth_tag) { - c->crypto_suite->hash_rtp(c, pl_end, &to_auth); - pl_end += c->crypto_suite->srtp_auth_tag; + c->crypto_suite->hash_rtp(c, pl_end, &to_auth, index); + pl_end += c->crypto_suite->srtp_auth_tag / 8; } s->len = pl_end - s->s; @@ -132,8 +151,59 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { return 0; } +/* rfc 3711, section 3.3 */ int rtp_savp2avp(str *s, struct crypto_context *c) { + struct rtp_header *rtp; + u_int64_t index; + str payload, mki, to_auth; + char hmac[20], *auth_tag = NULL; + int i; + + if (rtp_payload(&payload, s)) + return -1; if (check_session_key(c)) return -1; + + rtp = (void *) s->s; + index = packet_index(c, rtp); + + /* rfc 3711 section 3.1 */ + + to_auth = *s; + + if (c->crypto_suite->srtp_auth_tag) { + i = c->crypto_suite->srtp_auth_tag / 8; + + assert(sizeof(hmac) >= i); + if (payload.len < i) + return -1; + + auth_tag = payload.s + payload.len - i; + payload.len -= i; + to_auth.len -= i; + } + + if (c->mki_len) { + if (payload.len < c->mki_len) + return -1; + + str_init_len(&mki, payload.s - c->mki_len, c->mki_len); + payload.len -= c->mki_len; + to_auth.len -= c->mki_len; + + /* ignoring the mki for now */ + } + + if (c->crypto_suite->srtp_auth_tag) { + c->crypto_suite->hash_rtp(c, hmac, &to_auth, index); + if (memcmp(hmac, auth_tag, c->crypto_suite->srtp_auth_tag / 8)) + return -1; + } + + if (crypto_decrypt_rtp(c, rtp, &payload, index)) + return -1; + + *s = to_auth; + return 0; }