diff --git a/daemon/call.c b/daemon/call.c index 75f511642..4bc8c557c 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -224,16 +224,16 @@ 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.out); + return rtp_avp2savp(s, &r->peer.crypto.out); } static int call_avp2savp_rtcp(str *s, struct streamrelay *r) { - return 0; + return rtcp_avp2savp(s, &r->peer.crypto.out); } 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; + return rtcp_savp2avp(s, &r->peer.crypto.in); } diff --git a/daemon/crypto.c b/daemon/crypto.c index 54b257657..12092ea85 100644 --- a/daemon/crypto.c +++ b/daemon/crypto.c @@ -7,11 +7,14 @@ #include "str.h" #include "aux.h" #include "rtp.h" +#include "rtcp.h" static int aes_cm_encrypt_rtp(struct crypto_context *, struct rtp_header *, str *, u_int64_t); +static int aes_cm_encrypt_rtcp(struct crypto_context *, struct rtcp_packet *, str *, u_int64_t); static int hmac_sha1_rtp(struct crypto_context *, char *out, str *in, u_int64_t); +static int hmac_sha1_rtcp(struct crypto_context *, char *out, str *in); /* all lengths are in bits, some code assumes everything to be multiples of 8 */ const struct crypto_suite crypto_suites[] = { @@ -32,7 +35,10 @@ const struct crypto_suite crypto_suites[] = { .srtcp_auth_key_len = 160, .encrypt_rtp = aes_cm_encrypt_rtp, .decrypt_rtp = aes_cm_encrypt_rtp, + .encrypt_rtcp = aes_cm_encrypt_rtcp, + .decrypt_rtcp = aes_cm_encrypt_rtcp, .hash_rtp = hmac_sha1_rtp, + .hash_rtcp = hmac_sha1_rtcp, }, { .name = "AES_CM_128_HMAC_SHA1_32", @@ -51,7 +57,10 @@ const struct crypto_suite crypto_suites[] = { .srtcp_auth_key_len = 160, .encrypt_rtp = aes_cm_encrypt_rtp, .decrypt_rtp = aes_cm_encrypt_rtp, + .encrypt_rtcp = aes_cm_encrypt_rtcp, + .decrypt_rtcp = aes_cm_encrypt_rtcp, .hash_rtp = hmac_sha1_rtp, + .hash_rtcp = hmac_sha1_rtcp, }, { .name = "F8_128_HMAC_SHA1_80", @@ -69,6 +78,7 @@ const struct crypto_suite crypto_suites[] = { .srtp_auth_key_len = 160, .srtcp_auth_key_len = 160, .hash_rtp = hmac_sha1_rtp, + .hash_rtcp = hmac_sha1_rtcp, }, }; @@ -197,7 +207,7 @@ int crypto_gen_session_key(struct crypto_context *c, str *out, unsigned char lab } /* rfc 3711 section 4.1.1 */ -static int aes_cm_encrypt_rtp(struct crypto_context *c, struct rtp_header *r, str *s, u_int64_t idx) { +static int aes_cm_encrypt(struct crypto_context *c, u_int32_t ssrc, str *s, u_int64_t idx) { unsigned char iv[16]; unsigned char *p; int i; @@ -205,7 +215,7 @@ static int aes_cm_encrypt_rtp(struct crypto_context *c, struct rtp_header *r, st ZERO(iv); memcpy(iv, c->session_salt, 14); - p = (void *) &r->ssrc; + p = (void *) &ssrc; for (i = 0; i < 4; i++) iv[i + 4] = iv[i + 4] ^ p[i]; @@ -217,6 +227,16 @@ static int aes_cm_encrypt_rtp(struct crypto_context *c, struct rtp_header *r, st return 0; } +/* rfc 3711 section 4.1 */ +static int aes_cm_encrypt_rtp(struct crypto_context *c, struct rtp_header *r, str *s, u_int64_t idx) { + return aes_cm_encrypt(c, r->ssrc, s, idx); +} + +/* rfc 3711 sections 3.4 and 4.1 */ +static int aes_cm_encrypt_rtcp(struct crypto_context *c, struct rtcp_packet *r, str *s, u_int64_t idx) { + return aes_cm_encrypt(c, r->ssrc, s, idx); +} + /* rfc 3711, sections 4.2 and 4.2.1 */ static int hmac_sha1_rtp(struct crypto_context *c, char *out, str *in, u_int64_t index) { unsigned char hmac[20]; @@ -235,3 +255,33 @@ static int hmac_sha1_rtp(struct crypto_context *c, char *out, str *in, u_int64_t return 0; } + +/* rfc 3711, sections 4.2 and 4.2.1 */ +static int hmac_sha1_rtcp(struct crypto_context *c, char *out, str *in) { + unsigned char hmac[20]; + + HMAC(EVP_sha1(), c->session_auth_key, c->crypto_suite->srtcp_auth_key_len / 8, + (unsigned char *) in->s, in->len, hmac, NULL); + + assert(sizeof(hmac) >= c->crypto_suite->srtcp_auth_tag / 8); + memcpy(out, hmac, c->crypto_suite->srtcp_auth_tag / 8); + + return 0; +} + +int crypto_gen_session_keys(struct crypto_context *c) { + str s; + + 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; +} diff --git a/daemon/crypto.h b/daemon/crypto.h index 6ab9cd833..c3c9f8846 100644 --- a/daemon/crypto.h +++ b/daemon/crypto.h @@ -26,9 +26,12 @@ enum mac { struct crypto_context; struct rtp_header; +struct rtcp_packet; -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, u_int64_t); +typedef int (*crypto_func_rtp)(struct crypto_context *, struct rtp_header *, str *, u_int64_t); +typedef int (*crypto_func_rtcp)(struct crypto_context *, struct rtcp_packet *, str *, u_int64_t); +typedef int (*hash_func_rtp)(struct crypto_context *, char *out, str *in, u_int64_t); +typedef int (*hash_func_rtcp)(struct crypto_context *, char *out, str *in); struct crypto_suite { const char *name; @@ -47,9 +50,12 @@ struct crypto_suite { srtcp_lifetime; enum cipher cipher; enum mac mac; - crypto_func encrypt_rtp, - decrypt_rtp; - hash_func hash_rtp; + crypto_func_rtp encrypt_rtp, + decrypt_rtp; + crypto_func_rtcp encrypt_rtcp, + decrypt_rtcp; + hash_func_rtp hash_rtp; + hash_func_rtcp hash_rtcp; }; struct crypto_context { @@ -88,24 +94,36 @@ extern const int num_crypto_suites; const struct crypto_suite *crypto_find_suite(const str *); +int crypto_gen_session_keys(struct crypto_context *c); int crypto_gen_session_key(struct crypto_context *, str *, unsigned char); static inline int crypto_encrypt_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->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); } +static inline int crypto_encrypt_rtcp(struct crypto_context *c, struct rtcp_packet *rtcp, + str *payload, u_int64_t index) +{ + return c->crypto_suite->encrypt_rtcp(c, rtcp, payload, index); +} +static inline int crypto_decrypt_rtcp(struct crypto_context *c, struct rtcp_packet *rtcp, + str *payload, u_int64_t index) +{ + return c->crypto_suite->decrypt_rtcp(c, rtcp, payload, index); +} +static inline int crypto_check_session_keys(struct crypto_context *c) { + if (c->have_session_key) + return 0; + if (!c->crypto_suite) + return -1; + return crypto_gen_session_keys(c); +} diff --git a/daemon/rtcp.c b/daemon/rtcp.c index 37815dd9b..05dfa9c42 100644 --- a/daemon/rtcp.c +++ b/daemon/rtcp.c @@ -7,6 +7,8 @@ #include "str.h" #include "call.h" #include "log.h" +#include "rtp.h" +#include "crypto.h" @@ -31,17 +33,6 @@ -struct rtcp_header { - unsigned char v_p_x; - unsigned char pt; - u_int16_t length; -} __attribute__ ((packed)); - -struct rtcp_packet { - struct rtcp_header header; - u_int32_t ssrc; -} __attribute__ ((packed)); - struct report_block { u_int32_t ssrc; unsigned char fraction_lost; @@ -320,3 +311,93 @@ int rtcp_avpf2avp(str *s) { return 0; } + + +static int rtcp_payload(struct rtcp_packet **out, str *p, const str *s) { + struct rtcp_packet *rtcp; + + if (s->len < sizeof(*rtcp)) + return -1; + + rtcp = (void *) s->s; + + if ((rtcp->header.v_p_x & 0xc0) != 0x80) /* version 2 */ + return -1; + if (rtcp->header.pt != RTCP_PT_SR + && rtcp->header.pt != RTCP_PT_RR) + return -1; + + *p = *s; + str_shift(p, sizeof(*rtcp)); + *out = rtcp; + + return 0; +} + +/* rfc 3711 section 3.4 */ +int rtcp_avp2savp(str *s, struct crypto_context *c) { + struct rtcp_packet *rtcp; + u_int32_t *idx; + str to_auth, payload; + + if (rtcp_payload(&rtcp, &payload, s)) + return -1; + if (crypto_check_session_keys(c)) + return -1; + + if (crypto_encrypt_rtcp(c, rtcp, &payload, c->num_packets)) + return -1; + + idx = (void *) s->s + s->len; + *idx = htonl(0x80000000ULL | c->num_packets++); + s->len += sizeof(*idx); + + to_auth = *s; + + rtp_append_mki(s, c); + + c->crypto_suite->hash_rtcp(c, s->s + s->len, &to_auth); + s->len += c->crypto_suite->srtp_auth_tag / 8; + + return 0; +} + + +/* rfc 3711 section 3.4 */ +int rtcp_savp2avp(str *s, struct crypto_context *c) { + struct rtcp_packet *rtcp; + str payload, to_auth, to_decrypt, auth_tag; + u_int32_t idx, *idx_p; + char hmac[20]; + + if (rtcp_payload(&rtcp, &payload, s)) + return -1; + if (crypto_check_session_keys(c)) + return -1; + + if (srtp_payloads(&to_auth, &to_decrypt, &auth_tag, NULL, + c->crypto_suite->srtcp_auth_tag, c->mki_len, + s, &payload)) + return -1; + + if (to_decrypt.len < sizeof(idx)) + return -1; + to_decrypt.len -= sizeof(idx); + idx_p = (void *) to_decrypt.s + to_decrypt.len; + idx = ntohl(*idx_p); + + assert(sizeof(hmac) >= auth_tag.len); + c->crypto_suite->hash_rtcp(c, hmac, &to_auth); + if (str_memcmp(&auth_tag, hmac)) + return -1; + + if (idx & 0x80000000ULL) { + if (crypto_decrypt_rtcp(c, rtcp, &to_decrypt, idx & 0x7fffffffULL)) + return -1; + } + + *s = to_auth; + to_auth.len -= sizeof(idx); + + return 0; +} diff --git a/daemon/rtcp.h b/daemon/rtcp.h index 32674a664..d535b5220 100644 --- a/daemon/rtcp.h +++ b/daemon/rtcp.h @@ -3,6 +3,28 @@ #include "str.h" + +struct crypto_context; + + + +struct rtcp_header { + unsigned char v_p_x; + unsigned char pt; + u_int16_t length; +} __attribute__ ((packed)); + +struct rtcp_packet { + struct rtcp_header header; + u_int32_t ssrc; +} __attribute__ ((packed)); + + + int rtcp_avpf2avp(str *); +int rtcp_avp2savp(str *, struct crypto_context *); +int rtcp_savp2avp(str *, struct crypto_context *); + + #endif diff --git a/daemon/rtp.c b/daemon/rtp.c index 0d7e08115..aa46108bf 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -10,29 +10,7 @@ -static inline int check_session_key(struct crypto_context *c) { - str s; - - if (c->have_session_key) - return 0; - if (!c->crypto_suite) - return -1; - - 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; -} - -static int rtp_payload(str *p, str *s) { +static int rtp_payload(struct rtp_header **out, str *p, const str *s) { struct rtp_header *rtp; struct rtp_extension *ext; @@ -59,6 +37,8 @@ static int rtp_payload(str *p, str *s) { return -1; } + *out = rtp; + return 0; } @@ -94,21 +74,46 @@ static u_int64_t packet_index(struct crypto_context *c, struct rtp_header *rtp) return index; } +void rtp_append_mki(str *s, struct crypto_context *c) { + u_int32_t mki_part; + char *p; + + if (!c->mki_len) + return; + + /* RTP_BUFFER_TAIL_ROOM guarantees enough room */ + p = s->s + s->len; + memset(p, 0, c->mki_len); + if (c->mki_len > 4) { + mki_part = (c->mki & 0xffffffff00000000ULL) >> 32; + mki_part = htonl(mki_part); + if (c->mki_len < 8) + memcpy(p, ((char *) &mki_part) + (8 - c->mki_len), c->mki_len - 4); + else + memcpy(p + (c->mki_len - 8), &mki_part, 4); + } + mki_part = (c->mki & 0xffffffffULL); + mki_part = htonl(mki_part); + if (c->mki_len < 4) + memcpy(p, ((char *) &mki_part) + (4 - c->mki_len), c->mki_len); + else + memcpy(p + (c->mki_len - 4), &mki_part, 4); + + s->len += c->mki_len; +} + /* 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)) + if (rtp_payload(&rtp, &payload, s)) return -1; - if (check_session_key(c)) + if (crypto_check_session_keys(c)) return -1; - rtp = (void *) s->s; index = packet_index(c, rtp); /* rfc 3711 section 3.1 */ @@ -116,38 +121,15 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { if (crypto_encrypt_rtp(c, rtp, &payload, index)) return -1; - pl_end = s->s + s->len; to_auth = *s; - if (c->mki_len) { - /* RTP_BUFFER_TAIL_ROOM guarantees enough room */ - memset(pl_end, 0, c->mki_len); - if (c->mki_len > 4) { - mki_part = (c->mki & 0xffffffff00000000ULL) >> 32; - mki_part = htonl(mki_part); - if (c->mki_len < 8) - memcpy(pl_end, ((char *) &mki_part) + (8 - c->mki_len), c->mki_len - 4); - else - memcpy(pl_end + (c->mki_len - 8), &mki_part, 4); - } - mki_part = (c->mki & 0xffffffffULL); - mki_part = htonl(mki_part); - if (c->mki_len < 4) - memcpy(pl_end, ((char *) &mki_part) + (4 - c->mki_len), c->mki_len); - else - memcpy(pl_end + (c->mki_len - 4), &mki_part, 4); - - pl_end += c->mki_len; - to_auth.len += c->mki_len; - } + rtp_append_mki(s, c); if (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; + c->crypto_suite->hash_rtp(c, s->s + s->len, &to_auth, index); + s->len += c->crypto_suite->srtp_auth_tag / 8; } - s->len = pl_end - s->s; - return 0; } @@ -155,55 +137,68 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { 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; + str payload, to_auth, to_decrypt, auth_tag; + char hmac[20]; - if (rtp_payload(&payload, s)) + if (rtp_payload(&rtp, &payload, s)) return -1; - if (check_session_key(c)) + if (crypto_check_session_keys(c)) return -1; - rtp = (void *) s->s; index = packet_index(c, rtp); + if (srtp_payloads(&to_auth, &to_decrypt, &auth_tag, NULL, + c->crypto_suite->srtp_auth_tag, c->mki_len, + s, &payload)) + return -1; - /* 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) + if (auth_tag.len) { + assert(sizeof(hmac) >= auth_tag.len); + c->crypto_suite->hash_rtp(c, hmac, &to_auth, index); + if (str_memcmp(&auth_tag, hmac)) 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; + if (crypto_decrypt_rtp(c, rtp, &to_decrypt, index)) + 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; + *s = to_auth; - /* ignoring the mki for now */ - } + return 0; +} - 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)) +/* rfc 3711 section 3.1 and 3.4 */ +int srtp_payloads(str *to_auth, str *to_decrypt, str *auth_tag, str *mki, + int auth_len, int mki_len, + const str *packet, const str *payload) +{ + auth_len /= 8; /* given in bits */ + *to_auth = *packet; + *to_decrypt = *payload; + /* packet and payload should be identical except for the respective header */ + assert(to_auth->s + to_auth->len == to_decrypt->s + to_decrypt->len); + assert(to_decrypt->s >= to_auth->s); + + *auth_tag = STR_NULL; + if (auth_len) { + if (to_decrypt->len < auth_len) return -1; + + str_init_len(auth_tag, to_decrypt->s + to_decrypt->len - auth_len, auth_len); + to_decrypt->len -= auth_len; + to_auth->len -= auth_len; } - if (crypto_decrypt_rtp(c, rtp, &payload, index)) - return -1; + if (mki) + *mki = STR_NULL; + if (mki_len) { + if (to_decrypt->len < mki_len) + return -1; - *s = to_auth; + if (mki) + str_init_len(mki, to_decrypt->s - mki_len, mki_len); + to_decrypt->len -= mki_len; + to_auth->len -= mki_len; + } return 0; } diff --git a/daemon/rtp.h b/daemon/rtp.h index bec3f7f2c..3405b0a47 100644 --- a/daemon/rtp.h +++ b/daemon/rtp.h @@ -30,6 +30,11 @@ struct rtp_extension { int rtp_avp2savp(str *, struct crypto_context *); int rtp_savp2avp(str *, struct crypto_context *); +void rtp_append_mki(str *s, struct crypto_context *c); +int srtp_payloads(str *to_auth, str *to_decrypt, str *auth_tag, str *mki, + int auth_len, int mki_len, + const str *packet, const str *payload); + diff --git a/daemon/str.h b/daemon/str.h index dd8546d95..5ca46cff0 100644 --- a/daemon/str.h +++ b/daemon/str.h @@ -51,6 +51,8 @@ static inline str *str_dup(const str *s); static inline str *str_chunk_insert(GStringChunk *c, const str *s); /* shifts pointer by len chars and decrements len. returns -1 if buffer too short, 0 otherwise */ static inline int str_shift(str *s, int len); +/* binary compares str object with memory chunk of equal size */ +static inline int str_memcmp(const str *s, void *m); /* asprintf() analogs */ #define str_sprintf(fmt, a...) __str_sprintf(STR_MALLOC_PADDING fmt, a) @@ -196,5 +198,8 @@ static inline str *g_string_free_str(GString *gs) { g_string_free(gs, FALSE); return ret; } +static inline int str_memcmp(const str *s, void *m) { + return memcmp(s->s, m, s->len); +} #endif