Skip to content

Commit

Permalink
srtp: allow rekeying of running streams (baresip#2975)
Browse files Browse the repository at this point in the history
* srtp: reset srtp_rx for changed remote crypto info

* srtp: transmission rekeying command via callid

* stream: api to remove media encrytion media object for rekeying

* srtp: rekeying testcase

* srtp: add missing header

* srtp: rework formating

* srtp: remove srtp rekeying command
  + move tx rekeying code into test case
  + rename srtp_remove_menc_media to srtp_remove_menc_media_state
  + allow menc_media_state deref only for SRTP module

* srtp: implement tx rekeying inside testcase

* stream: move menc media state removal function into core.h

* srtp: fix function argument list (formatting)
  • Loading branch information
cHuberCoffee authored Apr 10, 2024
1 parent 63570f5 commit 0029de1
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 7 deletions.
49 changes: 42 additions & 7 deletions modules/srtp/srtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* Copyright (C) 2010 Alfred E. Heggestad
*/
#include <string.h>
#include <re.h>
#include <re_atomic.h>
#include <baresip.h>
Expand Down Expand Up @@ -272,24 +273,51 @@ static int sdp_enc(struct menc_st *st, struct sdp_media *m,

static int start_crypto(struct menc_st *st, const struct pl *key_info)
{
size_t olen, len;
size_t olen = 0, len = 0;
char buf[64] = "";
int err;
uint8_t *new_key = NULL;
int err = 0;

len = get_master_keylen(resolve_suite(st->crypto_suite));

/* key-info is BASE64 encoded */
new_key = mem_zalloc(len, NULL);
if (!new_key)
return ENOMEM;

olen = sizeof(st->key_rx);
err = base64_decode(key_info->p, key_info->l, st->key_rx, &olen);
if (err)
olen = len;
err = base64_decode(key_info->p, key_info->l, new_key, &olen);
if (err) {
mem_deref(new_key);
return err;
}

if (len != olen) {
warning("srtp: %s: srtp keylen is %u (should be %zu)\n",
st->crypto_suite, olen, len);
warning("srtp: %s: %s: srtp keylen is %u (should be %zu)\n",
stream_name(st->strm), st->crypto_suite, olen, len);
mem_deref(new_key);
return err;
}

if (olen > sizeof(st->key_rx)) {
warning("srtp: %s: received key exceeds max key length\n",
stream_name(st->strm));
mem_deref(new_key);
return ERANGE;
}

/* receiving key-info changed -> reset srtp_rx */
if (st->srtp_rx && mem_seccmp(st->key_rx, new_key,
sizeof(st->key_rx) > olen ? olen : sizeof(st->key_rx))) {
info("srtp: %s: re-keying in progress\n",
stream_name(st->strm));
st->srtp_rx = mem_deref(st->srtp_rx);
}

memcpy(st->key_rx, new_key, olen);
mem_secclean(new_key, olen);
new_key = mem_deref(new_key);

err = start_srtp(st, st->crypto_suite);
if (err)
return err;
Expand Down Expand Up @@ -328,6 +356,13 @@ static bool sdp_attr_handler(const char *name, const char *value, void *arg)
if (!cryptosuite_issupported(&c.suite))
return false;

/* receiving crypto-suite changed -> reset srtp_rx */
if (st->srtp_rx && pl_strcmp(&c.suite, st->crypto_suite)) {
info ("srtp (%s-rx): cipher suite changed from %s to %r\n",
stream_name(st->strm), st->crypto_suite, &c.suite);
st->srtp_rx = mem_deref(st->srtp_rx);
}

st->crypto_suite = mem_deref(st->crypto_suite);
pl_strdup(&st->crypto_suite, &c.suite);

Expand Down
1 change: 1 addition & 0 deletions src/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ void stream_set_rtcp_interval(struct stream *s, uint32_t n);
void stream_set_srate(struct stream *s, uint32_t srate_tx, uint32_t srate_rx);
bool stream_is_ready(const struct stream *strm);
int stream_print(struct re_printf *pf, const struct stream *s);
void stream_remove_menc_media_state(struct stream *strm);
enum media_type stream_type(const struct stream *strm);
enum sdp_dir stream_ldir(const struct stream *s);
struct rtp_sock *stream_rtp_sock(const struct stream *strm);
Expand Down
19 changes: 19 additions & 0 deletions src/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,25 @@ int stream_update(struct stream *s)
}


/**
* Removes the media encryption state from a stream.
*
* Only apply if SRTP module is used!
*
* The encryption consists of 1 encryption session state and N encryption
* media states.
*
* @param strm Stream to remove the media encryption state.
*/
void stream_remove_menc_media_state(struct stream *strm)
{
if (!strm)
return;

strm->mes = mem_deref(strm->mes);
}


void stream_update_encoder(struct stream *s, int pt_enc)
{
if (!s)
Expand Down
160 changes: 160 additions & 0 deletions test/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -3066,3 +3066,163 @@ int test_call_hold_resume(void)
out:
return err;
}


static bool sdp_crypto_handler(const char *name, const char *value, void *arg)
{
char **key = arg;
struct pl key_info = PL_INIT, key_prms = PL_INIT;
int err = 0;

(void)name;

if (!str_isset(value))
return false;

err = re_regex(value, str_len(value), "[0-9]+ [^ ]+ [^ ]+[]*[^]*",
NULL, NULL, &key_prms, NULL, NULL);
if (err)
return false;

err = re_regex(key_prms.p, key_prms.l, "[^:]+:[^|]+[|]*[^|]*[|]*[^|]*",
NULL, &key_info, NULL, NULL, NULL, NULL);
if (err)
return false;

return 0 == pl_strdup(key, &key_info);
}


int test_call_srtp_tx_rekey(void)
{
struct fixture fix, *f = &fix;
struct cancel_rule *cr = NULL;
struct auplay *auplay = NULL;

char *a_rx_key = NULL, *a_tx_key = NULL;
char *b_rx_key = NULL, *b_tx_key = NULL;
char *a_rx_key_new = NULL, *a_tx_key_new = NULL;
char *b_rx_key_new = NULL, *b_tx_key_new = NULL;
int err = 0;

err = module_load(".", "srtp");
err |= module_load(".", "ausine");
TEST_ERR(err);

err = mock_auplay_register(&auplay, baresip_auplayl(),
auframe_handler, f);
TEST_ERR(err);

fixture_init_prm(f, ";mediaenc=srtp-mand"
";ptime=1;audio_player=mock-auplay,a");
f->b.ua = mem_deref(f->b.ua);
err = ua_alloc(&f->b.ua, "B <sip:b@127.0.0.1>;mediaenc=srtp-mand"
";regint=0;ptime=1;audio_player=mock-auplay,b");
TEST_ERR(err);

f->behaviour = BEHAVIOUR_ANSWER;
f->estab_action = ACTION_NOTHING;

/* call established cancel rule */
cancel_rule_new(UA_EVENT_CALL_ESTABLISHED, f->a.ua, 0, 0, 1);
cancel_rule_and(UA_EVENT_CALL_ESTABLISHED, f->b.ua, 1, 0, 1);

/* Call A to B */
err = ua_connect(f->a.ua, 0, NULL, f->buri, VIDMODE_ON);
TEST_ERR(err);

err = re_main_timeout(5000);
TEST_ERR(err);
TEST_ERR(fix.err);

/* verify audio was enabled and bi-directional */
ASSERT_TRUE(call_has_audio(ua_call(f->a.ua)));
ASSERT_TRUE(call_has_audio(ua_call(f->b.ua)));

struct sdp_media *m;
m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua))));
ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m));
ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m));
sdp_media_lattr_apply(m, "crypto", sdp_crypto_handler, &a_tx_key);
sdp_media_rattr_apply(m, "crypto", sdp_crypto_handler, &a_rx_key);

