/* * 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 */ #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