/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set et sw=2 ts=2: */ /*************************************************************************** * srtp.cc * * Thu Sep 5 10:46:10 CEST 2013 * Copyright 2013 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of lrtp. * * lrtp is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * lrtp 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with lrtp; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "srtp.h" #ifdef WIN32 #include #else #include #endif #include "asc2bin.h" // This macro translates srtp status codes into exceptions: #define SRTP_THROW(s) \ do { \ switch(s) { \ case err_status_fail: throw LRTP_SRTP_FAIL; \ case err_status_bad_param: throw LRTP_SRTP_BAD_PARAM; \ case err_status_alloc_fail: throw LRTP_SRTP_ALLOC_FAIL; \ case err_status_dealloc_fail: throw LRTP_SRTP_DEALLOC_FAIL; \ case err_status_init_fail: throw LRTP_SRTP_INIT_FAIL; \ case err_status_terminus: throw LRTP_SRTP_TERMINUS; \ case err_status_auth_fail: throw LRTP_SRTP_AUTH_FAIL; \ case err_status_cipher_fail: throw LRTP_SRTP_CIPHER_FAIL; \ case err_status_replay_fail: throw LRTP_SRTP_REPLAY_FAIL; \ case err_status_replay_old: throw LRTP_SRTP_REPLAY_OLD; \ case err_status_algo_fail: throw LRTP_SRTP_ALGO_FAIL; \ case err_status_no_such_op: throw LRTP_SRTP_NO_SUCH_OP; \ case err_status_no_ctx: throw LRTP_SRTP_NO_CTX; \ case err_status_cant_check: throw LRTP_SRTP_CANT_CHECK; \ case err_status_key_expired: throw LRTP_SRTP_KEY_EXPIRED; \ case err_status_socket_err: throw LRTP_SRTP_SOCKET_ERR; \ case err_status_signal_err: throw LRTP_SRTP_SIGNAL_ERR; \ case err_status_nonce_bad: throw LRTP_SRTP_NONCE_BAD; \ case err_status_read_fail: throw LRTP_SRTP_READ_FAIL; \ case err_status_write_fail: throw LRTP_SRTP_WRITE_FAIL; \ case err_status_parse_err: throw LRTP_SRTP_PARSE_ERR; \ case err_status_encode_err: throw LRTP_SRTP_ENCODE_ERR; \ case err_status_semaphore_err: throw LRTP_SRTP_SEMAPHORE_ERR; \ case err_status_pfkey_err: throw LRTP_SRTP_PFKEY_ERR; \ default: throw LRTP_SRTP_FAIL; \ } \ } while(0) struct SRTP::prv { char *key; size_t key_len; unsigned int ssrc; srtp_t session; srtp_policy_t policy; }; SRTP::SRTP(std::string key, unsigned int ssrc) _throw(enum lrtp_status_t) { prv = NULL; err_status_t status; prv = new struct prv; if(!prv) throw LRTP_OUT_OF_MEMORY; prv->ssrc = ssrc; setupKey(key); setupPolicy(true, true); status = srtp_create(&prv->session, &prv->policy); if(status != err_status_ok) SRTP_THROW(status); status = srtp_add_stream(prv->session, &prv->policy); if(status != err_status_ok) SRTP_THROW(status); } SRTP::~SRTP() { err_status_t status = srtp_remove_stream(prv->session, htonl(prv->ssrc)); if(status != err_status_ok) { // TODO: Error handling printf("srtp_remove_stream failed %d\n", status); } status = srtp_dealloc(prv->session); if(status != err_status_ok) { // TODO: Error handling printf("srtp_dealloc failed %d\n", status); } if(prv) { free(prv->key); delete prv; } } void SRTP::setupKey(const std::string &key) _throw(enum lrtp_status_t) { prv->key = (char *)calloc(MASTER_KEY_LEN, 1); prv->key_len = MASTER_KEY_LEN; if(key.length() > MASTER_KEY_LEN * 2) printf("KeyTooLong\n"); // TODO // Read key from hexadecimal on command line into an octet string ssize_t len = asc2bin(prv->key, prv->key_len, key.c_str(), key.size()); if(len == -1) printf("InvalidHexKeyString\n"); // TODO prv->key_len = len; // check that hex string is the right length. if(len < MASTER_KEY_LEN) printf("KeyTooShort\n"); // TODO } void SRTP::setupPolicy(bool confidentiality, bool authentication) _throw(enum lrtp_status_t) { #ifndef USE_CRYPTO confidentiality = authentication = false; printf("No crypto!\n"); #endif /* * create policy structure, using the default mechanisms but * with only the security services requested on the command line, * using the right SSRC value */ if(confidentiality && authentication) { crypto_policy_set_aes_cm_128_hmac_sha1_80(&prv->policy.rtp); prv->policy.rtp.sec_serv = sec_serv_conf_and_auth; } else if(confidentiality && !authentication) { crypto_policy_set_aes_cm_128_null_auth(&prv->policy.rtp); prv->policy.rtp.sec_serv = sec_serv_conf; } else if(!confidentiality && authentication) { crypto_policy_set_null_cipher_hmac_sha1_80(&prv->policy.rtp); prv->policy.rtp.sec_serv = sec_serv_auth; } else { /* * we're not providing security services, so set the policy to the * null policy * * Note that this policy does not conform to the SRTP * specification, since RTCP authentication is required. However, * the effect of this policy is to turn off SRTP, so that this * application is now a vanilla-flavored RTP application. */ prv->policy.rtp.cipher_type = NULL_CIPHER; prv->policy.rtp.cipher_key_len = 0; prv->policy.rtp.auth_type = NULL_AUTH; prv->policy.rtp.auth_key_len = 0; prv->policy.rtp.auth_tag_len = 0; prv->policy.rtp.sec_serv = sec_serv_none; } crypto_policy_set_rtcp_default(&prv->policy.rtcp); prv->policy.rtcp.sec_serv = sec_serv_none; // No need for RTCP prv->policy.key = (uint8_t *)prv->key; prv->policy.ssrc.type = ssrc_specific; prv->policy.ssrc.value = prv->ssrc; prv->policy.next = NULL; } int SRTP::encrypt(char *packet, size_t size) _throw(enum lrtp_status_t) { int sz = size; err_status_t status = srtp_protect(prv->session, packet, &sz); if(status != err_status_ok) { // TODO: throw SRTP::UnprotectException(); printf("srtp_protect failed %d\n", status); } return sz; } int SRTP::decrypt(char *packet, size_t size) _throw(enum lrtp_status_t) { int sz = size; err_status_t status = srtp_unprotect(prv->session, packet, &sz); switch(status) { case err_status_ok: // No errors. break; case err_status_replay_fail: // TODO: throw SRTP::ReplayException();// (replay check failed) printf("srtp_unprotect failed replay %d\n", status); sz = -1; break; case err_status_replay_old: // TODO: throw SRTP::ReplayOldException();// (replay check failed) printf("srtp_unprotect failed replay_old %d\n", status); sz = -1; break; case err_status_auth_fail: // TODO: throw SRTP::AuthCheckException();// (auth check failed) printf("srtp_unprotect failed auth %d\n", status); sz = -1; break; default: // TODO: throw SRTP::UnprotectException(); printf("srtp_unprotect failed %d\n", status); sz = -1; break; } /* if(octets_recvd - RTP_HEADER_LEN > (ssize_t)size) { printf("BufferSize %d\n", status); } */ // TODO: rtp.fromBuffer(packet, size); //memcpy(buf, prv->receiver->message.body, octets_recvd - RTP_HEADER_LEN); return sz; } // Global SRTP instance reference counter static int active_srtp_instances = 0; SRTP::SRTPInstance::SRTPInstance() _throw(enum lrtp_status_t) { err_status_t status; if(active_srtp_instances == 0) { status = srtp_init(); active_srtp_instances++; if(status != err_status_ok) SRTP_THROW(status); } } SRTP::SRTPInstance::~SRTPInstance() _throw(enum lrtp_status_t) { err_status_t status; active_srtp_instances--; if(active_srtp_instances == 0) { status = srtp_shutdown(); if(status != err_status_ok) SRTP_THROW(status); } }