freeswitch/libs/libzrtp/src/zrtp_srtp_builtin.c
Travis Cross d2edcad66e Merge Phil Zimmermann's libzrtp as a FreeSWITCH library
Thanks to Phil Zimmermann for the code and for the license exception
we needed to include it.

There remains some build system integration work to be done before
this code will build properly in the FreeSWITCH tree.
2012-03-31 23:42:27 +00:00

1470 lines
46 KiB
C

/*
* libZRTP SDK library, implements the ZRTP secure VoIP protocol.
* Copyright (c) 2006-2009 Philip R. Zimmermann. All rights reserved.
* Contact: http://philzimmermann.com
* For licensing and other legal details, see the file zrtp_legal.c.
*
* Vitaly Rozhkov <v.rozhkov at soft-industry.com>
*/
#include "zrtp.h"
#define _ZTU_ "zrtp srtp"
#if (!defined(ZRTP_USE_EXTERN_SRTP)) || (ZRTP_USE_EXTERN_SRTP == 0)
/* constants that are used for packet's parsing */
#define octets_in_rtp_header 12
#define uint32s_in_rtp_header 3
#define octets_in_rtcp_header 8
#define uint32s_in_rtcp_header 2
/*
defines to make work with cipher component little bit easy
*/
#define zrtp_cipher_init(self) \
( ((self)->cipher)->init(((self)->cipher)) )
#define zrtp_cipher_start(self, key, extra_data, mode) \
( ((self)->cipher)->start(((self)->cipher), (key), (extra_data), (mode)) )
#define zrtp_cipher_set_iv(self, iv) \
( ((self)->cipher)->set_iv( ((self)->cipher), ((self)->ctx), (iv)) )
#define zrtp_cipher_encrypt(self, buf, len) \
( ((self)->cipher)->encrypt( ((self)->cipher), ((self)->ctx), (buf), (len)) )
#define zrtp_cipher_decrypt(self, buf, len) \
( ((self)->cipher)->decrypt( ((self)->cipher), ((self)->ctx), (buf), (len)) )
#define zrtp_cipher_self_test(self) \
( ((self)->cipher)->self_test(((self)->cipher)) )
#define zrtp_cipher_stop(self) \
( ((self)->cipher)->stop(((self)->cipher), ((self)->ctx)) )
#define zrtp_cipher_free(self) \
( ((self)->cipher)->free(((self)->cipher)) )
/*===========================================================================*/
/* Replay protection serve functions set */
/*===========================================================================*/
/*! \brief Allocates and initializes replay protection context. Initialize
* mutexes and linked lists.
* \return
* - allocated replay protection context
* - NULL if error
*/
/*---------------------------------------------------------------------------*/
zrtp_rp_ctx_t* rp_init()
{
zrtp_rp_ctx_t *ctx = zrtp_sys_alloc(sizeof(zrtp_rp_ctx_t));
if(NULL == ctx){
return NULL;
}
if(zrtp_status_ok != zrtp_mutex_init(&ctx->inc_sync)){
zrtp_sys_free(ctx);
return NULL;
}
if(zrtp_status_ok != zrtp_mutex_init(&ctx->out_sync)){
zrtp_mutex_destroy(ctx->inc_sync);
zrtp_sys_free(ctx);
return NULL;
}
init_mlist(&ctx->inc_head.mlist);
init_mlist(&ctx->out_head.mlist);
return ctx;
}
/*! \brief Deinitializes and deallocates replay protection context.
* \param ctx - replay protection context
* \return
* - zrtp_status_ok
*/
/*---------------------------------------------------------------------------*/
zrtp_status_t rp_destroy(zrtp_rp_ctx_t *ctx)
{
mlist_t *pos, *n;
zrtp_rp_node_t *node = NULL;
/*free all existing replay protection nodes in the incoming list*/
zrtp_mutex_lock(ctx->inc_sync);
mlist_for_each_safe(pos, n, &ctx->inc_head.mlist){
node = mlist_get_struct(zrtp_rp_node_t, mlist, pos);
mlist_del(&node->mlist);
zrtp_sys_free(node);
}
zrtp_mutex_unlock(ctx->inc_sync);
zrtp_mutex_destroy(ctx->inc_sync);
/*free all existing replay protection nodes in the outgoing list*/
zrtp_mutex_lock(ctx->out_sync);
mlist_for_each_safe(pos, n, &ctx->out_head.mlist){
node = mlist_get_struct(zrtp_rp_node_t, mlist, pos);
mlist_del(&node->mlist);
zrtp_sys_free(node);
}
zrtp_mutex_unlock(ctx->out_sync);
zrtp_mutex_destroy(ctx->out_sync);
zrtp_sys_free(ctx);
return zrtp_status_ok;
}
/*! \brief Finds replay protection node by given ssrc. Which linked list to search is
* determined by the direction param.
* \warning This function doesn't lock the linked list before search and is for internal usage.
* To find necessary replay protection node use get_rp_node() function.
* \param ctx - pointer to replay protection context
* \param direction - defines what list to search. It may have values:
* - RP_INCOMING_DIRECTION
* - RP_OUTGOING_DIRECTION
* \return
* - pointer to found replay protection node
* - NULL if node hasn't been found or if error
*/
/*---------------------------------------------------------------------------*/
zrtp_rp_node_t *get_rp_node_non_lock( zrtp_rp_ctx_t *ctx,
uint8_t direction,
uint32_t ssrc)
{
zrtp_rp_node_t *node = NULL;
mlist_t *pos;
mlist_t *head = NULL;
switch(direction){
case RP_INCOMING_DIRECTION:
head = &ctx->inc_head.mlist;
break;
case RP_OUTGOING_DIRECTION:
head = &ctx->out_head.mlist;
break;
default:
head = NULL;
break;
};
if(NULL != head){
mlist_for_each(pos, head){
node = mlist_get_struct(zrtp_rp_node_t, mlist, pos);
if(ssrc == node->ssrc){
break;
}else{
node = NULL;
}
}
}
return node;
}
///*! \brief Finds replay protection node by given ssrc. Linked list to search is
// * determined by direction param. This function locks the linked list to
// * ensure exclusive access.
// *
// * \param ctx - pointer to replay protection context
// * \param direction - defines what list to search. It may have values:
// * - RP_INCOMING_DIRECTION
// * - RP_OUTGOING_DIRECTION
// * \param ssrc - value by which search will be made
// * \return
// * - pointer to found replay protection node
// * - NULL if node hasn't been found or if error
// */
///*---------------------------------------------------------------------------*/
//zrtp_rp_node_t *get_rp_node(zrtp_rp_ctx_t *ctx, uint8_t direction, uint32_t ssrc)
//{
// zrtp_rp_node_t *node = NULL;
// zrtp_mutex_t *sync = NULL;
//
// switch(direction){
// case RP_INCOMING_DIRECTION:
// sync = ctx->inc_sync;
// break;
// case RP_OUTGOING_DIRECTION:
// sync = ctx->out_sync;
// break;
// default:
// sync = NULL;
// break;
// };
//
// if(NULL != sync){
// zrtp_mutex_lock(sync);
// node = get_rp_node_non_lock(ctx, direction, ssrc);
// zrtp_mutex_unlock(sync);
// }
//
// return node;
//}
/*! \brief Allocates new replay protection node for given direction and ssrc and adds it into
* appropriate linked list.
* \warning This function is for internal usage. Use add_rp_node() and add_rp_node_unique().
* \param srtp_ctx - pointer to SRTP ctx related with created node. Used for removing node on SRTP session destruction.
* \param ctx - pointer to replay protection context
* \param direction - defines in which list newly created node will be inserted. It may have values:
* - RP_INCOMING_DIRECTION
* - RP_OUTGOING_DIRECTION
* \param ssrc - newly created replay protection node key value.
* \param is_unique - defines what should be returned when replay protection node
* with given direction and ssrc values already exists:
* - pointer to existing node if is_unique == 0
* - NULL if is_unique == 1
* \return
* - pointer to newly created replay protection node
* - pointer to existing replay protection node
* - NULL if is_unique == 1 and needed replay protection node already exists or if error
*/
/*---------------------------------------------------------------------------*/
zrtp_rp_node_t *add_rp_node_ex( zrtp_srtp_ctx_t *srtp_ctx,
zrtp_rp_ctx_t *ctx,
uint8_t direction,
uint32_t ssrc,
uint8_t is_unique)
{
zrtp_rp_node_t *node = NULL;
zrtp_mutex_t *sync = NULL;
mlist_t *head = NULL;
switch(direction){
case RP_INCOMING_DIRECTION:
sync = ctx->inc_sync;
head = &ctx->inc_head.mlist;
break;
case RP_OUTGOING_DIRECTION:
sync = ctx->out_sync;
head = &ctx->out_head.mlist;
break;
default:
sync = NULL;
head = NULL;
break;
};
if(NULL != sync && NULL != head){
zrtp_mutex_lock(sync);
do{
node = get_rp_node_non_lock(ctx, direction, ssrc);
/*create new node if not found*/
if(NULL == node){
node = zrtp_sys_alloc(sizeof(zrtp_rp_node_t));
if(NULL == node){
break;
}
/*clean sliding window and on-top sequence number value*/
zrtp_memset(node, 0, sizeof(zrtp_rp_node_t));
node->ssrc = ssrc;
node->srtp_ctx = srtp_ctx;
mlist_add_tail(head, &node->mlist);
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_,"\tadd %s rp node. ssrc[%u] srtp_ctx[0x%08x]",
direction==RP_INCOMING_DIRECTION?"incoming":"outgoing\n",
zrtp_ntoh32(node->ssrc), node->srtp_ctx));
#endif
}else if(is_unique){
// ???: why do we need unique mode at all?
node = NULL;
}
}while(0);
zrtp_mutex_unlock(sync);
}
return node;
}
/*! \brief Allocates new replay protection node for given direction and ssrc and adds it into
* appropriate linked list. This function is based on add_rp_node_ex().
* \param srtp_ctx - pointer to SRTP ctx related with created node. Used for removing node on SRTP session destruction.
* \param ctx - pointer to replay protection context
* \param direction - defines in which list newly created node will be inserted. It may have values:
* - RP_INCOMING_DIRECTION
* - RP_OUTGOING_DIRECTION
* \param ssrc - newly created replay protection node key value.
* \return
* - pointer to newly created replay protection node
* - pointer to existing replay protection node
* - NULL if error
*/
zrtp_rp_node_t *add_rp_node(zrtp_srtp_ctx_t *srtp_ctx, zrtp_rp_ctx_t *ctx, uint8_t direction, uint32_t ssrc){
/*not-unique mode*/
// ???: why do we need unique mode at all?
return add_rp_node_ex(srtp_ctx, ctx, direction, ssrc, 0);
}
///*! \brief Allocates new replay protection node for given direction and ssrc and adds it into
// * appropriate linked list. This function is based on add_rp_node_ex().
// * \param srtp_ctx - pointer to SRTP ctx related with created node. Used for removing node on SRTP session destruction.
// * \param ctx - pointer to replay protection context
// * \param direction - defines in which list newly created node will be inserted. It may have values:
// * - RP_INCOMING_DIRECTION
// * - RP_OUTGOING_DIRECTION
// * \param ssrc - newly created replay protection node key value.
// * \return
// * - pointer to newly created replay protection node
// * - NULL if error or if needed node already exists
// */
//zrtp_rp_node_t *add_rp_node_unique(zrtp_srtp_ctx_t *srtp_ctx, zrtp_rp_ctx_t *ctx, uint8_t direction, uint32_t ssrc){
// /*unique mode*/
// return add_rp_node_ex(srtp_ctx, ctx, direction, ssrc, 1);
//}
/*! \brief Removes replay protection node with given ssrc from linked list defined by direction value.
* \param ctx - pointer to replay protection context
* \param direction - defines from which list replay protection node will be removed. It may have values:
* - RP_INCOMING_DIRECTION
* - RP_OUTGOING_DIRECTION
* \param ssrc - key value of replay protection node to remove
* \return
* - zrtp_status_ok if replay protection node has been removed successfully
* - zrtp_status_fail if node hasn't been found
*/
/*---------------------------------------------------------------------------*/
zrtp_status_t remove_rp_node(zrtp_rp_ctx_t *ctx, uint8_t direction, uint32_t ssrc){
zrtp_rp_node_t *node = NULL;
zrtp_mutex_t *sync = NULL;
zrtp_status_t res = zrtp_status_fail;
switch(direction){
case RP_INCOMING_DIRECTION:
sync = ctx->inc_sync;
break;
case RP_OUTGOING_DIRECTION:
sync = ctx->out_sync;
break;
default:
sync = NULL;
break;
};
if(NULL != sync){
zrtp_mutex_lock(sync);
node = get_rp_node_non_lock(ctx, direction, ssrc);
if(NULL != node){
mlist_del(&node->mlist);
zrtp_sys_free(node);
res = zrtp_status_ok;
}
zrtp_mutex_unlock(sync);
}
return res;
}
zrtp_status_t remove_rp_nodes_by_srtp_ctx(zrtp_srtp_ctx_t *srtp_ctx, zrtp_rp_ctx_t *ctx){
zrtp_status_t res = zrtp_status_ok;
zrtp_rp_node_t *node = NULL;
mlist_t *pos, *n;
if((NULL == srtp_ctx) || (NULL == ctx)){
return zrtp_status_bad_param;
}
/* Walk over incoming nodes list */
zrtp_mutex_lock(ctx->inc_sync);
mlist_for_each_safe(pos, n, &ctx->inc_head.mlist){
node = mlist_get_struct(zrtp_rp_node_t, mlist, pos);
if((NULL != node->srtp_ctx) && (node->srtp_ctx == srtp_ctx)){
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_,"\tremove incoming rp node. ssrc[%u] srtp_ctx[0x%08x]\n",
zrtp_ntoh32(node->ssrc), node->srtp_ctx));
#endif
mlist_del(&node->mlist);
zrtp_sys_free(node);
}
}
zrtp_mutex_unlock(ctx->inc_sync);
/* Walk over outgoing nodes list */
zrtp_mutex_lock(ctx->out_sync);
mlist_for_each_safe(pos, n, &ctx->out_head.mlist){
node = mlist_get_struct(zrtp_rp_node_t, mlist, pos);
if((NULL != node->srtp_ctx) && (node->srtp_ctx == srtp_ctx)){
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_,"\tremove outgoing rp node. ssrc[%u] srtp_ctx[0x%08x]\n",
zrtp_ntoh32(node->ssrc), node->srtp_ctx));
#endif
mlist_del(&node->mlist);
zrtp_sys_free(node);
}
}
zrtp_mutex_unlock(ctx->out_sync);
return res;
}
/*===========================================================================*/
/* Replay protection mechanism functions set */
/*===========================================================================*/
/*! \brief This function is used for RTCP replay protection to generate next sequence number
* of outgoing RTCP packet. If the sequence number is too large it returns zrtp_status_key_expired.
* See RFC3711 for more details.
* \param srtp_rp - pointer to replay protection engine data
* \return
* - zrtp_status_key_expired if next sequence number is too large
* - zrtp_status_ok otherwise
*/
zrtp_status_t zrtp_srtp_rp_increment(zrtp_srtp_rp_t *srtp_rp){
if(srtp_rp->seq++ > 0x7fffffff){
return zrtp_status_key_expired;
}else{
return zrtp_status_ok;
}
}
/*! \brief Returns current on-top sequence number. This function is used for RTCP
* replay protection.
* \param srtp_rp - pointer to replay protection engine data
* \return current on-top sequence number
*/
uint32_t zrtp_srtp_rp_get_value(zrtp_srtp_rp_t *srtp_rp){
return srtp_rp->seq;
}
/*! \brief This function checks packet sequence number position relative to
* sliding window current position and makes the decision to accept or discard packet.
* \param srtp_rp - pointer to replay protection engine data
* \param packet - pointer to packet structure
* \return
* - zrtp_status_ok if packet must be accepted
* - zrtp_status_old_pkt if packet sequence number is lower than lowest sequence number
* which can be into the sliding window at the current time. In this case packet must be discarded.
* - zrtp_status_fail if packet must be discarded
*/
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_rp_check(zrtp_srtp_rp_t *srtp_rp, zrtp_rtp_info_t *packet)
{
int32_t delta = packet->seq - srtp_rp->seq;
if(delta > 0){
/*if delta is positive, it's good*/
return zrtp_status_ok;
}else if(ZRTP_SRTP_WINDOW_WIDTH-1 + delta < 0){
/*if delta is lower than the bitmask, it's bad*/
return zrtp_status_old_pkt;
}else{
if(1 == zrtp_bitmap_get_bit(srtp_rp->window, ZRTP_SRTP_WINDOW_WIDTH-1 + delta)){
/*delta is within the window, so check the bitmask*/
return zrtp_status_fail;
}
}
return zrtp_status_ok;
}
/*! \brief This function updates the sliding window state by setting appropriate bit and
* shifting the sliding window if needed.
* \param srtp_rp - pointer to replay protection engine data
* \param packet - pointer to packet structure
* \return
* - zrtp_status_ok
*/
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_rp_add(zrtp_srtp_rp_t *srtp_rp, zrtp_rtp_info_t *packet)
{
int32_t delta = packet->seq - srtp_rp->seq;
if(delta > 0){
/* packet sequence nubmer is larger than current on-top sequence number.
shift the window, set top bit and update on-top sequence number value */
srtp_rp->seq = packet->seq;
zrtp_bitmap_left_shift(srtp_rp->window, ZRTP_SRTP_WINDOW_WIDTH_BYTES, delta);
zrtp_bitmap_set_bit(srtp_rp->window, ZRTP_SRTP_WINDOW_WIDTH-1);
}else
/* commented by book, 19.07.07:
we need not consider case when delta == 0
if(0 == delta){
zrtp_bitmap_set_bit(srtp_rp->window, ZRTP_SRTP_WINDOW_WIDTH-1);
}else*/
{
/*
packet sequence number is into the sliding window.
set appropriate bit
*/
zrtp_bitmap_set_bit(srtp_rp->window, ZRTP_SRTP_WINDOW_WIDTH-1 + delta);
}
return zrtp_status_ok;
}
/*===========================================================================*/
/* Key derivation mechanism functions set */
/*===========================================================================*/
/*! \brief This function allocates key derivation context and initializes it with
* given master key, master salt and cipher.
* \param cipher - pointer to cipher that is used for key derivation
* \param key - pointer to master key
* \param salt - pointer to master salt
* \return
* - allocated key derivation context
* - NULL if error
*/
/*---------------------------------------------------------------------------*/
zrtp_dk_ctx *zrtp_dk_init( zrtp_cipher_t *cipher,
zrtp_stringn_t *key,
zrtp_stringn_t *salt)
{
zrtp_dk_ctx *ctx = NULL;
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_,"\tzrtp_dk_init():\n"));
ZRTP_LOG(3,(_ZTU_,"\tcipher ID[%i]\n", cipher->base.id));
#endif
do{
ctx = zrtp_sys_alloc(sizeof(zrtp_dk_ctx));
if(NULL == ctx){
break;
}
ctx->ctx = cipher->start(cipher, key->buffer, salt->buffer, ZRTP_CIPHER_MODE_CTR);
if(NULL == ctx->ctx){
zrtp_sys_free(ctx);
ctx = NULL;
break;
}
ctx->cipher = cipher;
}while(0);
return ctx;
}
/*! \brief This function derives key for different purposes like SRTP encryption,
* SRTP message authentication, etc. See RFC3711, "4.3. Key Derivation" for more details.
* \warning This function may change length field value in the result_key variable when
* length is larger than max_length field value.
* \param ctx - pointer to key derivation context
* \param label - defines purpose of key to derive
* \param result_key - out parameter. It contains derived key on success.
* \return
* - actually derived key length
* - -1 if error
*/
/*---------------------------------------------------------------------------*/
uint16_t zrtp_derive_key( zrtp_dk_ctx *ctx,
zrtp_srtp_prf_label label,
zrtp_stringn_t *result_key )
{
zrtp_v128_t nonce;
uint16_t length;
#if ZRTP_DEBUG_SRTP_KEYS
char buffer[256];
ZRTP_LOG(3,(_ZTU_,"\tzrtp_derive_key():\n"));
#endif
/* set eigth octet of nonce to <label>, set the rest of it to zero */
zrtp_memset(&nonce, 0, sizeof(zrtp_v128_t));
nonce.v8[7] = label;
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\tcipher IV[%s]\n",
hex2str((const char*)nonce.v8, sizeof(zrtp_v128_t), (char*)buffer, sizeof(buffer))));
#endif
zrtp_cipher_set_iv(ctx, &nonce);
length = (uint16_t) ZRTP_MIN(result_key->length, result_key->max_length);
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\texcepced key length[%i] result key length[%i]\n", result_key->length, length));
#endif
zrtp_memset(result_key->buffer, 0, length);
if(zrtp_status_ok == zrtp_cipher_encrypt(ctx, (uint8_t*)result_key->buffer, length)){
result_key->length = length;
return length;
}else{
return -1;
}
}
/*! \brief This function deallocates key derivation context allocated by \ref zrtp_dk_init() call.
* \param ctx - pointer to key derivation context to deallocate
*/
void zrtp_dk_deinit(zrtp_dk_ctx *ctx)
{
zrtp_cipher_stop(ctx);
zrtp_memset(ctx, 0, sizeof(zrtp_dk_ctx));
zrtp_sys_free(ctx);
}
/*! \brief This function allocates SRTP session and two stream contexts.
* \return
* - pointer to allocated SRTP session structure
* - NULL if error
*/
/*---------------------------------------------------------------------------*/
zrtp_srtp_ctx_t * zrtp_srtp_alloc()
{
zrtp_srtp_ctx_t *srtp_ctx = NULL;
do{
srtp_ctx = zrtp_sys_alloc(sizeof(zrtp_srtp_ctx_t));
if(NULL == srtp_ctx){
break;
}
srtp_ctx->incoming_srtp = zrtp_sys_alloc(sizeof(zrtp_srtp_stream_ctx_t));
if(NULL == srtp_ctx->incoming_srtp){
/*deallocate everything previously allocated on failure*/
zrtp_sys_free(srtp_ctx);
srtp_ctx = NULL;
break;
}
srtp_ctx->outgoing_srtp = zrtp_sys_alloc(sizeof(zrtp_srtp_stream_ctx_t));
if(NULL == srtp_ctx->outgoing_srtp){
/*deallocate everything previously allocated on failure*/
zrtp_sys_free(srtp_ctx->incoming_srtp);
zrtp_sys_free(srtp_ctx);
srtp_ctx = NULL;
break;
}
}while(0);
return srtp_ctx;
}
/*! \brief This function deallocates SRTP session structure allocated by zrtp_srtp_alloc() call.
* \param srtp_ctx - pointer to SRTP session structure.
*/
void zrtp_srtp_free(zrtp_srtp_ctx_t * srtp_ctx)
{
if (srtp_ctx)
{
if (srtp_ctx->incoming_srtp)
zrtp_sys_free(srtp_ctx->incoming_srtp);
if (srtp_ctx->outgoing_srtp)
zrtp_sys_free(srtp_ctx->outgoing_srtp);
zrtp_sys_free(srtp_ctx);
}
}
/*! \brief This function initializes stream context based on given profile.
* \param srtp_global - pointer to SRTP engine global context
* \param srtp_stream - pointer to stream context to initialize
* \param profile - pointer to profile for stream initialization
* \return
* - zrtp_status_ok if stream has been initialized successfully
* - one of \ref zrtp_status_t errors - if error
*/
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_stream_init( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_stream_ctx_t *srtp_stream,
zrtp_srtp_profile_t *profile )
{
#if ZRTP_DEBUG_SRTP_KEYS
char buffer[256];
#endif
zrtp_status_t res = zrtp_status_ok;
/*
TODO: use dynamic buffers for temoprary keys storing
NOTE!: be sure that tmp_key contains enought buffer length to store all
of derived keys. Authentication keys may be large.
*/
zrtp_string128_t tmp_key = ZSTR_INIT_EMPTY(tmp_key);
/*salt length is 16 bytes always*/
zrtp_string16_t tmp_salt = ZSTR_INIT_EMPTY(tmp_salt);
do{
zrtp_dk_ctx *dk_ctx = NULL;
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\tzrtp_srtp_stream_init():\n"));
#endif
if(NULL == srtp_stream || NULL == profile){
res = zrtp_status_bad_param;
break;
}
dk_ctx = zrtp_dk_init( profile->dk_cipher,
(zrtp_stringn_t*)&profile->key,
(zrtp_stringn_t*)&profile->salt );
if(NULL == dk_ctx)
{
res = zrtp_status_fail;
break;
}
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\tmaster_key[%s]\n",
hex2str(profile->key.buffer, profile->key.length, buffer, sizeof(buffer))));
ZRTP_LOG(3,(_ZTU_, "\t\tmaster_salt[%s]\n",
hex2str(profile->salt.buffer, profile->salt.length, buffer, sizeof(buffer))));
#endif
/*------------ init RTP-items ----------------*/
srtp_stream->rtp_cipher.cipher = profile->rtp_policy.cipher;
tmp_key.length = (uint16_t) profile->rtp_policy.cipher_key_len;
tmp_salt.length = profile->salt.length;
zrtp_derive_key(dk_ctx, label_rtp_encryption, (zrtp_stringn_t*)&tmp_key);
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\tderive RTP encryption key[%s] label:%i\n",
hex2str(tmp_key.buffer, tmp_key.length, buffer, sizeof(buffer)), label_rtp_encryption));
#endif
zrtp_derive_key(dk_ctx, label_rtp_salt, (zrtp_stringn_t*)&tmp_salt);
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\tderive RTP encryption salt[%s] label:%i\n",
hex2str(tmp_salt.buffer, tmp_salt.length, buffer, sizeof(buffer)), label_rtp_salt));
#endif
srtp_stream->rtp_cipher.ctx = zrtp_cipher_start(&srtp_stream->rtp_cipher,
tmp_key.buffer,
tmp_salt.buffer,
ZRTP_CIPHER_MODE_CTR );
if(NULL == srtp_stream->rtp_cipher.ctx){
zrtp_dk_deinit(dk_ctx);
res = zrtp_status_fail;
break;
}
srtp_stream->rtp_auth.hash = profile->rtp_policy.hash;
srtp_stream->rtp_auth.key_len = profile->rtp_policy.auth_key_len;
srtp_stream->rtp_auth.tag_len = profile->rtp_policy.auth_tag_len;
srtp_stream->rtp_auth.key = zrtp_sys_alloc(srtp_stream->rtp_auth.key_len);
if(NULL == srtp_stream->rtp_auth.key){
zrtp_dk_deinit(dk_ctx);
zrtp_cipher_stop(&srtp_stream->rtp_cipher);
res = zrtp_status_fail;
break;
}
tmp_key.length = (uint16_t)srtp_stream->rtp_auth.key_len;
zrtp_derive_key(dk_ctx, label_rtp_msg_auth, (zrtp_stringn_t*)&tmp_key);
zrtp_memcpy(srtp_stream->rtp_auth.key, tmp_key.buffer, tmp_key.length);
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\tderive RTP auth key[%s]\n",
hex2str(tmp_key.buffer, tmp_key.length, buffer, sizeof(buffer))));
#endif
/*--------- init RTCP-items ----------------*/
srtp_stream->rtcp_cipher.cipher = profile->rtcp_policy.cipher;
tmp_key.length = (uint16_t) profile->rtcp_policy.cipher_key_len;
tmp_salt.length = profile->salt.length;
zrtp_derive_key(dk_ctx, label_rtcp_encryption, (zrtp_stringn_t*)&tmp_key);
zrtp_derive_key(dk_ctx, label_rtcp_salt, (zrtp_stringn_t*)&tmp_salt);
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\tderive RTCP encryption key[%s]\n",
hex2str(tmp_key.buffer, tmp_key.length, buffer, sizeof(buffer))));
ZRTP_LOG(3,(_ZTU_, "\t\tderive RTCP encryption salt[%s]\n",
hex2str(tmp_salt.buffer, tmp_salt.length, buffer, sizeof(buffer))));
#endif
srtp_stream->rtcp_cipher.ctx = zrtp_cipher_start(&srtp_stream->rtcp_cipher,
tmp_key.buffer,
tmp_salt.buffer,
ZRTP_CIPHER_MODE_CTR );
if(NULL == srtp_stream->rtcp_cipher.ctx){
zrtp_dk_deinit(dk_ctx);
zrtp_cipher_stop(&srtp_stream->rtp_cipher);
zrtp_sys_free(srtp_stream->rtp_auth.key);
res = zrtp_status_fail;
break;
}
srtp_stream->rtcp_auth.hash = profile->rtcp_policy.hash;
srtp_stream->rtcp_auth.key_len = profile->rtcp_policy.auth_key_len;
srtp_stream->rtcp_auth.tag_len = profile->rtcp_policy.auth_tag_len;
srtp_stream->rtcp_auth.key = zrtp_sys_alloc(srtp_stream->rtcp_auth.key_len);
if(NULL == srtp_stream->rtcp_auth.key){
zrtp_dk_deinit(dk_ctx);
zrtp_cipher_stop(&srtp_stream->rtp_cipher);
zrtp_sys_free(srtp_stream->rtp_auth.key);
zrtp_cipher_stop(&srtp_stream->rtcp_cipher);
res = zrtp_status_fail;
break;
}
tmp_key.length = (uint16_t)srtp_stream->rtcp_auth.key_len;
zrtp_derive_key(dk_ctx, label_rtcp_msg_auth, (zrtp_stringn_t*)&tmp_key);
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\t\tderive RTCP auth key[%s]\n",
hex2str(tmp_key.buffer, tmp_key.length, buffer, sizeof(buffer))));
#endif
zrtp_memcpy(srtp_stream->rtcp_auth.key, tmp_key.buffer, tmp_key.length);
zrtp_dk_deinit(dk_ctx);
zrtp_wipe_zstring(ZSTR_GV(tmp_key));
zrtp_wipe_zstring(ZSTR_GV(tmp_salt));
}while(0);
return res;
}
/*! \brief This function deinitializes stream context.
* \param srtp_global - pointer to SRTP engine global context
* \param srtp_stream - pointer to steam to deinitialize
*/
/*---------------------------------------------------------------------------*/
void zrtp_srtp_stream_deinit( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_stream_ctx_t *srtp_stream )
{
zrtp_cipher_stop(&srtp_stream->rtp_cipher);
zrtp_memset(srtp_stream->rtp_auth.key, 0, srtp_stream->rtp_auth.key_len);
zrtp_sys_free(srtp_stream->rtp_auth.key);
zrtp_cipher_stop(&srtp_stream->rtcp_cipher);
zrtp_memset(srtp_stream->rtcp_auth.key, 0, srtp_stream->rtcp_auth.key_len);
zrtp_sys_free(srtp_stream->rtcp_auth.key);
}
/*! \brief This function initializes SRTP session context.
* \param srtp_global - pointer to SRTP engine global context
* \param srtp_ctx - pointer to SRTP session context to initialize
* \param inc_profile - profile for incoming stream configuration;
* \param out_profile - profile for outgoing stream configuration.
* \return
* - zrtp_status_ok if stream has been initialized successfully
* - one of \ref zrtp_status_t errors - if error
*/
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_init_ctx( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_ctx_t *srtp_ctx,
zrtp_srtp_profile_t *inc_profile,
zrtp_srtp_profile_t *out_profile)
{
zrtp_status_t res = zrtp_status_ok;
do{
if(NULL == srtp_ctx || NULL == inc_profile || NULL == out_profile){
res = zrtp_status_bad_param;
break;
}
if(zrtp_status_ok != zrtp_srtp_stream_init(srtp_global, srtp_ctx->incoming_srtp, inc_profile)){
res = zrtp_status_fail;
break;
}
if(zrtp_status_ok != zrtp_srtp_stream_init(srtp_global, srtp_ctx->outgoing_srtp, out_profile)){
zrtp_srtp_stream_deinit(srtp_global, srtp_ctx->incoming_srtp);
res = zrtp_status_fail;
break;
}
}while(0);
return res;
}
/*===========================================================================*/
/* Public interface */
/*===========================================================================*/
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_init(zrtp_global_t *zrtp){
zrtp_srtp_global_t *srtp_global;
zrtp->srtp_global = NULL;
if(EXIT_SUCCESS != zrtp_bg_gen_tabs())
return zrtp_status_fail;
srtp_global = zrtp_sys_alloc(sizeof(zrtp_srtp_global_t));
if(NULL == srtp_global){
return zrtp_status_fail;
}
srtp_global->rp_ctx = rp_init();
if(NULL == srtp_global->rp_ctx){
zrtp_sys_free(srtp_global);
return zrtp_status_fail;
}
zrtp->srtp_global = srtp_global;
return zrtp_status_ok;
}
zrtp_status_t zrtp_srtp_down(zrtp_global_t *zrtp){
zrtp_srtp_global_t *srtp_global = zrtp->srtp_global;
rp_destroy(srtp_global->rp_ctx);
zrtp_sys_free(srtp_global);
zrtp->srtp_global = NULL;
return zrtp_status_ok;
}
zrtp_srtp_ctx_t * zrtp_srtp_create( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_profile_t *inc_profile,
zrtp_srtp_profile_t *out_profile)
{
zrtp_srtp_ctx_t *srtp_ctx = NULL;
if(NULL == inc_profile || NULL == out_profile){
return NULL;
}
do{
srtp_ctx = zrtp_srtp_alloc();
if(NULL == srtp_ctx){
break;
}
if(zrtp_status_ok != zrtp_srtp_init_ctx(srtp_global, srtp_ctx, inc_profile, out_profile)){
zrtp_srtp_free(srtp_ctx);
srtp_ctx = NULL;
break;
}
}while(0);
return srtp_ctx;
}
zrtp_status_t zrtp_srtp_destroy(zrtp_srtp_global_t *srtp_global, zrtp_srtp_ctx_t * srtp_ctx){
zrtp_status_t res = zrtp_status_ok;
remove_rp_nodes_by_srtp_ctx(srtp_ctx, srtp_global->rp_ctx);
zrtp_srtp_stream_deinit(srtp_global, srtp_ctx->incoming_srtp);
zrtp_srtp_stream_deinit(srtp_global, srtp_ctx->outgoing_srtp);
zrtp_srtp_free(srtp_ctx);
return res;
}
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_protect( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_ctx_t *srtp_ctx,
zrtp_rtp_info_t *packet)
{
zrtp_srtp_stream_ctx_t *srtp_stream_ctx = srtp_ctx->outgoing_srtp;
zrtp_rp_node_t *rp_node;
uint32_t *enc_start; /* pointer to start of encrypted portion */
uint32_t *auth_start; /* pointer to start of auth. portion */
unsigned enc_octet_len = 0; /* number of octets in encrypted portion */
uint8_t *auth_tag = NULL; /* location of auth_tag within packet */
zrtp_status_t status;
ZRTP_UNALIGNED(zrtp_rtp_hdr_t) *hdr;
zrtp_v128_t iv;
uint64_t packet_seq = 0;
zrtp_string64_t auth_tag_str = ZSTR_INIT_EMPTY(auth_tag_str);
void *hash_ctx = NULL;
/* add new replay protection node or get existing one */
rp_node = add_rp_node(srtp_ctx, srtp_global->rp_ctx, RP_OUTGOING_DIRECTION, packet->ssrc);
if(NULL == rp_node){
return zrtp_status_rp_fail;
}
/* check the packet length - it must at least contain a full header */
if (*(packet->length) < octets_in_rtp_header){
return zrtp_status_bad_param;
}
hdr = (zrtp_rtp_hdr_t*)(packet->packet);
enc_start = (uint32_t *)hdr + uint32s_in_rtp_header + hdr->cc;
if (1 == hdr->x) {
zrtp_rtp_hdr_xtnd_t *xtn_hdr = (zrtp_rtp_hdr_xtnd_t *)enc_start;
enc_start += (zrtp_ntoh16(xtn_hdr->length) + 1);
}
//WIN64
enc_octet_len = *(packet->length) - (uint32_t)((enc_start - (uint32_t *)hdr) << 2);
auth_start = (uint32_t *)hdr;
auth_tag = (uint8_t *)hdr + *(packet->length);
status = zrtp_srtp_rp_check(&rp_node->rtp_rp, packet);
if(zrtp_status_ok != status){
return zrtp_status_rp_fail;
}
zrtp_srtp_rp_add(&rp_node->rtp_rp, packet);
iv.v32[0] = 0;
iv.v32[1] = hdr->ssrc;
#ifdef ZRTP_NO_64BIT_MATH
iv.v64[1] = zrtp_hton64(make64((packet->seq) >> 16, (packet->seq) << 16));
#else
iv.v64[1] = zrtp_hton64(((uint64_t)(packet->seq)) << 16);
#endif
status = zrtp_cipher_set_iv(&srtp_stream_ctx->rtp_cipher, &iv);
if(status){
return zrtp_status_cipher_fail;
}
status = zrtp_cipher_encrypt(&srtp_stream_ctx->rtp_cipher, (unsigned char*)enc_start, enc_octet_len);
if(status){
return zrtp_status_cipher_fail;
}
/* shift est, put into network byte order */
packet_seq = packet->seq;
#ifdef ZRTP_NO_64BIT_MATH
packet_seq = zrtp_hton64(make64((high32(packet_seq) << 16) |
(low32(packet_seq) >> 16),
low32(packet_seq) << 16));
#else
packet_seq = zrtp_hton64(packet_seq << 16);
#endif
hash_ctx = srtp_stream_ctx->rtp_auth.hash->hmac_begin_c( srtp_stream_ctx->rtp_auth.hash,
(const char*)srtp_stream_ctx->rtp_auth.key,
srtp_stream_ctx->rtp_auth.key_len );
if(NULL == hash_ctx)
{
return zrtp_status_auth_fail;
}
status = srtp_stream_ctx->rtp_auth.hash->hmac_update( srtp_stream_ctx->rtp_auth.hash,
hash_ctx,
(const char*)auth_start,
*packet->length);
if(status)
{
return zrtp_status_auth_fail;
}
status = srtp_stream_ctx->rtp_auth.hash->hmac_update( srtp_stream_ctx->rtp_auth.hash,
hash_ctx,
(const char*)&packet_seq,
4);
if(status)
{
return zrtp_status_auth_fail;
}
status = srtp_stream_ctx->rtp_auth.hash->hmac_end( srtp_stream_ctx->rtp_auth.hash,
hash_ctx,
(zrtp_stringn_t*) &auth_tag_str,
srtp_stream_ctx->rtp_auth.tag_len->tag_length);
if(status)
{
return zrtp_status_auth_fail;
}
/* uncomment this for authentication debug */
#if ZRTP_DEBUG_SRTP_KEYS
{
char buff[256];
ZRTP_LOG(3,(_ZTU_,
"\tzrtp_srtp_protect authentication make: npacket_seq[%s] expected auth length[%i] result auth length[%i]\n",
hex2str((char*)&packet_seq, sizeof(packet_seq), buff, sizeof(buff)),
srtp_stream_ctx->rtp_auth.tag_len->tag_length,
auth_tag_str.length));
ZRTP_LOG(3,(_ZTU_, "\tauth tag[%s]\n",
hex2str(auth_tag_str.buffer, auth_tag_str.length, buff, sizeof(buff))));
}
#endif
zrtp_memcpy(auth_tag, auth_tag_str.buffer, auth_tag_str.length);
*packet->length += auth_tag_str.length;
return status;
}
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_unprotect( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_ctx_t *srtp_ctx,
zrtp_rtp_info_t *packet)
{
zrtp_srtp_stream_ctx_t *srtp_stream_ctx = srtp_ctx->incoming_srtp;
zrtp_rp_node_t *rp_node;
uint32_t *enc_start; /* pointer to start of encrypted portion */
uint32_t *auth_start; /* pointer to start of auth. portion */
unsigned enc_octet_len = 0; /* number of octets in encrypted portion */
uint8_t *auth_tag = NULL; /* location of auth_tag within packet */
zrtp_status_t status;
ZRTP_UNALIGNED(zrtp_rtp_hdr_t) *hdr = NULL;
void *hash_ctx = NULL;
zrtp_v128_t iv;
int tag_len = 0;
/*add new replay protection node or get existing one*/
rp_node = add_rp_node(srtp_ctx, srtp_global->rp_ctx, RP_INCOMING_DIRECTION, packet->ssrc);
if(NULL == rp_node){
return zrtp_status_rp_fail;
}
/* check the packet length - it must at least contain a full header */
if (*(packet->length) < octets_in_rtp_header)
{
return zrtp_status_bad_param;
}
hdr = (zrtp_rtp_hdr_t*)(packet->packet);
status = zrtp_srtp_rp_check(&rp_node->rtp_rp, packet);
if(zrtp_status_ok != status){
return zrtp_status_rp_fail;
}
iv.v32[0] = 0;
iv.v32[1] = hdr->ssrc;
#ifdef ZRTP_NO_64BIT_MATH
iv.v64[1] = zrtp_hton64(make64((packet->seq) >> 16, (packet->seq) << 16));
#else
iv.v64[1] = zrtp_hton64((uint64_t)(packet->seq) << 16);
#endif
status = zrtp_cipher_set_iv(&srtp_stream_ctx->rtp_cipher, &iv);
if(status){
return zrtp_status_cipher_fail;
}
tag_len = srtp_stream_ctx->rtp_auth.tag_len->tag_length;
hdr = (zrtp_rtp_hdr_t*)(packet->packet);
enc_start = (uint32_t *)hdr + uint32s_in_rtp_header + hdr->cc;
if (1 == hdr->x) {
zrtp_rtp_hdr_xtnd_t *xtn_hdr = (zrtp_rtp_hdr_xtnd_t *)enc_start;
enc_start += (zrtp_ntoh16(xtn_hdr->length) + 1);
}
//WIN64
enc_octet_len = *(packet->length) - tag_len - (uint32_t)((enc_start - (uint32_t *)hdr) << 2);
auth_start = (uint32_t *)hdr;
auth_tag = (uint8_t *)hdr + *(packet->length) - tag_len;
if(tag_len>0){
zrtp_string64_t auth_tag_str = ZSTR_INIT_EMPTY(auth_tag_str);
/* shift est, put into network byte order */
uint64_t packet_seq = packet->seq;
#ifdef ZRTP_NO_64BIT_MATH
packet_seq = zrtp_hton64( make64((high32(packet_seq) << 16) |
(low32(packet_seq) >> 16),
low32(packet_seq) << 16));
#else
packet_seq = zrtp_hton64(packet_seq << 16);
#endif
hash_ctx = srtp_stream_ctx->rtp_auth.hash->hmac_begin_c( srtp_stream_ctx->rtp_auth.hash,
(const char*)srtp_stream_ctx->rtp_auth.key,
srtp_stream_ctx->rtp_auth.key_len);
if(NULL == hash_ctx){
return zrtp_status_auth_fail;
}
status = srtp_stream_ctx->rtp_auth.hash->hmac_update( srtp_stream_ctx->rtp_auth.hash,
hash_ctx,
(const char*)auth_start,
*packet->length - tag_len);
if(status){
return zrtp_status_auth_fail;
}
status = srtp_stream_ctx->rtp_auth.hash->hmac_update( srtp_stream_ctx->rtp_auth.hash,
hash_ctx,
(const char*)&packet_seq,
4);
if(status){
return zrtp_status_auth_fail;
}
status = srtp_stream_ctx->rtp_auth.hash->hmac_end( srtp_stream_ctx->rtp_auth.hash,
hash_ctx,
(zrtp_stringn_t*) &auth_tag_str,
srtp_stream_ctx->rtp_auth.tag_len->tag_length);
#if ZRTP_DEBUG_SRTP_KEYS
{
char buff[256];
ZRTP_LOG(3,(_ZTU_,
"\tzrtp_srtp_unprotect authentication check. packet_seq[%s] expected auth length[%i] result auth length[%i]\n",
hex2str((char*)&packet_seq, sizeof(packet_seq), buff, sizeof(buff)),
srtp_stream_ctx->rtp_auth.tag_len->tag_length,
auth_tag_str.length));
ZRTP_LOG(3,(_ZTU_, "\tauth tag[%s]\n",
hex2str(auth_tag_str.buffer, auth_tag_str.length, buff, sizeof(buff))));
}
#endif
if(status || tag_len != auth_tag_str.length){
#if ZRTP_DEBUG_SRTP_KEYS
ZRTP_LOG(3,(_ZTU_, "\tAuthentication fail1: status[%i] auth_tag_length[%i] result auth_tag_len[%i]\n",
status, tag_len, auth_tag_str.length));
#endif
return zrtp_status_auth_fail;
}
if(0 != zrtp_memcmp((uint8_t *)auth_tag_str.buffer, (uint8_t *)auth_tag, tag_len)){
#if ZRTP_DEBUG_SRTP_KEYS
char buff[256], buff2[256];
ZRTP_LOG(3,(_ZTU_, "\tAuthentication fail2: tag[%s] computed_tag[%s]\n",
hex2str((uint8_t *)auth_tag, tag_len, buff, sizeof(buff)),
hex2str(auth_tag_str.buffer, auth_tag_str.length, buff2, sizeof(buff2))));
#endif
return zrtp_status_auth_fail;
}
}
status = zrtp_cipher_decrypt(&srtp_stream_ctx->rtp_cipher, (unsigned char*)enc_start, enc_octet_len);
if(status){
return zrtp_status_cipher_fail;
}
zrtp_srtp_rp_add(&rp_node->rtp_rp, packet);
*packet->length -= tag_len;
return status;
}
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_protect_rtcp( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_ctx_t *srtp_ctx,
zrtp_rtp_info_t *packet)
{
zrtp_srtp_stream_ctx_t *srtp_stream_ctx = srtp_ctx->outgoing_srtp;
zrtp_rp_node_t *rp_node;
uint32_t *enc_start; /* pointer to start of encrypted portion */
uint32_t *auth_start; /* pointer to start of auth. portion */
unsigned enc_octet_len = 0; /* number of octets in encrypted portion */
uint8_t *auth_tag = NULL; /* location of auth_tag within packet */
zrtp_status_t status;
ZRTP_UNALIGNED(zrtp_rtcp_hdr_t) *hdr;
ZRTP_UNALIGNED(uint32_t) *trailer; /* pointer to start of trailer */
uint32_t seq_num;
zrtp_v128_t iv;
zrtp_string64_t auth_tag_str = ZSTR_INIT_EMPTY(auth_tag_str);
/*add new replay protection node or get existing one*/
rp_node = add_rp_node(srtp_ctx, srtp_global->rp_ctx, RP_OUTGOING_DIRECTION, packet->ssrc);
if(NULL == rp_node){
return zrtp_status_rp_fail;
}
/* check the packet length - it must at least contain a full header */
if (*(packet->length) < octets_in_rtcp_header){
return zrtp_status_bad_param;
}
hdr = (zrtp_rtcp_hdr_t*)(packet->packet);
enc_start = (uint32_t *)hdr + uint32s_in_rtcp_header;
enc_octet_len = *(packet->length) - octets_in_rtcp_header;
/* all of the packet, except the header, gets encrypted */
/* NOTE: hdr->length is not usable - it refers to only the first
RTCP report in the compound packet! */
/* NOTE: trailer is 32-bit aligned because RTCP 'packets' are always
multiples of 32-bits (RFC 3550 6.1) */
trailer = (uint32_t *) ((char *)enc_start + enc_octet_len);
/*
* RFC gives us ability of using non-crypted RTCP packets
* but we encrypt them anyway. It may be option of stream
* context in the future.
* if no encryption is used trailer should contain 0x00000000
*/
*trailer = zrtp_hton32(ZRTP_RTCP_E_BIT); /* set encrypt bit */
/*
* set the auth_start and auth_tag pointers to the proper locations
* (note that srtpc *always* provides authentication, unlike srtp)
*/
/* Note: This would need to change for optional mikey data */
auth_start = (uint32_t *)hdr;
auth_tag = (uint8_t *)hdr + *(packet->length) + sizeof(zrtp_rtcp_trailer_t);
status = zrtp_srtp_rp_increment(&rp_node->rtcp_rp);
if(zrtp_status_ok != status){
return zrtp_status_rp_fail;
}
seq_num = zrtp_srtp_rp_get_value(&rp_node->rtcp_rp);
*trailer |= zrtp_hton32(seq_num);
packet->seq = seq_num;
iv.v32[0] = 0;
iv.v32[1] = hdr->ssrc;
iv.v32[2] = zrtp_hton32(seq_num >> 16);
iv.v32[3] = zrtp_hton32(seq_num << 16);
status = zrtp_cipher_set_iv(&srtp_stream_ctx->rtcp_cipher, &iv);
if(status){
return zrtp_status_cipher_fail;
}
status = zrtp_cipher_encrypt(&srtp_stream_ctx->rtcp_cipher, (unsigned char*)enc_start, enc_octet_len);
if(status){
return zrtp_status_cipher_fail;
}
status = srtp_stream_ctx->rtcp_auth.hash->hmac_truncated_c(srtp_stream_ctx->rtcp_auth.hash,
(const char*)srtp_stream_ctx->rtcp_auth.key,
srtp_stream_ctx->rtcp_auth.key_len,
(const char*)auth_start,
*packet->length + sizeof(zrtp_rtcp_trailer_t),
srtp_stream_ctx->rtcp_auth.tag_len->tag_length,
(zrtp_stringn_t*) &auth_tag_str);
if(status){
return zrtp_status_auth_fail;
}
zrtp_memcpy(auth_tag, auth_tag_str.buffer, auth_tag_str.length);
/* increase the packet length by the length of the auth tag and seq_num*/
*packet->length += (auth_tag_str.length + sizeof(zrtp_rtcp_trailer_t));
#if ZRTP_DEBUG_SRTP_KEYS
{
char buffer[1000];
ZRTP_LOG(3,(_ZTU_, "\tpacket: %s\n",
hex2str(packet->packet, (*packet->length) - (auth_tag_str.length + sizeof(zrtp_rtcp_trailer_t)), buffer, 1000)));
ZRTP_LOG(3,(_ZTU_, "\ttrailer and auth tag: %s\n",
hex2str((uint8_t*)packet->packet + ((*packet->length) - (auth_tag_str.length + sizeof(zrtp_rtcp_trailer_t))),
auth_tag_str.length + sizeof(zrtp_rtcp_trailer_t),
buffer, 1000)));
}
#endif
return status;
}
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_srtp_unprotect_rtcp( zrtp_srtp_global_t *srtp_global,
zrtp_srtp_ctx_t *srtp_ctx,
zrtp_rtp_info_t *packet)
{
zrtp_srtp_stream_ctx_t *srtp_stream_ctx = srtp_ctx->incoming_srtp;
zrtp_rp_node_t *rp_node;
uint32_t *enc_start; /* pointer to start of encrypted portion */
uint32_t *auth_start; /* pointer to start of auth. portion */
unsigned enc_octet_len = 0; /* number of octets in encrypted portion */
uint8_t *auth_tag = NULL; /* location of auth_tag within packet */
zrtp_status_t status;
ZRTP_UNALIGNED(zrtp_rtcp_hdr_t) *hdr;
ZRTP_UNALIGNED(uint32_t) *trailer; /* pointer to start of trailer */
int tag_len = 0;
zrtp_v128_t iv;
/* add new replay protection node or get existing one */
rp_node = add_rp_node(srtp_ctx, srtp_global->rp_ctx, RP_INCOMING_DIRECTION, packet->ssrc);
if(NULL == rp_node){
return zrtp_status_rp_fail;
}
/* check the packet length - it must at least contain a full header */
if (*(packet->length) < octets_in_rtcp_header){
return zrtp_status_bad_param;
}
tag_len = srtp_stream_ctx->rtcp_auth.tag_len->tag_length;
hdr = (zrtp_rtcp_hdr_t*)(packet->packet);
enc_octet_len = *packet->length -
(octets_in_rtcp_header + tag_len + sizeof(zrtp_rtcp_trailer_t));
/* index & E (encryption) bit follow normal data. hdr->len
is the number of words (32-bit) in the normal packet minus 1 */
/* This should point trailer to the word past the end of the
normal data. */
/* This would need to be modified for optional mikey data */
/*
* NOTE: trailer is 32-bit aligned because RTCP 'packets' are always
* multiples of 32-bits (RFC 3550 6.1)
*/
trailer = (uint32_t *) ((char *) hdr + *packet->length - (tag_len + sizeof(zrtp_rtcp_trailer_t)));
if (*((unsigned char *) trailer) & ZRTP_RTCP_E_BYTE_BIT) {
enc_start = (uint32_t *)hdr + uint32s_in_rtcp_header;
} else {
enc_octet_len = 0;
enc_start = NULL; /* this indicates that there's no encryption */
}
/*
* set the auth_start and auth_tag pointers to the proper locations
* (note that srtcp *always* uses authentication, unlike srtp)
*/
auth_start = (uint32_t *)hdr;
auth_tag = (uint8_t *)hdr + *packet->length - tag_len;
packet->seq = zrtp_ntoh32(*trailer) & 0x7fffffff;
status = zrtp_srtp_rp_check(&rp_node->rtcp_rp, packet);
if(zrtp_status_ok != status){
return zrtp_status_rp_fail;
}
iv.v32[0] = 0;
iv.v32[1] = hdr->ssrc; /* still in network order! */
iv.v32[2] = zrtp_hton32(packet->seq >> 16);
iv.v32[3] = zrtp_hton32(packet->seq << 16);
status = zrtp_cipher_set_iv(&srtp_stream_ctx->rtcp_cipher, &iv);
if(status){
return zrtp_status_cipher_fail;
}
if(tag_len>0){
zrtp_string64_t auth_tag_str = ZSTR_INIT_EMPTY(auth_tag_str);
status = srtp_stream_ctx->rtcp_auth.hash->hmac_truncated_c(srtp_stream_ctx->rtcp_auth.hash,
(const char*)srtp_stream_ctx->rtcp_auth.key,
srtp_stream_ctx->rtcp_auth.key_len,
(const char*)auth_start,
*packet->length - tag_len,
tag_len,
(zrtp_stringn_t*) &auth_tag_str);
if(status || tag_len != auth_tag_str.length){
return zrtp_status_auth_fail;
}
if(0 != zrtp_memcmp((uint8_t *)auth_tag_str.buffer, (uint8_t *)auth_tag, tag_len)){
return zrtp_status_auth_fail;
}
}else{
return zrtp_status_auth_fail;
}
if(enc_start){
status = zrtp_cipher_decrypt(&srtp_stream_ctx->rtcp_cipher, (unsigned char*)enc_start, enc_octet_len);
if(status){
return zrtp_status_cipher_fail;
}
}
zrtp_srtp_rp_add(&rp_node->rtcp_rp, packet);
*packet->length -= (tag_len + sizeof(zrtp_rtcp_trailer_t));
return status;
}
#endif /* !ZRTP_USE_EXTERN_SRTP */