diff --git a/CMakeLists.txt b/CMakeLists.txt index e3de65d28..4dcc42389 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,6 +143,7 @@ endif() set(SOURCES_C srtp/srtp.c + srtp/srtp_policy.c ) set(CIPHERS_SOURCES_C @@ -471,6 +472,17 @@ if(LIBSRTP_TEST_APPS) endif() target_link_libraries(test_srtp srtp3) add_test(test_srtp test_srtp) + + add_executable(test_srtp_policy test/test_srtp_policy.c test/util.c) + target_set_warnings( + TARGET + test_srtp_policy + ENABLE + ${ENABLE_WARNINGS} + AS_ERRORS + ${ENABLE_WARNINGS_AS_ERRORS}) + target_link_libraries(test_srtp_policy srtp3) + add_test(test_srtp_policy test_srtp_policy) endif() find_program(BASH_PROGRAM bash) diff --git a/include/srtp.h b/include/srtp.h index 67cd19b95..96c68f19d 100644 --- a/include/srtp.h +++ b/include/srtp.h @@ -1090,6 +1090,7 @@ size_t srtp_profile_get_master_salt_length(srtp_profile_t profile); * @warning There must be at least bytes_in_salt + bytes_in_key bytes * available at the location pointed to by key. * + * */ void srtp_append_salt_to_key(uint8_t *key, size_t bytes_in_key, @@ -1492,6 +1493,44 @@ srtp_err_status_t srtp_stream_get_roc(srtp_t session, #define SRTCP_E_BYTE_BIT 0x80 #define SRTCP_INDEX_MASK 0x7fffffff +/* WIP new config policy API */ + +typedef struct srtp_policy2_ctx_t_ srtp_policy2_ctx_t; +typedef srtp_policy2_ctx_t *srtp_policy2_t; + +srtp_err_status_t srtp_policy2_create(srtp_policy2_t *policy); +srtp_err_status_t srtp_policy2_set_ssrc(srtp_policy2_t policy, + srtp_ssrc_t ssrc); +srtp_err_status_t srtp_policy2_set_profile(srtp_policy2_t policy, + srtp_profile_t profile); +srtp_err_status_t srtp_policy2_set_key(srtp_policy2_t policy, + const uint8_t *key, + size_t key_len, + const uint8_t *salt, + size_t salt_len); +srtp_err_status_t srtp_policy2_use_mki(srtp_policy2_t policy, size_t mki_len); +srtp_err_status_t srtp_policy2_add_key(srtp_policy2_t policy, + const uint8_t *key, + size_t key_len, + const uint8_t *salt, + size_t salt_len, + const uint8_t *mki, + size_t mki_len); +srtp_err_status_t srtp_policy2_set_window_size(srtp_policy2_t policy, + size_t window_size); +srtp_err_status_t srtp_policy2_set_allow_repeat_tx(srtp_policy2_t policy, + bool allow); +srtp_err_status_t srtp_policy2_use_cryptex(srtp_policy2_t policy); +srtp_err_status_t srtp_policy2_set_enc_hdr_xtnd_ids(srtp_policy2_t policy, + const uint8_t *hdr_xtnd_ids, + size_t num_xtnd_ids); +srtp_err_status_t srtp_policy2_set_roc(srtp_policy2_t policy, uint32_t roc); +void srtp_policy2_destroy(srtp_policy2_t policy); + +srtp_err_status_t srtp_policy2_validate(srtp_policy2_t policy); + +srtp_err_status_t srtp_create2(srtp_t *session, const srtp_policy2_t policy); + #ifdef __cplusplus } #endif diff --git a/include/srtp_priv.h b/include/srtp_priv.h index 72e87cd94..963b4bae6 100644 --- a/include/srtp_priv.h +++ b/include/srtp_priv.h @@ -67,6 +67,24 @@ typedef struct srtp_stream_ctx_t_ srtp_stream_ctx_t; typedef srtp_stream_ctx_t *srtp_stream_t; typedef struct srtp_stream_list_ctx_t_ *srtp_stream_list_t; +typedef struct srtp_master_key2_t { + uint8_t key[SRTP_MAX_KEY_LEN]; + size_t key_len; + uint8_t mki_id[SRTP_MAX_MKI_LEN]; + size_t mki_id_len; +} srtp_master2_key_t; +#define SRTP_MAX_NUM_HDR_XTND_IDS 16 +typedef struct srtp_policy2_ctx_t_ { + srtp_profile_t profile; + srtp_policy_t legacy; + srtp_master2_key_t master_key_store[SRTP_MAX_NUM_MASTER_KEYS]; + srtp_master_key_t master_keys[SRTP_MAX_NUM_MASTER_KEYS]; + srtp_master_key_t *keys[SRTP_MAX_NUM_MASTER_KEYS]; + uint8_t enc_hdr_xtnd_ids[SRTP_MAX_NUM_HDR_XTND_IDS]; +} srtp_policy2_ctx_t_; + +srtp_err_status_t srtp_valid_policy(const srtp_policy_t *policy); + /* * the following declarations are libSRTP internal functions */ diff --git a/srtp/srtp.c b/srtp/srtp.c index 250dabc95..4295078a8 100644 --- a/srtp/srtp.c +++ b/srtp/srtp.c @@ -554,7 +554,7 @@ static srtp_err_status_t srtp_remove_and_dealloc_streams( return data.status; } -static srtp_err_status_t srtp_valid_policy(const srtp_policy_t *policy) +srtp_err_status_t srtp_valid_policy(const srtp_policy_t *policy) { if (policy == NULL) { return srtp_err_status_bad_param; diff --git a/srtp/srtp_policy.c b/srtp/srtp_policy.c new file mode 100644 index 000000000..c6f8f2fd1 --- /dev/null +++ b/srtp/srtp_policy.c @@ -0,0 +1,235 @@ +/* + * srtp_policy.c + * + * extensible policy API for libSRTP + */ +/* + * + * Copyright (c) 2026 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "srtp_priv.h" + +#include + +#include "alloc.h" + +srtp_err_status_t srtp_policy2_create(srtp_policy2_t *policy) +{ + srtp_policy2_t p; + + if (policy == NULL) { + return srtp_err_status_bad_param; + } + + p = (srtp_policy2_t)srtp_crypto_alloc(sizeof(*p)); + if (p == NULL) { + *policy = NULL; + return srtp_err_status_alloc_fail; + } + + memset(p, 0, sizeof(*p)); + + // set up key store + for (size_t i = 0; i < SRTP_MAX_NUM_MASTER_KEYS; i++) { + p->master_keys[i].key = p->master_key_store[i].key; + p->master_keys[i].mki_id = p->master_key_store[i].mki_id; + p->keys[i] = &p->master_keys[i]; + } + p->legacy.keys = p->keys; + + // setup hdr xtnd id's + p->legacy.enc_xtn_hdr = p->enc_hdr_xtnd_ids; + + *policy = p; + + return srtp_err_status_ok; +} + +void srtp_policy2_destroy(srtp_policy2_t policy) +{ + if (policy == NULL) { + return; + } + + octet_string_set_to_zero(policy->keys, sizeof(policy->keys)); + srtp_crypto_free(policy); +} + +srtp_err_status_t srtp_policy2_validate(srtp_policy2_t policy) +{ + if (policy == NULL) { + return srtp_err_status_bad_param; + } + + if (policy->legacy.ssrc.type != ssrc_any_inbound && + policy->legacy.ssrc.type != ssrc_any_outbound && + policy->legacy.ssrc.type != ssrc_specific) { + return srtp_err_status_bad_param; + } + + if (policy->profile == srtp_profile_reserved) { + return srtp_err_status_bad_param; + } + + return srtp_valid_policy(&policy->legacy); +} + +srtp_err_status_t srtp_policy2_set_ssrc(srtp_policy2_t policy, srtp_ssrc_t ssrc) +{ + if (policy == NULL) { + return srtp_err_status_bad_param; + } + + if (ssrc.type != ssrc_any_inbound && ssrc.type != ssrc_any_outbound && + ssrc.type != ssrc_specific) { + return srtp_err_status_bad_param; + } + + policy->legacy.ssrc = ssrc; + + return srtp_err_status_ok; +} + +srtp_err_status_t srtp_policy2_set_profile(srtp_policy2_t policy, + srtp_profile_t profile) +{ + if (policy == NULL) { + return srtp_err_status_bad_param; + } + + srtp_err_status_t status; + status = srtp_crypto_policy_set_from_profile_for_rtp(&policy->legacy.rtp, + profile); + if (status != srtp_err_status_ok) { + return status; + } + status = srtp_crypto_policy_set_from_profile_for_rtcp(&policy->legacy.rtcp, + profile); + if (status != srtp_err_status_ok) { + return status; + } + + policy->profile = profile; + + return srtp_err_status_ok; +} + +static srtp_err_status_t policy2_add_key(srtp_policy2_t policy, + const uint8_t *key, + size_t key_len, + const uint8_t *salt, + size_t salt_len, + const uint8_t *mki, + size_t mki_len) +{ + if (policy->legacy.num_master_keys >= SRTP_MAX_NUM_MASTER_KEYS) { + return srtp_err_status_bad_param; + } + + if (key_len + salt_len > SRTP_MAX_KEY_LEN) { + return srtp_err_status_bad_param; + } + + size_t key_index = policy->legacy.num_master_keys; + memcpy(policy->master_key_store[key_index].key, key, key_len); + memcpy(policy->master_key_store[key_index].key + key_len, salt, salt_len); + policy->master_key_store[key_index].key_len = key_len + salt_len; + memcpy(policy->master_key_store[key_index].mki_id, mki, mki_len); + policy->master_key_store[key_index].mki_id_len = mki_len; + + policy->legacy.num_master_keys++; + + return srtp_err_status_ok; +} + +srtp_err_status_t srtp_policy2_set_key(srtp_policy2_t policy, + const uint8_t *key, + size_t key_len, + const uint8_t *salt, + size_t salt_len) +{ + if (policy == NULL) { + return srtp_err_status_bad_param; + } + + if (policy->legacy.num_master_keys != 0) { + return srtp_err_status_bad_param; + } + + if (key_len + salt_len > SRTP_MAX_KEY_LEN) { + return srtp_err_status_bad_param; + } + + policy->legacy.use_mki = false; + policy->legacy.mki_size = 0; + + return policy2_add_key(policy, key, key_len, salt, salt_len, NULL, 0); +} + +srtp_err_status_t srtp_policy2_use_mki(srtp_policy2_t policy, size_t mki_len) +{ + if (policy == NULL) { + return srtp_err_status_bad_param; + } + + if (mki_len > SRTP_MAX_MKI_LEN) { + return srtp_err_status_bad_param; + } + + policy->legacy.use_mki = true; + policy->legacy.mki_size = mki_len; + + return srtp_err_status_ok; +} + +srtp_err_status_t srtp_policy2_add_key(srtp_policy2_t policy, + const uint8_t *key, + size_t key_len, + const uint8_t *salt, + size_t salt_len, + const uint8_t *mki, + size_t mki_len) +{ + if (policy == NULL) { + return srtp_err_status_bad_param; + } + + if (!policy->legacy.use_mki) { + return srtp_err_status_bad_param; + } + + return policy2_add_key(policy, key, key_len, salt, salt_len, mki, mki_len); +} + +srtp_err_status_t srtp_create2(srtp_t *session, const srtp_policy2_t policy) +{ + return srtp_create(session, policy ? &policy->legacy : NULL); +} diff --git a/test/test_srtp_policy.c b/test/test_srtp_policy.c new file mode 100644 index 000000000..11cc68aba --- /dev/null +++ b/test/test_srtp_policy.c @@ -0,0 +1,152 @@ +/* + * test_srtp_policy.c + * + * Unit tests for srtp_policy2 API + * + */ +/* + * + * Copyright (c) 2026 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "cutest.h" + +#include "srtp.h" +#include "util.h" + +static void srtp_policy2_create_destroy_ok(void) +{ + srtp_policy2_t policy; + + CHECK_RETURN(srtp_policy2_create(&policy), srtp_err_status_ok); + CHECK(policy != NULL); + + srtp_policy2_destroy(policy); +} + +static void srtp_policy2_empty_not_valid(void) +{ + srtp_policy2_t policy; + + CHECK_RETURN(srtp_policy2_create(&policy), srtp_err_status_ok); + + CHECK_RETURN(srtp_policy2_validate(policy), srtp_err_status_bad_param); + + srtp_policy2_destroy(policy); +} + +static void srtp_policy2_minimal(void) +{ + // clang-format off + uint8_t master_key[16] = { + 0xe1, 0xf9, 0x7a, 0x0d, 0x3e, 0x01, 0x8b, 0xe0, + 0xd6, 0x4f, 0xa3, 0x2c, 0x06, 0xde, 0x41, 0x39, + }; + uint8_t master_salt[14] = { + 0xc3, 0x17, 0xf2, 0xda, 0xbe, 0x35, 0x77, 0x93, + 0xb6, 0x96, 0x0b, 0x3a, 0xab, 0xe6 + }; + // clang-format on + + srtp_policy2_t policy; + + CHECK_RETURN(srtp_policy2_create(&policy), srtp_err_status_ok); + + CHECK_RETURN( + srtp_policy2_set_ssrc(policy, (srtp_ssrc_t){ ssrc_any_outbound, 0 }), + srtp_err_status_ok); + CHECK_RETURN( + srtp_policy2_set_profile(policy, srtp_profile_aes128_cm_sha1_80), + srtp_err_status_ok); + CHECK_RETURN(srtp_policy2_set_key(policy, master_key, sizeof(master_key), + master_salt, sizeof(master_salt)), + srtp_err_status_ok); + + CHECK_RETURN(srtp_policy2_validate(policy), srtp_err_status_ok); + + srtp_t srtp; + srtp_init(); + CHECK_RETURN(srtp_create2(&srtp, policy), srtp_err_status_ok); + srtp_dealloc(srtp); + srtp_shutdown(); + + srtp_policy2_destroy(policy); +} + +static void srtp_policy2_mki(void) +{ + // clang-format off + uint8_t master_key[16] = { + 0xe1, 0xf9, 0x7a, 0x0d, 0x3e, 0x01, 0x8b, 0xe0, + 0xd6, 0x4f, 0xa3, 0x2c, 0x06, 0xde, 0x41, 0x39, + }; + uint8_t master_salt[14] = { + 0xc3, 0x17, 0xf2, 0xda, 0xbe, 0x35, 0x77, 0x93, + 0xb6, 0x96, 0x0b, 0x3a, 0xab, 0xe6 + }; + uint8_t mki[4] = { + 0x01, 0x02, 0x03, 0x04 + }; + // clang-format on + + srtp_policy2_t policy; + + CHECK_RETURN(srtp_policy2_create(&policy), srtp_err_status_ok); + + CHECK_RETURN( + srtp_policy2_set_ssrc(policy, (srtp_ssrc_t){ ssrc_any_outbound, 0 }), + srtp_err_status_ok); + CHECK_RETURN( + srtp_policy2_set_profile(policy, srtp_profile_aes128_cm_sha1_80), + srtp_err_status_ok); + CHECK_RETURN(srtp_policy2_use_mki(policy, sizeof(mki)), srtp_err_status_ok); + CHECK_RETURN(srtp_policy2_add_key(policy, master_key, sizeof(master_key), + master_salt, sizeof(master_salt), mki, + sizeof(mki)), + srtp_err_status_ok); + + CHECK_RETURN(srtp_policy2_validate(policy), srtp_err_status_ok); + + srtp_t srtp; + srtp_init(); + CHECK_RETURN(srtp_create2(&srtp, policy), srtp_err_status_ok); + srtp_dealloc(srtp); + srtp_shutdown(); + + srtp_policy2_destroy(policy); +} + +TEST_LIST = { + { "srtp_policy2_create_destroy_ok()", srtp_policy2_create_destroy_ok }, + { "srtp_policy2_empty_not_valid()", srtp_policy2_empty_not_valid }, + { "srtp_policy2_minimal()", srtp_policy2_minimal }, + { "srtp_policy2_mki()", srtp_policy2_mki }, + { 0 } +};