diff --git a/include/srtp.h b/include/srtp.h index 3f5a52475..6cb92c5c3 100644 --- a/include/srtp.h +++ b/include/srtp.h @@ -1765,6 +1765,24 @@ srtp_err_status_t srtp_set_stream_use_cryptex(srtp_t session, const srtp_ssrc_t *ssrc, int enable); +/** + * @brief srtp_set_stream_require_cryptex(session, ssrc, enable) + * + * Require cryptex, RFC 9335, processing for the stream identified by the given + * SSRC. For wildcard SSRC types the require cryptex setting is applied to the + * session template and any streams created from it. + * + * @param session is the SRTP session containing the stream to update. + * @param ssrc describes the SSRC to require cryptex for. + * @param enable whether to require sending and receiving cryptex. + * + * @returns srtp_err_status_ok on success, or srtp_err_status_bad_param if the + * stream or template cannot be found for the given SSRC. + */ +srtp_err_status_t srtp_set_stream_require_cryptex(srtp_t session, + const srtp_ssrc_t *ssrc, + int enable); + /** * @} */ diff --git a/include/srtp_priv.h b/include/srtp_priv.h index ee20d63c4..d88f813dc 100644 --- a/include/srtp_priv.h +++ b/include/srtp_priv.h @@ -146,6 +146,7 @@ typedef struct srtp_stream_ctx_t_ { int *enc_xtn_hdr; int enc_xtn_hdr_count; int use_cryptex; + int require_cryptex; uint32_t pending_roc; /* The next and prev pointers are here to allow for a stream list to be diff --git a/srtp/srtp.c b/srtp/srtp.c index 66bbde8a8..61da153d8 100644 --- a/srtp/srtp.c +++ b/srtp/srtp.c @@ -235,6 +235,10 @@ static srtp_err_status_t srtp_cryptex_unprotect_init( *inuse = 0; } + if (stream->require_cryptex && !*inuse && hdr->x == 1) { + return srtp_err_status_cryptex_err; + } + if (*inuse) { srtp_hdr_xtnd_t *xtn_hdr = srtp_get_rtp_xtn_hdr(hdr); *enc_start -= @@ -5128,6 +5132,7 @@ static int set_cryptex_from_template_cb(srtp_stream_t stream, void *raw_data) if (stream->session_keys[0].rtp_auth == data->template->session_keys[0].rtp_auth) { stream->use_cryptex = data->template->use_cryptex; + stream->require_cryptex = data->template->require_cryptex; } return 0; @@ -5171,6 +5176,45 @@ srtp_err_status_t srtp_set_stream_use_cryptex(srtp_t session, return srtp_err_status_ok; } +srtp_err_status_t srtp_set_stream_require_cryptex(srtp_t session, + const srtp_ssrc_t *ssrc, + int enable) +{ + srtp_stream_t stream; + + if (session == NULL || ssrc == NULL) { + return srtp_err_status_bad_param; + } + + switch (ssrc->type) { + case ssrc_specific: + stream = srtp_get_stream(session, htonl(ssrc->value)); + if (stream == NULL) { + return srtp_err_status_bad_param; + } + stream->require_cryptex = enable != 0; + break; + case ssrc_any_inbound: { + struct set_cryptex_from_template_data data; + + if (session->stream_template == NULL) { + return srtp_err_status_bad_param; + } + session->stream_template->require_cryptex = enable != 0; + data.template = session->stream_template; + srtp_stream_list_for_each(session->stream_list, + set_cryptex_from_template_cb, &data); + break; + } + case ssrc_any_outbound: + // Requiring cryptex is not possible for outbound SSRCs, fall through. + default: + return srtp_err_status_bad_param; + } + + return srtp_err_status_ok; +} + #ifndef SRTP_NO_STREAM_LIST /* in the default implementation, we have an intrusive doubly-linked list */ diff --git a/test/srtp_driver.c b/test/srtp_driver.c index a54e9624d..1bc4db39c 100644 --- a/test/srtp_driver.c +++ b/test/srtp_driver.c @@ -115,6 +115,8 @@ srtp_err_status_t srtp_test_cryptex_csrc_but_no_extension_header(void); srtp_err_status_t srtp_test_cryptex_disable(void); +srtp_err_status_t srtp_test_require_cryptex(void); + double srtp_bits_per_second(int msg_len_octets, const srtp_policy_t *policy); double srtp_rejections_per_second(int msg_len_octets, @@ -684,6 +686,14 @@ int main(int argc, char *argv[]) printf("failed\n"); exit(1); } + + printf("testing require_cryptex()..."); + if (srtp_test_require_cryptex() == srtp_err_status_ok) { + printf("passed\n"); + } else { + printf("failed\n"); + exit(1); + } } if (do_stream_list) { @@ -2707,6 +2717,47 @@ srtp_err_status_t srtp_test_cryptex_disable(void) return srtp_err_status_ok; } +srtp_err_status_t srtp_test_require_cryptex(void) +{ + srtp_policy_t policy; + memset(&policy, 0, sizeof(policy)); + srtp_crypto_policy_set_rtp_default(&policy.rtp); + srtp_crypto_policy_set_rtcp_default(&policy.rtcp); + policy.ssrc.type = ssrc_any_outbound; + policy.ssrc.value = 0xcafebabe; + policy.key = test_key; + policy.window_size = 128; + policy.allow_repeat_tx = 0; + policy.next = NULL; + + srtp_t srtp_snd, srtp_recv; + CHECK_OK(srtp_create(&srtp_snd, &policy)); + CHECK_OK(srtp_set_stream_use_cryptex(srtp_snd, &policy.ssrc, 0)); + /* + * requiring cryptex is not defined for the outbound wildcard ssrc. + */ + CHECK_RETURN(srtp_set_stream_require_cryptex(srtp_snd, &policy.ssrc, 1), + srtp_err_status_bad_param); + policy.ssrc.type = ssrc_any_inbound; + CHECK_OK(srtp_create(&srtp_recv, &policy)); + CHECK_OK(srtp_set_stream_require_cryptex(srtp_recv, &policy.ssrc, 1)); + + int packet_len; + srtp_hdr_t *packet = + srtp_create_test_packet_ext_hdr(100, policy.ssrc.value, &packet_len); + + CHECK_OK(srtp_protect(srtp_snd, packet, &packet_len)); + + CHECK_RETURN(srtp_unprotect(srtp_recv, packet, &packet_len), + srtp_err_status_cryptex_err); + + CHECK_OK(srtp_dealloc(srtp_snd)); + CHECK_OK(srtp_dealloc(srtp_recv)); + free(packet); + + return srtp_err_status_ok; +} + #ifdef GCM /* * srtp_validate_gcm() verifies the correctness of libsrtp by comparing