#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT /* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com> * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom <http://doubango.org> * * This file is part of Open Source Doubango Framework. * * DOUBANGO is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DOUBANGO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DOUBANGO. * */ /**@file trtp_srtp.c */ #include "tinyrtp/trtp_srtp.h" #include "tinyrtp/trtp_manager.h" #if HAVE_SRTP extern err_status_t crypto_get_random(unsigned char *buffer, unsigned int length); int trtp_srtp_ctx_internal_init(struct trtp_srtp_ctx_internal_xs* ctx, int32_t tag, trtp_srtp_crypto_type_t type, uint32_t ssrc) { char* key_str = ctx->key_str; err_status_t srtp_err; tsk_size_t size; if(!ctx){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(ctx->initialized){ trtp_srtp_ctx_internal_deinit(ctx); } ctx->tag = tag; ctx->crypto_type = type; if((srtp_err = crypto_get_random((unsigned char*)ctx->key_bin, sizeof(ctx->key_bin))) != err_status_ok){ TSK_DEBUG_ERROR("crypto_get_random() failed"); return -2; } size = tsk_base64_encode((const uint8_t*)ctx->key_bin, sizeof(ctx->key_bin), &key_str); key_str[size] = '\0'; switch(ctx->crypto_type){ case HMAC_SHA1_80: { crypto_policy_set_aes_cm_128_hmac_sha1_80(&ctx->policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&ctx->policy.rtcp); break; } case HMAC_SHA1_32: default: { crypto_policy_set_aes_cm_128_hmac_sha1_32(&ctx->policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&ctx->policy.rtcp); // RTCP always 80 break; } } ctx->policy.key = (unsigned char*)ctx->key_bin; ctx->policy.ssrc.type = ssrc_any_outbound; ctx->policy.ssrc.value = ssrc; ctx->policy.window_size = 2048; ctx->policy.allow_repeat_tx = 1; if((srtp_err = srtp_create(&ctx->session, &ctx->policy)) != err_status_ok){ TSK_DEBUG_ERROR("srtp_create() failed"); return -3; } ctx->initialized = tsk_true; return 0; } int trtp_srtp_ctx_internal_deinit(struct trtp_srtp_ctx_internal_xs* ctx) { if(!ctx){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(ctx->initialized){ /*err_status_t srtp_err =*/ srtp_dealloc(ctx->session); memset(&ctx->policy, 0, sizeof(ctx->policy)); ctx->initialized = tsk_false; } return 0; } int trtp_srtp_ctx_init(trtp_srtp_ctx_xt* ctx, int32_t tag, trtp_srtp_crypto_type_t type, uint32_t ssrc) { int ret; if(!ctx){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if((ret = trtp_srtp_ctx_internal_init(&ctx->rtp, tag, type, ssrc))){ return ret; } return trtp_srtp_ctx_internal_init(&ctx->rtcp, tag, type, ssrc); } int trtp_srtp_ctx_deinit(trtp_srtp_ctx_xt* ctx) { int ret; if(!ctx){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if((ret = trtp_srtp_ctx_internal_deinit(&ctx->rtp))){ return ret; } return trtp_srtp_ctx_internal_deinit(&ctx->rtcp); } int trtp_srtp_match_line(const char* crypto_line, int32_t* tag, int32_t* crypto_type, char* key, tsk_size_t key_size) { char* saveptr; char* v = tsk_strtok_r((char*)crypto_line, " :|;", &saveptr); int32_t k = 0; while(v){ switch(k){ case 0: { if(tag){ *tag = atoi(v); } break; } case 1: { if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_80)){ if(crypto_type){ *crypto_type = HMAC_SHA1_80; } } else if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_32)){ if(crypto_type){ *crypto_type = HMAC_SHA1_32; } } else { return -0xFF; } break; } case 2: { if(!tsk_striequals(v, "inline")){ return -0xFF; } break; } case 3: { if(key && key_size){ memset(key, 0, key_size); memcpy(key, v, TSK_MIN(key_size, tsk_strlen(v))); } return 0; } } ++k; v = tsk_strtok_r(tsk_null, " :|;", &saveptr); } return -0xF0; } tsk_size_t trtp_srtp_get_local_contexts(trtp_manager_t* rtp_mgr, const struct trtp_srtp_ctx_xs ** contexts, tsk_size_t contexts_count) { tsk_size_t ret = 0; if(!rtp_mgr || !contexts){ TSK_DEBUG_ERROR("Invalid parameter"); return 0; } if(contexts_count > ret && rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80].rtp.initialized){ contexts[ret++] = &rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80]; } if(contexts_count > ret && rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32].rtp.initialized){ contexts[ret++] = &rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32]; } return ret; } int trtp_srtp_set_crypto(struct trtp_manager_s* rtp_mgr, const char* crypto_line, int32_t idx) { //e.g. 2 F8_128_HMAC_SHA1_80 inline:MTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5QUJjZGVm|2^20|1:4;inline:QUJjZGVmMTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5|2^20|2:4" trtp_srtp_ctx_xt* srtp_ctx; int ret; uint8_t *key_bin; err_status_t srtp_err; int32_t tag, crypto_type; char key_str[SRTP_MAX_KEY_LEN + 1]; memset(key_str, 0, sizeof(key_str)); if((ret = trtp_srtp_match_line(crypto_line, &tag, &crypto_type, key_str, sizeof(key_str) - 1))){ return ret; } srtp_ctx = &rtp_mgr->srtp_contexts[idx][crypto_type]; ret = trtp_srtp_ctx_deinit(srtp_ctx); srtp_ctx->rtp.tag = tag; srtp_ctx->rtp.crypto_type = (trtp_srtp_crypto_type_t)crypto_type; memcpy(srtp_ctx->rtp.key_str, key_str, sizeof(srtp_ctx->rtp.key_str)); switch(srtp_ctx->rtp.crypto_type){ case HMAC_SHA1_80: { crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->rtp.policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->rtp.policy.rtcp); if(idx == TRTP_SRTP_LINE_IDX_REMOTE){ trtp_srtp_ctx_deinit(&rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32]); rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80].rtp.tag = rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80].rtcp.tag = srtp_ctx->rtp.tag; } break; } case HMAC_SHA1_32: { crypto_policy_set_aes_cm_128_hmac_sha1_32(&srtp_ctx->rtp.policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->rtp.policy.rtcp); // RTCP always 80 if(idx == TRTP_SRTP_LINE_IDX_REMOTE){ trtp_srtp_ctx_deinit(&rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80]); rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32].rtp.tag = rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32].rtcp.tag = srtp_ctx->rtp.tag; } break; } } key_bin = (unsigned char*)srtp_ctx->rtp.key_bin; tsk_base64_decode((const uint8_t*)srtp_ctx->rtp.key_str, tsk_strlen(srtp_ctx->rtp.key_str), (char**)&key_bin); srtp_ctx->rtp.policy.key = key_bin; srtp_ctx->rtp.policy.ssrc.type = idx == TRTP_SRTP_LINE_IDX_REMOTE ? ssrc_any_inbound : ssrc_any_outbound; srtp_ctx->rtp.policy.window_size = 2048; srtp_ctx->rtp.policy.allow_repeat_tx = 1; if((srtp_err = srtp_create(&srtp_ctx->rtp.session, &srtp_ctx->rtp.policy)) != err_status_ok){ TSK_DEBUG_ERROR("srtp_create() failed: %d", srtp_err); return -3; } srtp_ctx->rtp.initialized = tsk_true; return 0; } int trtp_srtp_set_key_and_salt(trtp_manager_t* rtp_mgr, trtp_srtp_crypto_type_t crypto_type, const void* key, tsk_size_t key_size, const void* salt, tsk_size_t salt_size, int32_t idx, tsk_bool_t is_rtp) { int ret; trtp_srtp_ctx_internal_xt* srtp_ctx; err_status_t srtp_err; if(!rtp_mgr || !key || !key_size || !salt || !salt_size){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } srtp_ctx = is_rtp ? &rtp_mgr->srtp_contexts[idx][crypto_type].rtp : &rtp_mgr->srtp_contexts[idx][crypto_type].rtcp; if((ret = trtp_srtp_ctx_internal_deinit(srtp_ctx))){ return ret; } switch((srtp_ctx->crypto_type = crypto_type)){ case HMAC_SHA1_80: default: { crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->policy.rtcp); break; } case HMAC_SHA1_32: { crypto_policy_set_aes_cm_128_hmac_sha1_32(&srtp_ctx->policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->policy.rtcp); // always 80 break; } } memcpy(srtp_ctx->key_bin, key, key_size); #if HAVE_APPEND_SALT_TO_KEY append_salt_to_key(srtp_ctx->key_bin, key_size, (void*)salt, salt_size); #else memcpy(&srtp_ctx->key_bin[key_size], salt, salt_size); #endif srtp_ctx->policy.key = (unsigned char *)srtp_ctx->key_bin; srtp_ctx->policy.ssrc.type = idx == TRTP_SRTP_LINE_IDX_REMOTE ? ssrc_any_inbound : ssrc_any_outbound; srtp_ctx->policy.window_size = 2048; srtp_ctx->policy.allow_repeat_tx = 1; if((srtp_err = srtp_create(&srtp_ctx->session, &srtp_ctx->policy)) != err_status_ok){ TSK_DEBUG_ERROR("srtp_create() failed: %d", srtp_err); return -3; } srtp_ctx->initialized = tsk_true; return 0; } tsk_bool_t trtp_srtp_is_initialized(trtp_manager_t* rtp_mgr) { if(!rtp_mgr){ return tsk_false; } return ((rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][0].rtp.initialized || rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][1].rtp.initialized) && rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_REMOTE][0].rtp.initialized); } tsk_bool_t trtp_srtp_is_started(trtp_manager_t* rtp_mgr) { if(!rtp_mgr){ TSK_DEBUG_ERROR("Invalid argument"); return tsk_false; } return (rtp_mgr ->srtp_ctx_neg_remote && rtp_mgr ->srtp_ctx_neg_local); } tsk_bool_t trtp_srtp_process_mikey_message(struct trtp_manager_s* rtp_mgr, const char* mikey_message) { return tsk_true; } #endif /* HAVE_SRTP */