m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua))));
ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m));
ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m));
sdp_media_lattr_apply(m, "crypto", sdp_crypto_handler, &b_tx_key);
sdp_media_rattr_apply(m, "crypto", sdp_crypto_handler, &b_rx_key);

/* crosscheck rx & tx keys */
TEST_STRCMP(a_rx_key, str_len(a_rx_key), b_tx_key, str_len(b_tx_key));
TEST_STRCMP(a_tx_key, str_len(a_tx_key), b_rx_key, str_len(b_rx_key));

/* rekeying transmission keys from a -> b */
struct le *le = NULL;
for (le = call_streaml(ua_call(f->a.ua))->head; le; le = le->next)
stream_remove_menc_media_state(le->data);

err = call_update_media(ua_call(f->a.ua));
err |= call_modify(ua_call(f->a.ua));
TEST_ERR(err);

cancel_rule_new(UA_EVENT_CUSTOM, f->a.ua, 0, 0, 1);
cr->prm = "auframe";
cr->n_auframe = 10;
cancel_rule_and(UA_EVENT_CUSTOM, f->b.ua, 1, 0, 1);
cr->prm = "auframe";
cr->n_auframe = 10;

err = re_main_timeout(5000);
TEST_ERR(err);
TEST_ERR(fix.err);

m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->a.ua))));
ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m));
ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m));
sdp_media_lattr_apply(m, "crypto", sdp_crypto_handler, &a_tx_key_new);
sdp_media_rattr_apply(m, "crypto", sdp_crypto_handler, &a_rx_key_new);

