--- configure.ac (Asterisk 13.21-cert6) +++ configure.ac (working copy) @@ -561,2 +561,5 @@ AST_EXT_LIB_SETUP([SRTP], [Secure RTP], [srtp]) +AST_EXT_LIB_SETUP_OPTIONAL([SRTP_256], [AES-256 (ICM)], [SRTP], [srtp]) +AST_EXT_LIB_SETUP_OPTIONAL([SRTP_192], [AES-192 (ICM)], [SRTP], [srtp]) +AST_EXT_LIB_SETUP_OPTIONAL([SRTP_GCM], [AES-128 (GCM) and AES-256 (GCM)], [SRTP], [srtp]) AST_EXT_LIB_SETUP_OPTIONAL([SRTP_SHUTDOWN], [SRTP Library Shutdown Function], [SRTP], [srtp]) @@ -2480,2 +2483,5 @@ then + AST_EXT_LIB_CHECK([SRTP_256], [srtp2], [srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80]) + AST_EXT_LIB_CHECK([SRTP_192], [srtp2], [srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80]) + AST_EXT_LIB_CHECK([SRTP_GCM], [srtp2], [srtp_crypto_policy_set_aes_gcm_128_8_auth]) AST_EXT_LIB_CHECK([SRTP_SHUTDOWN], [srtp2], [srtp_shutdown], [srtp2/srtp.h]) @@ -2511,2 +2517,5 @@ then + AST_EXT_LIB_CHECK([SRTP_256], [srtp], [crypto_policy_set_aes_cm_256_hmac_sha1_80]) + AST_EXT_LIB_CHECK([SRTP_192], [srtp], [crypto_policy_set_aes_cm_192_hmac_sha1_80]) + AST_EXT_LIB_CHECK([SRTP_GCM], [srtp], [aes_gcm_128_openssl]) AST_EXT_LIB_CHECK([SRTP_SHUTDOWN], [srtp], [srtp_shutdown], [srtp/srtp.h]) --- include/asterisk/res_srtp.h (Asterisk 13.21-cert6) +++ include/asterisk/res_srtp.h (working copy) @@ -22,4 +22,6 @@ #define _ASTERISK_RES_SRTP_H +#include "asterisk.h" /* for size_t */ + struct ast_srtp; struct ast_srtp_policy; @@ -54,5 +56,13 @@ enum ast_srtp_suite { + /* https://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml */ AST_AES_CM_128_HMAC_SHA1_80 = 1, - AST_AES_CM_128_HMAC_SHA1_32 = 2, - AST_F8_128_HMAC_SHA1_80 = 3 + AST_AES_CM_128_HMAC_SHA1_32, + AST_AES_CM_256_HMAC_SHA1_80, + AST_AES_CM_256_HMAC_SHA1_32, + AST_AES_GCM_128 = 7, + AST_AES_GCM_256, + AST_AES_GCM_128_8, + AST_AES_GCM_256_8, + AST_AES_CM_192_HMAC_SHA1_80, + AST_AES_CM_192_HMAC_SHA1_32, }; --- include/asterisk/sdp_srtp.h (Asterisk 13.21-cert6) +++ include/asterisk/sdp_srtp.h (working copy) @@ -22,4 +22,3 @@ * - * Specified in RFC 4568 - * Specified in RFC 3711 + * Specified in RFC 3711, 6188, 7714, and 4568 * @@ -31,3 +30,4 @@ -#include +#include "asterisk/linkedlists.h" /* for AST_LIST_ENTRY */ +#include "asterisk/rtp_engine.h" /* for ast_rtp_instance */ @@ -39,2 +39,3 @@ struct ast_sdp_crypto *crypto; + AST_LIST_ENTRY(ast_sdp_srtp) sdp_srtp_list; }; @@ -42,5 +43,10 @@ /* SRTP flags */ -#define AST_SRTP_CRYPTO_OFFER_OK (1 << 1) -#define AST_SRTP_CRYPTO_TAG_32 (1 << 2) -#define AST_SRTP_CRYPTO_TAG_80 (1 << 3) +#define AST_SRTP_CRYPTO_OFFER_OK (1 << 1) +#define AST_SRTP_CRYPTO_TAG_32 (1 << 2) +#define AST_SRTP_CRYPTO_TAG_80 (1 << 3) +#define AST_SRTP_CRYPTO_TAG_16 (1 << 4) +#define AST_SRTP_CRYPTO_TAG_8 (1 << 5) +#define AST_SRTP_CRYPTO_AES_192 (1 << 6) +#define AST_SRTP_CRYPTO_AES_256 (1 << 7) +#define AST_SRTP_CRYPTO_OLD_NAME (1 << 8) --- res/res_srtp.c (Asterisk 13.21-cert6) +++ res/res_srtp.c (working copy) @@ -38 +38 @@ -#include "asterisk.h" +#include "asterisk.h" /* for NULL, size_t, memcpy, etc */ @@ -55,6 +55,7 @@ -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/module.h" -#include "asterisk/options.h" -#include "asterisk/rtp_engine.h" -#include "asterisk/astobj2.h" +#include "asterisk/astobj2.h" /* for ao2_t_ref, etc */ +#include "asterisk/frame.h" /* for AST_FRIENDLY_OFFSET */ +#include "asterisk/logger.h" /* for ast_log, ast_debug, etc */ +#include "asterisk/module.h" /* for ast_module_info, etc */ +#include "asterisk/res_srtp.h" /* for ast_srtp_cb, ast_srtp_suite, etc */ +#include "asterisk/rtp_engine.h" /* for ast_rtp_engine_register_srtp, etc */ +#include "asterisk/utils.h" /* for ast_free, ast_calloc */ @@ -264,21 +265,47 @@ switch (suite) { case AST_AES_CM_128_HMAC_SHA1_80: - p->cipher_type = AES_128_ICM; - p->cipher_key_len = 30; - p->auth_type = HMAC_SHA1; - p->auth_key_len = 20; - p->auth_tag_len = 10; - p->sec_serv = sec_serv_conf_and_auth; + crypto_policy_set_aes_cm_128_hmac_sha1_80(p); return 0; case AST_AES_CM_128_HMAC_SHA1_32: - p->cipher_type = AES_128_ICM; - p->cipher_key_len = 30; - p->auth_type = HMAC_SHA1; - p->auth_key_len = 20; - p->auth_tag_len = 4; - p->sec_serv = sec_serv_conf_and_auth; + crypto_policy_set_aes_cm_128_hmac_sha1_32(p); return 0; +#ifdef HAVE_SRTP_192 + case AST_AES_CM_192_HMAC_SHA1_80: + crypto_policy_set_aes_cm_192_hmac_sha1_80(p); + return 0; + + case AST_AES_CM_192_HMAC_SHA1_32: + crypto_policy_set_aes_cm_192_hmac_sha1_32(p); + return 0; +#endif +#ifdef HAVE_SRTP_256 + case AST_AES_CM_256_HMAC_SHA1_80: + crypto_policy_set_aes_cm_256_hmac_sha1_80(p); + return 0; + + case AST_AES_CM_256_HMAC_SHA1_32: + crypto_policy_set_aes_cm_256_hmac_sha1_32(p); + return 0; +#endif +#ifdef HAVE_SRTP_GCM + case AST_AES_GCM_128: + crypto_policy_set_aes_gcm_128_16_auth(p); + return 0; + + case AST_AES_GCM_256: + crypto_policy_set_aes_gcm_256_16_auth(p); + return 0; + + case AST_AES_GCM_128_8: + crypto_policy_set_aes_gcm_128_8_auth(p); + return 0; + + case AST_AES_GCM_256_8: + crypto_policy_set_aes_gcm_256_8_auth(p); + return 0; +#endif + default: ast_log(LOG_ERROR, "Invalid crypto suite: %u\n", suite); --- channels/chan_sip.c (Asterisk 13.21-cert6) +++ channels/chan_sip.c (working copy) @@ -288,2 +288,3 @@ #include "asterisk/format_cache.h" +#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT */ @@ -13370,19 +13371,32 @@ static char *crypto_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32) { + struct ast_sdp_srtp *tmp = srtp; char *a_crypto; - const char *orig_crypto; - if (!srtp || dtls_enabled) { + if (!tmp || dtls_enabled) { return NULL; } - orig_crypto = ast_sdp_srtp_get_attrib(srtp, dtls_enabled, default_taglen_32); - if (ast_strlen_zero(orig_crypto)) { + a_crypto = ast_strdup(""); + if (!a_crypto) { return NULL; } - if (ast_asprintf(&a_crypto, "a=crypto:%s\r\n", orig_crypto) == -1) { - return NULL; - } + do { + char *copy = a_crypto; + const char *orig_crypto = ast_sdp_srtp_get_attrib(tmp, dtls_enabled, default_taglen_32); + + if (ast_strlen_zero(orig_crypto)) { + ast_free(copy); + return NULL; + } + if (ast_asprintf(&a_crypto, "%sa=crypto:%s\r\n", copy, orig_crypto) == -1) { + ast_free(copy); + return NULL; + } + + ast_free(copy); + } while ((tmp = AST_LIST_NEXT(tmp, sdp_srtp_list))); + return a_crypto; } --- res/res_pjsip_sdp_rtp.c (Asterisk 13.21-cert6) +++ res/res_pjsip_sdp_rtp.c (working copy) @@ -54,2 +54,3 @@ #include "asterisk/utils.h" +#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT */ @@ -1050,2 +1051,3 @@ struct ast_rtp_engine_dtls *dtls; + struct ast_sdp_srtp *tmp; static const pj_str_t STR_NEW = { "new", 3 }; @@ -1069,12 +1071,18 @@ - crypto_attribute = ast_sdp_srtp_get_attrib(session_media->srtp, - 0 /* DTLS running? No */, - session->endpoint->media.rtp.srtp_tag_32 /* 32 byte tag length? */); - if (!crypto_attribute) { - /* No crypto attribute to add, bad news */ - return -1; - } + tmp = session_media->srtp; + + do { + crypto_attribute = ast_sdp_srtp_get_attrib(tmp, + 0 /* DTLS running? No */, + session->endpoint->media.rtp.srtp_tag_32 /* 32 byte tag length? */); + if (!crypto_attribute) { + /* No crypto attribute to add, bad news */ + return -1; + } + + attr = pjmedia_sdp_attr_create(pool, "crypto", + pj_cstr(&stmp, crypto_attribute)); + media->attr[media->attr_count++] = attr; + } while ((tmp = AST_LIST_NEXT(tmp, sdp_srtp_list))); - attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute)); - media->attr[media->attr_count++] = attr; break; --- main/sdp_srtp.c (Asterisk 13.21-cert6) +++ main/sdp_srtp.c (working copy) @@ -21,6 +21,5 @@ * \brief SRTP and SDP Security descriptions * - * Specified in RFC 3711 - * Specified in RFC 4568 + * Specified in RFC 3711, 6188, 7714, and 4568 * * \author Mikael Magnusson @@ -35,13 +34,18 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include -#include "asterisk/options.h" -#include "asterisk/utils.h" -#include "asterisk/sdp_srtp.h" - -#define SRTP_MASTER_LEN 30 -#define SRTP_MASTERKEY_LEN 16 -#define SRTP_MASTERSALT_LEN ((SRTP_MASTER_LEN) - (SRTP_MASTERKEY_LEN)) -#define SRTP_MASTER_LEN64 (((SRTP_MASTER_LEN) * 8 + 5) / 6 + 1) +#include /* for pow */ +#if HAVE_SRTP_VERSION > 1 +#include /* for SRTP_MAX_KEY_LEN, etc */ +#include "../res/srtp/srtp_compat.h" +#else +#include /* for SRTP_MAX_KEY_LEN, etc */ +#endif + +#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT, etc */ +#include "asterisk/logger.h" /* for ast_log, LOG_ERROR, etc */ +#include "asterisk/rtp_engine.h" /* for ast_rtp_engine_dtls, etc */ +#include "asterisk/sdp_srtp.h" /* for ast_sdp_srtp, etc */ +#include "asterisk/strings.h" /* for ast_strlen_zero */ +#include "asterisk/utils.h" /* for ast_set_flag, ast_test_flag, etc */ extern struct ast_srtp_res *res_srtp; @@ -60,20 +64,29 @@ void ast_sdp_srtp_destroy(struct ast_sdp_srtp *srtp) { - if (srtp->crypto) { - ast_sdp_crypto_destroy(srtp->crypto); + struct ast_sdp_srtp *next; + + for (next = AST_LIST_NEXT(srtp, sdp_srtp_list); + srtp; + srtp = next, next = srtp ? AST_LIST_NEXT(srtp, sdp_srtp_list) : NULL) { + if (srtp->crypto) { + ast_sdp_crypto_destroy(srtp->crypto); + } + srtp->crypto = NULL; + ast_free(srtp); } - srtp->crypto = NULL; - ast_free(srtp); } struct ast_sdp_crypto { char *a_crypto; - unsigned char local_key[SRTP_MASTER_LEN]; + unsigned char local_key[SRTP_MAX_KEY_LEN]; int tag; - char local_key64[SRTP_MASTER_LEN64]; - unsigned char remote_key[SRTP_MASTER_LEN]; + char local_key64[((SRTP_MAX_KEY_LEN) * 8 + 5) / 6 + 1]; + unsigned char remote_key[SRTP_MAX_KEY_LEN]; + int key_len; }; -static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, unsigned long ssrc, int inbound); +struct ast_sdp_crypto *sdp_crypto_alloc(const int key_len); +struct ast_sdp_crypto *int_crypto_alloc(struct ast_sdp_crypto *p, const int key_len); +static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, int key_len, unsigned long ssrc, int inbound); void ast_sdp_crypto_destroy(struct ast_sdp_crypto *crypto) @@ -84,55 +97,65 @@ } -struct ast_sdp_crypto *ast_sdp_crypto_alloc(void) +struct ast_sdp_crypto *int_crypto_alloc(struct ast_sdp_crypto *p, const int key_len) { - struct ast_sdp_crypto *p; - int key_len; - unsigned char remote_key[SRTP_MASTER_LEN]; + unsigned char remote_key[key_len]; - if (!ast_rtp_engine_srtp_is_registered()) { + if (res_srtp->get_random(p->local_key, key_len) < 0) { return NULL; } - if (!(p = ast_calloc(1, sizeof(*p)))) { + ast_base64encode(p->local_key64, p->local_key, key_len, sizeof(p->local_key64)); + + p->key_len = ast_base64decode(remote_key, p->local_key64, sizeof(remote_key)); + + if (p->key_len != key_len) { + ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", p->key_len, key_len); return NULL; } - p->tag = 1; - if (res_srtp->get_random(p->local_key, sizeof(p->local_key)) < 0) { - ast_sdp_crypto_destroy(p); + if (memcmp(remote_key, p->local_key, p->key_len)) { + ast_log(LOG_ERROR, "base64 encode/decode bad key\n"); return NULL; } - ast_base64encode(p->local_key64, p->local_key, SRTP_MASTER_LEN, sizeof(p->local_key64)); + ast_debug(1 , "local_key64 %s len %zu\n", p->local_key64, strlen(p->local_key64)); - key_len = ast_base64decode(remote_key, p->local_key64, sizeof(remote_key)); + return p; +} - if (key_len != SRTP_MASTER_LEN) { - ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", key_len, SRTP_MASTER_LEN); - ast_sdp_crypto_destroy(p); +struct ast_sdp_crypto *sdp_crypto_alloc(const int key_len) +{ + struct ast_sdp_crypto *p, *result; + + if (!ast_rtp_engine_srtp_is_registered()) { return NULL; } - if (memcmp(remote_key, p->local_key, SRTP_MASTER_LEN)) { - ast_log(LOG_ERROR, "base64 encode/decode bad key\n"); - ast_sdp_crypto_destroy(p); + if (!(p = ast_calloc(1, sizeof(*p)))) { return NULL; } + p->tag = 1; - ast_debug(1 , "local_key64 %s len %zu\n", p->local_key64, strlen(p->local_key64)); + /* default is a key which uses AST_AES_CM_128_HMAC_SHA1_xx */ + result = int_crypto_alloc(p, key_len); + if (!result) { + ast_sdp_crypto_destroy(p); + } - return p; + return result; } -static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, unsigned long ssrc, int inbound) +struct ast_sdp_crypto *ast_sdp_crypto_alloc(void) { - const unsigned char *master_salt = NULL; + return sdp_crypto_alloc(SRTP_MASTER_KEY_LEN); +} +static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, int key_len, unsigned long ssrc, int inbound) +{ if (!ast_rtp_engine_srtp_is_registered()) { return -1; } - master_salt = master_key + SRTP_MASTERKEY_LEN; - if (res_srtp_policy->set_master_key(policy, master_key, SRTP_MASTERKEY_LEN, master_salt, SRTP_MASTERSALT_LEN) < 0) { + if (res_srtp_policy->set_master_key(policy, master_key, key_len, NULL, 0) < 0) { return -1; } @@ -148,5 +171,5 @@ } -static int crypto_activate(struct ast_sdp_crypto *p, int suite_val, unsigned char *remote_key, struct ast_rtp_instance *rtp) +static int crypto_activate(struct ast_sdp_crypto *p, int suite_val, unsigned char *remote_key, int key_len, struct ast_rtp_instance *rtp) { struct ast_srtp_policy *local_policy = NULL; @@ -175,9 +198,9 @@ } - if (set_crypto_policy(local_policy, suite_val, p->local_key, stats.local_ssrc, 0) < 0) { + if (set_crypto_policy(local_policy, suite_val, p->local_key, key_len, stats.local_ssrc, 0) < 0) { goto err; } - if (set_crypto_policy(remote_policy, suite_val, remote_key, 0, 1) < 0) { + if (set_crypto_policy(remote_policy, suite_val, remote_key, key_len, 0, 1) < 0) { goto err; } @@ -216,10 +239,13 @@ char *mki = NULL; /* Master Key Index */ int found = 0; - int key_len = 0; + int key_len_from_sdp; + int key_len_expected; + int tag_from_sdp; int suite_val = 0; - unsigned char remote_key[SRTP_MASTER_LEN]; - int taglen = 0; + unsigned char remote_key[SRTP_MAX_KEY_LEN]; + int taglen; double sdes_lifetime; - struct ast_sdp_crypto *crypto = srtp->crypto; + struct ast_sdp_crypto *crypto; + struct ast_sdp_srtp *tmp; if (!ast_rtp_engine_srtp_is_registered()) { @@ -240,5 +266,5 @@ /* RFC4568 9.1 - tag is 1-9 digits */ - if (sscanf(tag, "%30d", &crypto->tag) != 1 || crypto->tag < 0 || crypto->tag > 999999999) { + if (sscanf(tag, "%30d", &tag_from_sdp) != 1 || tag_from_sdp < 0 || tag_from_sdp > 999999999) { ast_log(LOG_WARNING, "Unacceptable a=crypto tag: %s\n", tag); return -1; @@ -250,14 +276,113 @@ } + /* On egress, Asterisk sent several crypto lines in the SIP/SDP offer + The remote party might have choosen another line than the first */ + for (tmp = srtp; tmp && tmp->crypto && tmp->crypto->tag != tag_from_sdp;) { + tmp = AST_LIST_NEXT(tmp, sdp_srtp_list); + } + if (tmp) { /* tag matched an already created crypto line */ + unsigned int flags = tmp->flags; + + /* Make that crypto line the head of the list, not by changing the + list structure but by exchanging the content of the list members */ + crypto = tmp->crypto; + tmp->crypto = srtp->crypto; + tmp->flags = srtp->flags; + srtp->crypto = crypto; + srtp->flags = flags; + } else { + crypto = srtp->crypto; + crypto->tag = tag_from_sdp; + } + + ast_clear_flag(srtp, AST_SRTP_CRYPTO_TAG_8); + ast_clear_flag(srtp, AST_SRTP_CRYPTO_TAG_16); + ast_clear_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_clear_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_clear_flag(srtp, AST_SRTP_CRYPTO_AES_192); + ast_clear_flag(srtp, AST_SRTP_CRYPTO_AES_256); + ast_clear_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_80")) { suite_val = AST_AES_CM_128_HMAC_SHA1_80; ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); - taglen = 80; + key_len_expected = 30; } else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_32")) { suite_val = AST_AES_CM_128_HMAC_SHA1_32; ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); - taglen = 32; + key_len_expected = 30; +#ifdef HAVE_SRTP_192 + } else if (!strcmp(suite, "AES_192_CM_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + key_len_expected = 38; + } else if (!strcmp(suite, "AES_192_CM_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + key_len_expected = 38; + /* RFC used a different name while in draft, some still use that */ + } else if (!strcmp(suite, "AES_CM_192_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 38; + } else if (!strcmp(suite, "AES_CM_192_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 38; +#endif +#ifdef HAVE_SRTP_256 + } else if (!strcmp(suite, "AES_256_CM_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = 46; + } else if (!strcmp(suite, "AES_256_CM_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = 46; + /* RFC used a different name while in draft, some still use that */ + } else if (!strcmp(suite, "AES_CM_256_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 46; + } else if (!strcmp(suite, "AES_CM_256_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 46; +#endif +#ifdef HAVE_SRTP_GCM + } else if (!strcmp(suite, "AEAD_AES_128_GCM")) { + suite_val = AST_AES_GCM_128; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_16); + key_len_expected = AES_128_GCM_KEYSIZE_WSALT; + } else if (!strcmp(suite, "AEAD_AES_256_GCM")) { + suite_val = AST_AES_GCM_256; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_16); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = AES_256_GCM_KEYSIZE_WSALT; + /* RFC contained a (too) short auth tag for RTP media, some still use that */ + } else if (!strcmp(suite, "AEAD_AES_128_GCM_8")) { + suite_val = AST_AES_GCM_128_8; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_8); + key_len_expected = AES_128_GCM_KEYSIZE_WSALT; + } else if (!strcmp(suite, "AEAD_AES_256_GCM_8")) { + suite_val = AST_AES_GCM_256_8; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_8); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = AES_256_GCM_KEYSIZE_WSALT; +#endif } else { - ast_log(LOG_WARNING, "Unsupported crypto suite: %s\n", suite); + ast_verb(1, "Unsupported crypto suite: %s\n", suite); return -1; } @@ -343,6 +468,6 @@ - key_len = ast_base64decode(remote_key, key_salt, sizeof(remote_key)); - if (key_len != SRTP_MASTER_LEN) { - ast_log(LOG_WARNING, "SRTP descriptions key length '%d' != master length '%d'\n", - key_len, SRTP_MASTER_LEN); + key_len_from_sdp = ast_base64decode(remote_key, key_salt, sizeof(remote_key)); + if (key_len_from_sdp != key_len_expected) { + ast_log(LOG_WARNING, "SRTP descriptions key length is '%d', not '%d'\n", + key_len_from_sdp, key_len_expected); return -1; @@ -350,3 +475,10 @@ - if (!memcmp(crypto->remote_key, remote_key, sizeof(crypto->remote_key))) { + /* on default, the key is 30 (AES-128); throw that away (only) when the suite changed actually */ + /* ingress: optional, but saves one expensive call to get_random(.) */ + /* egress: required, because the local key was communicated before the remote key is processed */ + if (crypto->key_len != key_len_from_sdp) { + if (!int_crypto_alloc(crypto, key_len_from_sdp)) { + return -1; + } + } else if (!memcmp(crypto->remote_key, remote_key, key_len_from_sdp)) { ast_debug(1, "SRTP remote key unchanged; maintaining current policy\n"); @@ -355,5 +487,12 @@ } - memcpy(crypto->remote_key, remote_key, sizeof(crypto->remote_key)); - if (crypto_activate(crypto, suite_val, remote_key, rtp) < 0) { + if (key_len_from_sdp > sizeof(crypto->remote_key)) { + ast_log(LOG_ERROR, + "SRTP key buffer is %zu although it must be at least %d bytes\n", + sizeof(crypto->remote_key), key_len_from_sdp); + return -1; + } + memcpy(crypto->remote_key, remote_key, key_len_from_sdp); + + if (crypto_activate(crypto, suite_val, remote_key, key_len_from_sdp, rtp) < 0) { return -1; @@ -361,2 +500,20 @@ + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_32)) { + taglen = 32; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_16)) { + taglen = 16; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_8)) { + taglen = 8; + } else { + taglen = 80; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_256)) { + taglen |= 0x0200; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_192)) { + taglen |= 0x0100; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME)) { + taglen |= 0x0080; + } + /* Finally, rebuild the crypto line */ @@ -376,8 +533,28 @@ } - if (ast_asprintf(&p->a_crypto, "%d AES_CM_128_HMAC_SHA1_%i inline:%s", - p->tag, taglen, p->local_key64) == -1) { - ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); - return -1; + if ((taglen & 0x007f) == 8) { + if (ast_asprintf(&p->a_crypto, "%d AEAD_AES_%d_GCM_%d inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), taglen & 0x007f, p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } + } else if ((taglen & 0x007f) == 16) { + if (ast_asprintf(&p->a_crypto, "%d AEAD_AES_%d_GCM inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } + } else if ((taglen & 0x0300) && !(taglen & 0x0080)) { + if (ast_asprintf(&p->a_crypto, "%d AES_%d_CM_HMAC_SHA1_%d inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), taglen & 0x007f, p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } + } else { + if (ast_asprintf(&p->a_crypto, "%d AES_CM_%d_HMAC_SHA1_%d inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), taglen & 0x007f, p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } } @@ -389,5 +566,5 @@ const char *ast_sdp_srtp_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32) { - int taglen = default_taglen_32 ? 32 : 80; + int taglen; if (!srtp) { @@ -397,5 +574,70 @@ /* Set encryption properties */ if (!srtp->crypto) { - srtp->crypto = ast_sdp_crypto_alloc(); + + if (AST_LIST_NEXT(srtp, sdp_srtp_list)) { + srtp->crypto = ast_sdp_crypto_alloc(); + ast_log(LOG_ERROR, "SRTP SDP list was not empty\n"); + } else { + const int len = default_taglen_32 ? AST_SRTP_CRYPTO_TAG_32 : AST_SRTP_CRYPTO_TAG_80; + const int attr[][3] = { + /* This array creates the following list: + * a=crypto:1 AES_CM_128_HMAC_SHA1_ ... + * a=crypto:2 AEAD_AES_128_GCM ... + * a=crypto:3 AES_256_CM_HMAC_SHA1_ ... + * a=crypto:4 AEAD_AES_256_GCM ... + * a=crypto:5 AES_192_CM_HMAC_SHA1_ ... + * something like 'AEAD_AES_192_GCM' is not specified by the RFCs + * + * If you want to prefer another crypto suite or you want to + * exclude a suite, change this array and recompile Asterisk. + * This list cannot be changed from rtp.conf because you should + * know what you are doing. Especially AES-192 and AES-GCM are + * broken in many VoIP clients, see + * https://github.com/cisco/libsrtp/pull/170 + * https://github.com/cisco/libsrtp/pull/184 + * Furthermore, AES-GCM uses a shorter crypto-suite string which + * causes Nokia phones based on Symbian/S60 to reject the whole + * INVITE with status 500, even if a matching suite was offered. + * AES-256 might just waste your processor cycles, especially if + * your TLS transport is not secured with equivalent grade, see + * https://security.stackexchange.com/q/61361 + * Therefore, AES-128 was preferred here. + * + * If you want to enable one of those defines, please, go for + * CFLAGS='-DENABLE_SRTP_AES_GCM' ./configure && sudo make install + */ + { len, 0, 30 }, +#if defined(HAVE_SRTP_GCM) && defined(ENABLE_SRTP_AES_GCM) + { AST_SRTP_CRYPTO_TAG_16, 0, AES_128_GCM_KEYSIZE_WSALT }, +#endif +#if defined(HAVE_SRTP_256) && defined(ENABLE_SRTP_AES_256) + { len, AST_SRTP_CRYPTO_AES_256, 46 }, +#endif +#if defined(HAVE_SRTP_GCM) && defined(ENABLE_SRTP_AES_GCM) && defined(ENABLE_SRTP_AES_256) + { AST_SRTP_CRYPTO_TAG_16, AST_SRTP_CRYPTO_AES_256, AES_256_GCM_KEYSIZE_WSALT }, +#endif +#if defined(HAVE_SRTP_192) && defined(ENABLE_SRTP_AES_192) + { len, AST_SRTP_CRYPTO_AES_192, 38 }, +#endif + }; + struct ast_sdp_srtp *tmp = srtp; + int i; + + for (i = 0; i < ARRAY_LEN(attr); i++) { + if (attr[i][0]) { + ast_set_flag(tmp, attr[i][0]); + } + if (attr[i][1]) { + ast_set_flag(tmp, attr[i][1]); + } + tmp->crypto = sdp_crypto_alloc(attr[i][2]); /* key_len */ + tmp->crypto->tag = (i + 1); /* tag starts at 1 */ + + if (i < ARRAY_LEN(attr) - 1) { + AST_LIST_NEXT(tmp, sdp_srtp_list) = ast_sdp_srtp_alloc(); + tmp = AST_LIST_NEXT(tmp, sdp_srtp_list); + } + } + } } @@ -410,4 +652,18 @@ } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_32)) { taglen = 32; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_16)) { + taglen = 16; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_8)) { + taglen = 8; + } else { + taglen = default_taglen_32 ? 32 : 80; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_256)) { + taglen |= 0x0200; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_192)) { + taglen |= 0x0100; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME)) { + taglen |= 0x0080; } --- res/srtp/srtp_compat.h (Asterisk 13.21-cert6) +++ res/srtp/srtp_compat.h (working copy) @@ -8,2 +8,22 @@ -#define AES_128_ICM 1 -#define HMAC_SHA1 3 +#define crypto_policy_set_aes_cm_128_hmac_sha1_80 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80 +#define crypto_policy_set_aes_cm_128_hmac_sha1_32 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32 +#define crypto_policy_set_aes_cm_192_hmac_sha1_80 srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80 +#define crypto_policy_set_aes_cm_192_hmac_sha1_32 srtp_crypto_policy_set_aes_cm_192_hmac_sha1_32 +#define crypto_policy_set_aes_cm_256_hmac_sha1_80 srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80 +#define crypto_policy_set_aes_cm_256_hmac_sha1_32 srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32 +#define crypto_policy_set_aes_gcm_128_16_auth srtp_crypto_policy_set_aes_gcm_128_16_auth +#define crypto_policy_set_aes_gcm_256_16_auth srtp_crypto_policy_set_aes_gcm_256_16_auth +#define crypto_policy_set_aes_gcm_128_8_auth srtp_crypto_policy_set_aes_gcm_128_8_auth +#define crypto_policy_set_aes_gcm_256_8_auth srtp_crypto_policy_set_aes_gcm_256_8_auth + +#if defined(SRTP_AES_GCM_128_KEY_LEN_WSALT) +#define AES_128_GCM_KEYSIZE_WSALT SRTP_AES_GCM_128_KEY_LEN_WSALT +#else +#define AES_128_GCM_KEYSIZE_WSALT SRTP_AES_128_GCM_KEYSIZE_WSALT +#endif + +#if defined(SRTP_AES_GCM_256_KEY_LEN_WSALT) +#define AES_256_GCM_KEYSIZE_WSALT SRTP_AES_GCM_256_KEY_LEN_WSALT +#else +#define AES_256_GCM_KEYSIZE_WSALT SRTP_AES_256_GCM_KEYSIZE_WSALT +#endif