diff --git a/README.md b/README.md index fa0129a88..bc23cf574 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ The options are described in more detail below. * --dtls-passive - Enabled the `DTLS=passive` flag for all calls unconditionally. + Enables the `DTLS=passive` flag for all calls unconditionally. * -d, --delete-delay @@ -776,11 +776,44 @@ Optionally included keys are: * `DTLS` - Contains a string and influences the behaviour of DTLS-SRTP. Currently the only recognized option - is `passive`, which instructs *rtpengine* to prefer the passive (i.e. server) role for the DTLS - handshake. The default is to take the active (client) role if possible. This is useful in cases where - the SRTP endpoint isn't able to receive or process the DTLS handshake packets, for example when it's - behind NAT or needs to finish ICE processing first. + Contains a string and influences the behaviour of DTLS-SRTP. Possible values are: + + - `off` or `no` or `disable` + + Prevents *rtpengine* from offering or acceping DTLS-SRTP when otherwise it would. The default + is to offer DTLS-SRTP when encryption is desired and to favour it over SDES when accepting + an offer. + + - `passive` + + Instructs *rtpengine* to prefer the passive (i.e. server) role for the DTLS + handshake. The default is to take the active (client) role if possible. This is useful in cases + where the SRTP endpoint isn't able to receive or process the DTLS handshake packets, for example + when it's behind NAT or needs to finish ICE processing first. + +* `SDES` + + A list of strings controlling the behaviour regarding SDES. The default is to offer SDES without any + session parameters when encryption is desired, and to accept it when DTLS-SRTP is unavailable. If two + SDES endpoints are connected to each other, then the default is to offer SDES with the same options + as were received from the other endpoint. + + These options can also be put into the `flags` list using a prefix of `SDES-`. All options controlling + SDES session parameters can be used either in all lower case or in all upper case. + + - `off` or `no` or `disable` + + Prevents *rtpengine* from offering SDES, leaving DTLS-SRTP as the other option. + + - `unencrypted_srtp`, `unencrypted_srtcp` and `unauthenticated_srtp` + + Enables the respective SDES session parameter (see section 6.3 or RFC 4568). The default is to + copy these options from the offering client, or not to have them enabled if SDES wasn't offered. + + - `encrypted_srtp`, `encrypted_srtcp` and `authenticated_srtp` + + Negates the respective option. This is useful if one of the session parameters was offered by + an SDES endpoint, but it should not be offered on the far side if this endpoint also speaks SDES. An example of a complete `offer` request dictionary could be (SDP body abbreviated): diff --git a/daemon/call.c b/daemon/call.c index 0b14501cb..0d0d338b8 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -538,6 +538,12 @@ static int __k_srtp_crypt(struct rtpengine_srtp *s, struct crypto_context *c) { memcpy(s->mki, c->params.mki, c->params.mki_len); memcpy(s->master_key, c->params.master_key, c->params.crypto_suite->master_key_len); memcpy(s->master_salt, c->params.master_salt, c->params.crypto_suite->master_salt_len); + + if (c->params.session_params.unencrypted_srtp) + s->cipher = REC_NULL; + if (c->params.session_params.unauthenticated_srtp) + s->auth_tag_len = 0; + return 0; } static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *stream) { @@ -2154,10 +2160,21 @@ static void __generate_crypto(const struct sdp_ng_flags *flags, struct call_medi if (!MEDIA_ISSET(this, INITIALIZED)) { /* we offer both DTLS and SDES by default */ - MEDIA_SET(this, DTLS); - MEDIA_SET(this, SDES); + /* unless this is overridden by flags */ + if (!flags->dtls_off) + MEDIA_SET(this, DTLS); + if (!flags->sdes_off) + MEDIA_SET(this, SDES); + else + goto skip_sdes; } else { + /* if both SDES and DTLS are supported, we may use the flags to select one + * over the other */ + if (MEDIA_ARESET2(this, DTLS, SDES) && flags->dtls_off) + MEDIA_CLEAR(this, DTLS); + /* flags->sdes_off is ignored as we prefer DTLS by default */ + /* if we're talking to someone understanding DTLS, then skip the SDES stuff */ if (MEDIA_ISSET(this, DTLS)) { MEDIA_CLEAR(this, SDES); @@ -2165,17 +2182,19 @@ static void __generate_crypto(const struct sdp_ng_flags *flags, struct call_medi } } + /* SDES parameters below */ + /* for answer case, otherwise we default to one */ this->sdes_out.tag = cp_in->crypto_suite ? this->sdes_in.tag : 1; if (other->sdes_in.params.crypto_suite) { /* SRTP <> SRTP case, copy from other stream */ - crypto_params_copy(cp, &other->sdes_in.params); - return; + cp->session_params = cp_in->session_params; + crypto_params_copy(cp, &other->sdes_in.params, (flags->opmode == OP_OFFER) ? 1 : 0); } if (cp->crypto_suite) - return; + goto apply_sdes_flags; cp->crypto_suite = cp_in->crypto_suite; if (!cp->crypto_suite) @@ -2185,6 +2204,23 @@ static void __generate_crypto(const struct sdp_ng_flags *flags, struct call_medi random_string((unsigned char *) cp->master_salt, cp->crypto_suite->master_salt_len); /* mki = mki_len = 0 */ + cp->session_params.unencrypted_srtp = cp_in->session_params.unencrypted_srtp; + cp->session_params.unencrypted_srtcp = cp_in->session_params.unencrypted_srtcp; + cp->session_params.unauthenticated_srtp = cp_in->session_params.unauthenticated_srtp; + +apply_sdes_flags: + if (flags->sdes_unencrypted_srtp && flags->opmode == OP_OFFER) + cp_in->session_params.unencrypted_srtp = cp->session_params.unencrypted_srtp = 1; + else if (flags->sdes_encrypted_srtp) + cp_in->session_params.unencrypted_srtp = cp->session_params.unencrypted_srtp = 0; + if (flags->sdes_unencrypted_srtcp && flags->opmode == OP_OFFER) + cp_in->session_params.unencrypted_srtcp = cp->session_params.unencrypted_srtcp = 1; + else if (flags->sdes_encrypted_srtcp) + cp_in->session_params.unencrypted_srtcp = cp->session_params.unencrypted_srtcp = 0; + if (flags->sdes_unauthenticated_srtp && flags->opmode == OP_OFFER) + cp_in->session_params.unauthenticated_srtp = cp->session_params.unauthenticated_srtp = 1; + else if (flags->sdes_authenticated_srtp) + cp_in->session_params.unauthenticated_srtp = cp->session_params.unauthenticated_srtp = 0; skip_sdes: ; @@ -2471,7 +2507,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, SHARED_FLAG_RTCP_MUX | SHARED_FLAG_ASYMMETRIC | SHARED_FLAG_ICE | SHARED_FLAG_TRICKLE_ICE | SHARED_FLAG_ICE_LITE); - crypto_params_copy(&other_media->sdes_in.params, &sp->crypto); + crypto_params_copy(&other_media->sdes_in.params, &sp->crypto, 1); other_media->sdes_in.tag = sp->sdes_tag; if (other_media->sdes_in.params.crypto_suite) MEDIA_SET(other_media, SDES); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index f9f495803..3b6555490 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -496,6 +496,33 @@ INLINE char *bencode_get_alt(bencode_item_t *i, const char *one, const char *two return bencode_dictionary_get_str(i, two, out); } +INLINE void ng_sdes_option(struct sdp_ng_flags *out, bencode_item_t *it, unsigned int strip) { + str s; + + if (!bencode_get_str(it, &s)) + return; + str_shift(&s, strip); + + if (!str_cmp(&s, "no") || !str_cmp(&s, "off") || !str_cmp(&s, "disabled") + || !str_cmp(&s, "disable")) + out->sdes_off = 1; + else if (!str_cmp(&s, "unencrypted_srtp") || !str_cmp(&s, "UNENCRYPTED_SRTP")) + out->sdes_unencrypted_srtp = 1; + else if (!str_cmp(&s, "unencrypted_srtcp") || !str_cmp(&s, "UNENCRYPTED_SRTCP")) + out->sdes_unencrypted_srtcp = 1; + else if (!str_cmp(&s, "unauthenticated_srtp") || !str_cmp(&s, "UNAUTHENTICATED_SRTP")) + out->sdes_unauthenticated_srtp = 1; + else if (!str_cmp(&s, "encrypted_srtp") || !str_cmp(&s, "ENCRYPTED_SRTP")) + out->sdes_encrypted_srtp = 1; + else if (!str_cmp(&s, "encrypted_srtcp") || !str_cmp(&s, "ENCRYPTED_SRTCP")) + out->sdes_encrypted_srtcp = 1; + else if (!str_cmp(&s, "authenticated_srtp") || !str_cmp(&s, "AUTHENTICATED_SRTP")) + out->sdes_authenticated_srtp = 1; + else + ilog(LOG_WARN, "Unknown 'SDES' flag encountered: '"STR_FORMAT"'", + STR_FMT(&s)); +} + static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) { bencode_item_t *list, *it; int diridx; @@ -508,7 +535,11 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu if ((list = bencode_dictionary_get_expect(input, "flags", BENCODE_LIST))) { for (it = list->child; it; it = it->sibling) { + if (it->type != BENCODE_STRING) + continue; + str_hyphenate(it); + if (!bencode_strcmp(it, "trust-address")) out->trust_address = 1; else if (!bencode_strcmp(it, "SIP-source-address")) @@ -519,6 +550,8 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu out->strict_source = 1; else if (!bencode_strcmp(it, "media-handover")) out->media_handover = 1; + else if (it->iov[1].iov_len >= 5 && !memcmp(it->iov[1].iov_base, "SDES-", 5)) + ng_sdes_option(out, it, 5); else ilog(LOG_WARN, "Unknown flag encountered: '"BENCODE_FORMAT"'", BENCODE_FMT(it)); @@ -568,6 +601,9 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu if (bencode_dictionary_get_str(input, "DTLS", &s)) { if (!str_cmp(&s, "passive")) out->dtls_passive = 1; + else if (!str_cmp(&s, "no") || !str_cmp(&s, "off") || !str_cmp(&s, "disabled") + || !str_cmp(&s, "disable")) + out->dtls_off = 1; else ilog(LOG_WARN, "Unknown 'DTLS' flag encountered: '"STR_FORMAT"'", STR_FMT(&s)); @@ -589,6 +625,13 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu } } + /* XXX abstractize the other list walking functions using callbacks */ + /* XXX module still needs to support this list */ + if ((list = bencode_dictionary_get_expect(input, "SDES", BENCODE_LIST))) { + for (it = list->child; it; it = it->sibling) + ng_sdes_option(out, it, 0); + } + bencode_get_alt(input, "transport-protocol", "transport protocol", &out->transport_protocol_str); out->transport_protocol = transport_protocol(&out->transport_protocol_str); bencode_get_alt(input, "media-address", "media address", &out->media_address); diff --git a/daemon/control_ng.c b/daemon/control_ng.c index dc0c1d1e5..29ee575e7 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -29,7 +29,8 @@ static void pretty_print(bencode_item_t *el, GString *s) { break; case BENCODE_LIST: - sep = "[ "; + g_string_append(s, "[ "); + sep = ""; for (chld = el->child; chld; chld = chld->sibling) { g_string_append(s, sep); pretty_print(chld, s); @@ -39,7 +40,8 @@ static void pretty_print(bencode_item_t *el, GString *s) { break; case BENCODE_DICTIONARY: - sep = "{ "; + g_string_append(s, "{ "); + sep = ""; for (chld = el->child; chld; chld = chld->sibling) { g_string_append(s, sep); pretty_print(chld, s); diff --git a/daemon/crypto.h b/daemon/crypto.h index ea27dc006..54aa37c62 100644 --- a/daemon/crypto.h +++ b/daemon/crypto.h @@ -57,6 +57,12 @@ struct crypto_suite { const char *dtls_profile_code; }; +struct crypto_session_params { + int unencrypted_srtcp:1, + unencrypted_srtp:1, + unauthenticated_srtp:1; +}; + struct crypto_params { const struct crypto_suite *crypto_suite; /* we only support one master key for now */ @@ -64,6 +70,7 @@ struct crypto_params { unsigned char master_salt[SRTP_MAX_MASTER_SALT_LEN]; unsigned char *mki; unsigned int mki_len; + struct crypto_session_params session_params; }; struct crypto_context { @@ -141,9 +148,17 @@ INLINE void crypto_reset(struct crypto_context *c) { c->last_index = 0; c->ssrc = 0; } -INLINE void crypto_params_copy(struct crypto_params *o, const struct crypto_params *i) { +INLINE void crypto_params_copy(struct crypto_params *o, const struct crypto_params *i, int copy_sp) { + struct crypto_session_params sp; + crypto_params_cleanup(o); + + if (!copy_sp) + sp = o->session_params; *o = *i; + if (!copy_sp) + o->session_params = sp; + if (o->mki_len > 255) o->mki_len = 0; if (o->mki_len) { @@ -153,7 +168,7 @@ INLINE void crypto_params_copy(struct crypto_params *o, const struct crypto_para } INLINE void crypto_init(struct crypto_context *c, const struct crypto_params *p) { crypto_cleanup(c); - crypto_params_copy(&c->params, p); + crypto_params_copy(&c->params, p, 1); } INLINE int crypto_params_cmp(const struct crypto_params *a, const struct crypto_params *b) { if (a->crypto_suite != b->crypto_suite) @@ -168,6 +183,8 @@ INLINE int crypto_params_cmp(const struct crypto_params *a, const struct crypto_ return 1; if (a->mki_len && memcmp(a->mki, b->mki, a->mki_len)) return 1; + if (memcmp(&a->session_params, &b->session_params, sizeof(a->session_params))) + return 1; return 0; } diff --git a/daemon/rtcp.c b/daemon/rtcp.c index 793bc39c5..6e0c782d5 100644 --- a/daemon/rtcp.c +++ b/daemon/rtcp.c @@ -401,11 +401,11 @@ int rtcp_avp2savp(str *s, struct crypto_context *c) { if (check_session_keys(c)) return -1; - if (crypto_encrypt_rtcp(c, rtcp, &payload, c->last_index)) + if (!c->params.session_params.unencrypted_srtcp && crypto_encrypt_rtcp(c, rtcp, &payload, c->last_index)) return -1; idx = (void *) s->s + s->len; - *idx = htonl(0x80000000ULL | c->last_index++); + *idx = htonl((c->params.session_params.unencrypted_srtcp ? 0ULL : 0x80000000ULL) | c->last_index++); s->len += sizeof(*idx); to_auth = *s; diff --git a/daemon/rtp.c b/daemon/rtp.c index 8fb624030..a70544bee 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -201,14 +201,14 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { /* rfc 3711 section 3.1 */ - if (crypto_encrypt_rtp(c, rtp, &payload, index)) + if (!c->params.session_params.unencrypted_srtp && crypto_encrypt_rtp(c, rtp, &payload, index)) return -1; to_auth = *s; rtp_append_mki(s, c); - if (c->params.crypto_suite->srtp_auth_tag) { + if (!c->params.session_params.unauthenticated_srtp && c->params.crypto_suite->srtp_auth_tag) { c->params.crypto_suite->hash_rtp(c, s->s + s->len, &to_auth, index); s->len += c->params.crypto_suite->srtp_auth_tag; } @@ -230,7 +230,8 @@ int rtp_savp2avp(str *s, struct crypto_context *c) { index = packet_index(c, rtp); if (srtp_payloads(&to_auth, &to_decrypt, &auth_tag, NULL, - c->params.crypto_suite->srtp_auth_tag, c->params.mki_len, + c->params.session_params.unauthenticated_srtp ? 0 : c->params.crypto_suite->srtp_auth_tag, + c->params.mki_len, s, &payload)) return -1; @@ -265,7 +266,7 @@ int rtp_savp2avp(str *s, struct crypto_context *c) { decrypt_idx: c->last_index = index; decrypt: - if (crypto_decrypt_rtp(c, rtp, &to_decrypt, index)) + if (!c->params.session_params.unencrypted_srtp && crypto_decrypt_rtp(c, rtp, &to_decrypt, index)) return -1; *s = to_auth; diff --git a/daemon/sdp.c b/daemon/sdp.c index b15b28966..6ccc22649 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -97,13 +97,13 @@ struct attribute_crypto { str tag_str; str crypto_suite_str; str key_params_str; - /* str session_params; */ str key_base64_str; str lifetime_str; str mki_str; unsigned int tag; + /* XXX use struct crypto_params for these below? */ const struct crypto_suite *crypto_suite; str master_key; str salt; @@ -111,6 +111,9 @@ struct attribute_crypto { u_int64_t lifetime; unsigned char mki[256]; unsigned int mki_len; + int unencrypted_srtcp:1, + unencrypted_srtp:1, + unauthenticated_srtp:1; }; struct attribute_ssrc { @@ -542,6 +545,15 @@ static int parse_attribute_crypto(struct sdp_attribute *output) { memcpy(c->mki + (c->mki_len - sizeof(u32)), &u32, sizeof(u32)); } + while (extract_token(&start, end, &s) == 0) { + if (!str_cmp(&s, "UNENCRYPTED_SRTCP")) + c->unencrypted_srtcp = 1; + else if (!str_cmp(&s, "UNENCRYPTED_SRTP")) + c->unencrypted_srtp = 1; + else if (!str_cmp(&s, "UNAUTHENTICATED_SRTP")) + c->unauthenticated_srtp = 1; + } + return 0; error: @@ -1233,6 +1245,9 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl attr->u.crypto.master_key.len); memcpy(sp->crypto.master_salt, attr->u.crypto.salt.s, attr->u.crypto.salt.len); + sp->crypto.session_params.unencrypted_srtp = attr->u.crypto.unencrypted_srtp; + sp->crypto.session_params.unencrypted_srtcp = attr->u.crypto.unencrypted_srtcp; + sp->crypto.session_params.unauthenticated_srtp = attr->u.crypto.unauthenticated_srtp; } /* a=sendrecv/sendonly/recvonly/inactive */ @@ -1863,6 +1878,12 @@ static void insert_crypto(struct call_media *media, struct sdp_chopper *chop) { ull |= cp->mki[cp->mki_len - i - 1] << (i * 8); chopper_append_printf(chop, "|%llu:%u", ull, cp->mki_len); } + if (cp->session_params.unencrypted_srtp) + chopper_append_c(chop, " UNENCRYPTED_SRTP"); + if (cp->session_params.unencrypted_srtcp) + chopper_append_c(chop, " UNENCRYPTED_SRTCP"); + if (cp->session_params.unauthenticated_srtp) + chopper_append_c(chop, " UNAUTHENTICATED_SRTP"); chopper_append_c(chop, "\r\n"); } diff --git a/daemon/sdp.h b/daemon/sdp.h index 5d20f7ec6..4a9005a99 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -32,7 +32,15 @@ struct sdp_ng_flags { rtcp_mux_reject:1, strict_source:1, media_handover:1, - dtls_passive:1; + dtls_passive:1, + dtls_off:1, + sdes_off:1, + sdes_unencrypted_srtp:1, + sdes_unencrypted_srtcp:1, + sdes_unauthenticated_srtp:1, + sdes_encrypted_srtp:1, + sdes_encrypted_srtcp:1, + sdes_authenticated_srtp:1; }; struct sdp_chopper { diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl index 3c2e98cb9..1885ddddd 100755 --- a/tests/simulator-ng.pl +++ b/tests/simulator-ng.pl @@ -120,10 +120,11 @@ sub rtcp_encrypt { my $iv = $$dctx{crypto_suite}{iv_rtcp}->($dctx, $r); my ($hdr, $to_enc) = unpack('a8a*', $r); - my $enc = $$dctx{crypto_suite}{enc_func}->($to_enc, $$dctx{rtcp_session_key}, + my $enc = $$dctx{unenc_srtcp} ? $to_enc : + $$dctx{crypto_suite}{enc_func}->($to_enc, $$dctx{rtcp_session_key}, $iv, $$dctx{rtcp_session_salt}); my $pkt = $hdr . $enc; - $pkt .= pack("N", (($$dctx{rtcp_index} || 0) | 0x80000000)); + $pkt .= pack("N", (($$dctx{rtcp_index} || 0) | ($$dctx{unenc_srtcp} ? 0 : 0x80000000))); my $hmac = hmac_sha1($pkt, $$dctx{rtcp_session_auth_key}); @@ -152,7 +153,7 @@ sub rtp_encrypt { ($NOENC && $NOENC{rtp_packet}) and return $NOENC{rtp_packet}; my ($pkt, $roc) = SRTP::encrypt_rtp(@$dctx{qw(crypto_suite rtp_session_key rtp_session_salt - rtp_session_auth_key rtp_roc rtp_mki rtp_mki_len)}, $r); + rtp_session_auth_key rtp_roc rtp_mki rtp_mki_len unenc_srtp unauth_srtp)}, $r); $$dctx{rtp_roc} = $roc; $NOENC{rtp_packet} = $pkt; @@ -166,8 +167,20 @@ sub savp_sdp { my ($ctx, $ctx_o) = @_; if (!$$ctx{out}{crypto_suite}) { - $$ctx{out}{crypto_suite} = $$ctx_o{in}{crypto_suite} ? $$ctx_o{in}{crypto_suite} - : $SRTP::crypto_suites[rand(@SRTP::crypto_suites)]; + if ($$ctx{in}{crypto_suite}) { + $$ctx{out}{crypto_suite} = $$ctx{in}{crypto_suite}; + $$ctx{out}{crypto_tag} = $$ctx{in}{crypto_tag}; + $$ctx{out}{unenc_srtp} = $$ctx{in}{unenc_srtp}; + $$ctx{out}{unenc_srtcp} = $$ctx{in}{unenc_srtcp}; + $$ctx{out}{unauth_srtp} = $$ctx{in}{unauth_srtp}; + } + else { + $$ctx{out}{crypto_suite} = $SRTP::crypto_suites[rand(@SRTP::crypto_suites)]; + $$ctx{out}{crypto_tag} = int(rand(100)); + $$ctx{out}{unenc_srtp} = rand() < .5 ? 0 : 1; + $$ctx{out}{unenc_srtcp} = rand() < .5 ? 0 : 1; + $$ctx{out}{unauth_srtp} = rand() < .5 ? 0 : 1; + } $$ctx{out}{rtp_mki_len} = 0; if (rand() > .5) { @@ -193,10 +206,12 @@ sub savp_sdp { $NOENC{rtp_master_salt} = $$ctx{out}{rtp_master_salt}; } - my $ret = "a=crypto:0 $$ctx{out}{crypto_suite}{str} inline:" . encode_base64($$ctx{out}{rtp_master_key} . $$ctx{out}{rtp_master_salt}, ''); - if ($$ctx{out}{rtp_mki_len}) { - $ret .= "|$$ctx{out}{rtp_mki}:$$ctx{out}{rtp_mki_len}"; - } + my $ret = "a=crypto:$$ctx{out}{crypto_tag} $$ctx{out}{crypto_suite}{str} inline:" . encode_base64($$ctx{out}{rtp_master_key} . $$ctx{out}{rtp_master_salt}, ''); + $$ctx{out}{rtp_mki_len} and $ret .= "|$$ctx{out}{rtp_mki}:$$ctx{out}{rtp_mki_len}"; + + $$ctx{out}{unenc_srtp} and $ret .= " UNENCRYPTED_SRTP"; + $$ctx{out}{unenc_srtcp} and $ret .= " UNENCRYPTED_SRTCP"; + $$ctx{out}{unauth_srtp} and $ret .= " UNAUTHENTICATED_SRTP"; $ret .= "\n"; return $ret; @@ -290,20 +305,24 @@ sub rtp_savp { sub savp_crypto { my ($sdp, $ctx, $ctx_o) = @_; - my @a = $sdp =~ /[\r\n]a=crypto:\d+ (\w+) inline:([\w\/+]{40})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?[\r\n]/sig; + my @a = $sdp =~ /[\r\n]a=crypto:(\d+) (\w+) inline:([\w\/+]{40})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?(?: (.*?))?[\r\n]/sig; @a or die; my $i = 0; - while (@a >= 6) { - $$ctx[$i]{in}{crypto_suite} = $SRTP::crypto_suites{$a[0]} or die; + while (@a >= 8) { + $$ctx[$i]{in}{crypto_suite} = $SRTP::crypto_suites{$a[1]} or die; + $$ctx[$i]{in}{crypto_tag} = $a[0]; ($$ctx[$i]{in}{rtp_master_key}, $$ctx[$i]{in}{rtp_master_salt}) - = SRTP::decode_inline_base64($a[1]); - $$ctx[$i]{in}{rtp_mki} = $a[4]; - $$ctx[$i]{in}{rtp_mki_len} = $a[5]; + = SRTP::decode_inline_base64($a[2]); + $$ctx[$i]{in}{rtp_mki} = $a[5]; + $$ctx[$i]{in}{rtp_mki_len} = $a[6]; undef($$ctx[$i]{in}{rtp_session_key}); undef($$ctx[$i]{in}{rtcp_session_key}); + ($a[7] || '') =~ /UNENCRYPTED_SRTP/ and $$ctx[$i]{in}{unenc_srtp} = 1; + ($a[7] || '') =~ /UNENCRYPTED_SRTCP/ and $$ctx[$i]{in}{unenc_srtcp} = 1; + ($a[7] || '') =~ /UNAUTHENTICATED_SRTP/ and $$ctx[$i]{in}{unauth_srtp} = 1; $i++; - @a = @a[6 .. $#a]; + @a = @a[8 .. $#a]; } } @@ -607,6 +626,25 @@ a=rtpmap:111 opus/48000/2 { $$dict{'address family'} = $$pr_o{family_str}; $$dict{'transport protocol'} = $$tr_o{name}; + + if ($$tr_o{name} =~ /SAVP/ && $op eq 'offer') { + my (@opts, @opt); + rand() < .2 and @opt = ('unencrypted_srtp'); + rand() < .2 and @opt = ('encrypted_srtp'); + rand() < .5 and @opt = (); + push(@opts, @opt); + @opt = (); + rand() < .2 and @opt = ('unencrypted_srtcp'); + rand() < .2 and @opt = ('encrypted_srtcp'); + rand() < .5 and @opt = (); + push(@opts, @opt); + @opt = (); + rand() < .2 and @opt = ('unauthenticated_srtp'); + rand() < .2 and @opt = ('authenticated_srtp'); + rand() < .5 and @opt = (); + push(@opts, @opt); + $$dict{SDES} = \@opts; + } } #print(Dumper($dict) . "\n\n"); diff --git a/utils/SRTP.pm b/utils/SRTP.pm index 83e19edd0..dfdf48ecf 100644 --- a/utils/SRTP.pm +++ b/utils/SRTP.pm @@ -215,14 +215,14 @@ sub decode_inline_base64 { } sub encrypt_rtp { - my ($suite, $skey, $ssalt, $sauth, $roc, $mki, $mki_len, $packet) = @_; + my ($suite, $skey, $ssalt, $sauth, $roc, $mki, $mki_len, $unenc_srtp, $unauth_srtp, $packet) = @_; my ($hdr, $seq, $h2, $to_enc) = unpack('a2na8a*', $packet); $roc = $roc || 0; $seq == 0 and $roc++; my $iv = $$suite{iv_rtp}->($packet, $ssalt, $roc); - my $enc = $$suite{enc_func}->($to_enc, $skey, + my $enc = $unenc_srtp ? $to_enc : $$suite{enc_func}->($to_enc, $skey, $iv, $ssalt); my $pkt = pack('a*na*a*', $hdr, $seq, $h2, $enc); @@ -232,7 +232,7 @@ sub encrypt_rtp { append_mki(\$pkt, $mki_len, $mki); #$pkt .= pack("N", 1); # mki - $pkt .= substr($hmac, 0, $$suite{auth_tag}); + $pkt .= substr($hmac, 0, $unauth_srtp ? 0 : $$suite{auth_tag}); return ($pkt, $roc); } diff --git a/utils/ng-client b/utils/ng-client index b8d54b3d5..e02ff152b 100755 --- a/utils/ng-client +++ b/utils/ng-client @@ -31,10 +31,8 @@ GetOptions( 'sdp-file=s' => \$options{'sdp-file'}, 'ICE=s' => \$options{'ICE'}, 'DTLS=s' => \$options{'DTLS'}, - 'rtcp-mux-offer' => \$options{'rtcp-mux-offer'}, - 'rtcp-mux-demux' => \$options{'rtcp-mux-demux'}, - 'rtcp-mux-accept' => \$options{'rtcp-mux-accept'}, - 'rtcp-mux-reject' => \$options{'rtcp-mux-reject'}, + 'SDES=s@' => \$options{'SDES'}, + 'rtcp-mux=s@' => \$options{'rtcp-mux'}, 'address-family=s' => \$options{'address family'}, 'direction=s' => \$options{'direction'}, 'force' => \$options{'force'}, @@ -57,8 +55,9 @@ for my $x (split(',', 'trust address,symmetric,asymmetric,force,strict source,me for my $x (split(',', 'origin,session connection')) { defined($options{'replace-' . $x}) and push(@{$packet{replace}}, $x); } -for my $x (split(',', 'offer,demux,accept,reject')) { - defined($options{'rtcp-mux-' . $x}) and push(@{$packet{'rtcp-mux'}}, $x); +for my $x (split(',', 'rtcp-mux,SDES')) { + defined($options{$x}) && ref($options{$x}) eq 'ARRAY' + and $packet{$x} = $options{$x}; } if (defined($options{direction})) { $options{direction} =~ /(.*),(.*)/ or die;