m = stream_sdpmedia(audio_strm(call_audio(ua_call(f->b.ua))));
ASSERT_EQ(SDP_SENDRECV, sdp_media_ldir(m));
ASSERT_EQ(SDP_SENDRECV, sdp_media_rdir(m));
sdp_media_lattr_apply(m, "crypto", sdp_crypto_handler, &b_tx_key_new);
sdp_media_rattr_apply(m, "crypto", sdp_crypto_handler, &b_rx_key_new);

/* transmission key of a must change */
ASSERT_TRUE(0 != str_casecmp(a_tx_key, a_tx_key_new));

/* transmission key of b must stay the same */
TEST_STRCMP(b_tx_key, str_len(b_tx_key),
b_tx_key_new, str_len(b_tx_key_new));

/* receiving key of b must be the new tx key of a*/
TEST_STRCMP(b_rx_key_new, str_len(b_rx_key_new),
a_tx_key_new, str_len(a_tx_key_new));

/* transmission key of a must be the new rx key of b*/
TEST_STRCMP(a_tx_key_new, str_len(a_tx_key_new),
b_rx_key_new, str_len(b_rx_key_new));

out:
if (err)
failure_debug(f, false);

fixture_close(f);
mem_deref(auplay);

module_unload("ausine");
module_unload("srtp");

a_rx_key = mem_deref(a_rx_key);
a_tx_key = mem_deref(a_tx_key);
b_rx_key = mem_deref(b_rx_key);
b_tx_key = mem_deref(b_tx_key);

a_rx_key_new = mem_deref(a_rx_key_new);
a_tx_key_new = mem_deref(a_tx_key_new);
b_rx_key_new = mem_deref(b_rx_key_new);
b_tx_key_new = mem_deref(b_tx_key_new);


return err;
}
1 change: 1 addition & 0 deletions test/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ static const struct test tests[] = {
TEST(test_call_100rel_audio),
TEST(test_call_100rel_video),
TEST(test_call_hold_resume),
TEST(test_call_srtp_tx_rekey),
TEST(test_cmd),
TEST(test_cmd_long),
TEST(test_contact),
Expand Down
1 change: 1 addition & 0 deletions test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ int test_call_ipv6ll(void);
int test_call_100rel_audio(void);
int test_call_100rel_video(void);
int test_call_hold_resume(void);
int test_call_srtp_tx_rekey(void);
int test_cmd(void);
int test_cmd_long(void);
int test_contact(void);
Expand Down

0 comments on commit 0029de1

Please sign in to comment.