1470 lines
46 KiB
C
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 */
|