freeswitch/src/zrtp_utils_proto.c
2012-03-31 22:56:16 +00:00

634 lines
19 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.
*
* Viktor Krykun <v.krikun at zfoneproject.com>
*/
#include "zrtp.h"
#define _ZTU_ "zrtp utils"
/*----------------------------------------------------------------------------*/
zrtp_status_t _zrtp_prepare_secrets(zrtp_session_t* session)
{
zrtp_secrets_t* sec = &session->secrets;
zrtp_status_t s = zrtp_status_ok;
/* Protect Secrets from race conditions on multistream calls. */
zrtp_mutex_lock(session->streams_protector);
if (!sec->is_ready) {
do {
uint32_t verifiedflag = 0;
session->secrets.rs1->_cachedflag = 0;
session->secrets.rs2->_cachedflag = 0;
if (session->zrtp->cb.cache_cb.on_get) {
s = session->zrtp->cb.cache_cb.on_get( ZSTR_GV(session->zid),
ZSTR_GV(session->peer_zid),
session->secrets.rs1,
0);
session->secrets.rs1->_cachedflag = (zrtp_status_ok == s);
s = session->zrtp->cb.cache_cb.on_get( ZSTR_GV(session->zid),
ZSTR_GV(session->peer_zid),
session->secrets.rs2,
1);
session->secrets.rs2->_cachedflag = (zrtp_status_ok == s);
}
if (session->zrtp->cb.cache_cb.on_get_verified) {
s = session->zrtp->cb.cache_cb.on_get_verified( ZSTR_GV(session->zid),
ZSTR_GV(session->peer_zid),
&verifiedflag);
}
if (session->zrtp->cb.cache_cb.on_get_mitm) {
s = session->zrtp->cb.cache_cb.on_get_mitm( ZSTR_GV(session->zid),
ZSTR_GV(session->peer_zid),
session->secrets.pbxs);
session->secrets.pbxs->_cachedflag = (zrtp_status_ok == s);
} else {
session->secrets.pbxs->_cachedflag = 0;
}
/* Duplicate all secrets flags to zrtp-context */
session->secrets.cached |= session->secrets.rs1->_cachedflag ? ZRTP_BIT_RS1 : 0;
session->secrets.cached |= session->secrets.rs2->_cachedflag ? ZRTP_BIT_RS2 : 0;
session->secrets.cached |= session->secrets.pbxs->_cachedflag ? ZRTP_BIT_PBX : 0;
{
char buff[128];
char buff2[128];
ZRTP_LOG(3,(_ZTU_,"\tRestoring Secrets: lZID=%s rZID=%s. V=%d sID=%u\n",
hex2str(session->zid.buffer, session->zid.length, buff, sizeof(buff)),
hex2str(session->peer_zid.buffer, session->peer_zid.length, buff2, sizeof(buff2)),
verifiedflag,
session->id));
ZRTP_LOG(3,(_ZTU_,"\t\tRS1 <%s>\n",
session->secrets.rs1->_cachedflag ?
hex2str( session->secrets.rs1->value.buffer,
session->secrets.rs1->value.length,
buff, sizeof(buff) ) : "EMPTY"));
ZRTP_LOG(3,(_ZTU_,"\t\tRS2 <%s>\n",
session->secrets.rs2->_cachedflag ?
hex2str( session->secrets.rs2->value.buffer,
session->secrets.rs2->value.length,
buff, sizeof(buff) ) : "EMPTY"));
ZRTP_LOG(3,(_ZTU_,"\t\tPBX <%s>\n",
session->secrets.pbxs->_cachedflag ?
hex2str( session->secrets.pbxs->value.buffer,
session->secrets.pbxs->value.length,
buff, sizeof(buff) ) : "EMPTY"));
}
sec->is_ready = 1;
s = zrtp_status_ok;
} while (0);
}
zrtp_mutex_unlock(session->streams_protector);
return s;
}
/*----------------------------------------------------------------------------*/
zrtp_shared_secret_t *_zrtp_alloc_shared_secret(zrtp_session_t* session)
{
zrtp_shared_secret_t *ss = zrtp_sys_alloc(sizeof(zrtp_shared_secret_t));
if (ss) {
zrtp_memset(ss, 0, sizeof(zrtp_shared_secret_t));
ZSTR_SET_EMPTY(ss->value);
ss->value.length = ZRTP_MIN(ss->value.max_length, ZRTP_RS_SIZE);
ss->lastused_at = (uint32_t)(zrtp_time_now()/1000);
ss->ttl = 0xFFFFFFFF;
ss->_cachedflag = 0;
ss->value.length = ZRTP_MIN(ss->value.max_length, ZRTP_RS_SIZE);
if (ss->value.length != zrtp_randstr( session->zrtp,
(unsigned char*)ss->value.buffer,
ss->value.length))
{
zrtp_sys_free(ss);
ss = NULL;
}
}
return ss;
}
/*----------------------------------------------------------------------------*/
int _zrtp_can_start_stream(zrtp_stream_t* stream, zrtp_stream_t **conc, zrtp_stream_mode_t mode)
{
uint8_t deny = 0;
mlist_t* node = NULL;
zrtp_mutex_lock(stream->zrtp->sessions_protector);
mlist_for_each(node, &stream->zrtp->sessions_head)
{
zrtp_session_t* tmp_sctx = mlist_get_struct(zrtp_session_t, _mlist, node);
if ( !zrtp_zstrcmp(ZSTR_GV(tmp_sctx->zid), ZSTR_GV(stream->session->zid)) &&
!zrtp_zstrcmp(ZSTR_GV(tmp_sctx->peer_zid), ZSTR_GV(stream->session->peer_zid)) )
{
int i = 0;
zrtp_mutex_lock(tmp_sctx->streams_protector);
for (i=0; i<ZRTP_MAX_STREAMS_PER_SESSION; i++)
{
zrtp_stream_t* tmp_stctx = &tmp_sctx->streams[i];
/*
* We don't need to lock the stream because it have been already locked
* by high level function: zrtp_process_srtp() or _initiating_secure()
*/
if ((stream != tmp_stctx) && (tmp_stctx->state != ZRTP_STATE_NONE)) {
deny = ( (tmp_stctx->state > ZRTP_STATE_START_INITIATINGSECURE) &&
(tmp_stctx->state < ZRTP_STATE_SECURE) );
if ((mode == ZRTP_STREAM_MODE_MULT) && deny) {
deny = !(tmp_stctx->mode == ZRTP_STREAM_MODE_MULT);
}
if (deny) {
*conc = tmp_stctx;
break;
}
}
}
zrtp_mutex_unlock(tmp_sctx->streams_protector);
if (deny) {
break;
}
}
}
zrtp_mutex_unlock(stream->zrtp->sessions_protector);
if (!deny){
*conc = NULL;
}
return !deny;
}
/*----------------------------------------------------------------------------*/
uint8_t _zrtp_choose_best_comp( zrtp_profile_t *profile,
zrtp_packet_Hello_t* peer_hello,
zrtp_crypto_comp_t type )
{
uint8_t* prof_elem = NULL;
int i=0, j=0;
int offset = 0;
int count = 0;
switch (type)
{
case ZRTP_CC_PKT:
{
uint8_t pref_peer_pk = ZRTP_COMP_UNKN;
uint8_t pref_pk = ZRTP_COMP_UNKN;
char *cp = NULL;
prof_elem = (uint8_t*)profile->pk_schemes;
offset = (peer_hello->hc + peer_hello->cc + peer_hello->ac) * ZRTP_COMP_TYPE_SIZE;
count = peer_hello->kc;
/* Looking for peer preferable DH scheme */
cp = (char*)peer_hello->comp + offset;
for (i=0; i<count; i++, cp+=ZRTP_COMP_TYPE_SIZE) {
uint8_t tmp_pref_peer_pk = zrtp_comp_type2id(type, cp);
j = 0;
while (prof_elem[j]) {
if (prof_elem[j++] == tmp_pref_peer_pk) {
pref_peer_pk = tmp_pref_peer_pk;
break;
}
}
if (ZRTP_COMP_UNKN != pref_peer_pk) {
break;
}
}
/* Looking for local preferable DH scheme */
i=0;
while (prof_elem[i]) {
uint8_t tmp_pref_pk = prof_elem[i++];
cp = (char*)peer_hello->comp + offset;
for (j=0; j<count; j++, cp+=ZRTP_COMP_TYPE_SIZE) {
if(tmp_pref_pk == zrtp_comp_type2id(type, cp)) {
pref_pk = tmp_pref_pk;
break;
}
}
if (ZRTP_COMP_UNKN != pref_pk) {
break;
}
}
ZRTP_LOG(3,(_ZTU_,"\t_zrtp_choose_best_comp() for PKT. local=%s remote=%s, choosen=%s\n",
zrtp_comp_id2type(type, pref_pk), zrtp_comp_id2type(type, pref_peer_pk), zrtp_comp_id2type(type, ZRTP_MIN(pref_peer_pk, pref_pk))));
/* Choose the fastest one. */
return ZRTP_MIN(pref_peer_pk, pref_pk);
} break;
case ZRTP_CC_HASH:
prof_elem = (uint8_t*)&profile->hash_schemes;
offset = 0;
count = peer_hello->hc;
break;
case ZRTP_CC_SAS:
prof_elem = (uint8_t*)profile->sas_schemes;
offset = (peer_hello->hc + peer_hello->cc + peer_hello->ac + peer_hello->kc)* ZRTP_COMP_TYPE_SIZE;
count = peer_hello->sc;
break;
case ZRTP_CC_CIPHER:
prof_elem = (uint8_t*)profile->cipher_types;
offset = peer_hello->hc * ZRTP_COMP_TYPE_SIZE;
count = peer_hello->cc;
break;
case ZRTP_CC_ATL:
prof_elem = (uint8_t*)profile->auth_tag_lens;
offset = (peer_hello->hc + peer_hello->cc)*ZRTP_COMP_TYPE_SIZE;
count = peer_hello->ac;
break;
default:
return ZRTP_COMP_UNKN;
}
while (prof_elem[i])
{
char *cp = (char*)peer_hello->comp + offset;
uint8_t comp_id = prof_elem[i++];
for (j=0; j<count; j++, cp+=ZRTP_COMP_TYPE_SIZE) {
if (comp_id == zrtp_comp_type2id(type, cp)) {
return comp_id;
}
}
}
return ZRTP_COMP_UNKN;
}
/*----------------------------------------------------------------------------*/
static int _is_presh_in_hello(zrtp_packet_Hello_t* hello)
{
int i = 0;
char* cp = (char*)hello->comp + (hello->hc + hello->cc + hello->ac) * ZRTP_COMP_TYPE_SIZE;
for (i=0; i < hello->kc; i++, cp+=ZRTP_COMP_TYPE_SIZE) {
if (!zrtp_memcmp(cp, ZRTP_PRESHARED, ZRTP_COMP_TYPE_SIZE)) {
return i;
}
}
return -1;
}
int _zrtp_is_dh_in_session(zrtp_stream_t* stream)
{
uint8_t i = 0;
for (i=0; i< ZRTP_MAX_STREAMS_PER_SESSION; i++) {
zrtp_stream_t *tmp_stream = &stream->session->streams[i];
if ((tmp_stream != stream) && ZRTP_IS_STREAM_DH(tmp_stream)) {
return 0;
}
}
return -1;
}
zrtp_stream_mode_t _zrtp_define_stream_mode(zrtp_stream_t* stream)
{
zrtp_session_t* session = stream->session;
/*
* If ZRTP Session key is available - use Multistream mode.
* If both sides ready for Preshared and we have RS1 and it has Verified flag - try Preshared.
* Use DH in other cases
*/
if (session->zrtpsess.length > 0) {
stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, ZRTP_PKTYPE_MULT, session->zrtp);
return ZRTP_STREAM_MODE_MULT;
} else {
/* If both sides ready for Preshared and we have RSes in our cache - try Preshared. */
if (ZRTP_PKTYPE_PRESH == stream->pubkeyscheme->base.id)
{
do {
uint32_t verifiedflag = 0;
uint32_t calls_counter = 0;
if (_is_presh_in_hello(&stream->messages.peer_hello) < 0) {
break;
}
if (ZRTP_IS_STREAM_PRESH(stream) && session->zrtp->cb.cache_cb.on_presh_counter_get) {
session->zrtp->cb.cache_cb.on_presh_counter_get( ZSTR_GV(session->zid),
ZSTR_GV(session->peer_zid),
&calls_counter);
if (calls_counter >= ZRTP_PRESHARED_MAX_ALLOWED) {
ZRTP_LOG(3,(_ZTU_,"\tDefine stream mode: user wants PRESHARED but Preshared"
"calls counter reached the maximum value (ID=%u) - Reset to DH.\n", stream->id));
break;
}
}
if (session->zrtp->cb.cache_cb.on_get_verified) {
session->zrtp->cb.cache_cb.on_get_verified( ZSTR_GV(session->zid),
ZSTR_GV(session->peer_zid),
&verifiedflag);
}
if (!session->secrets.rs1->_cachedflag || !verifiedflag) {
ZRTP_LOG(3,(_ZTU_,"\tDefine stream mode: user wants PRESHARED but we HAVE "
"RS1=%d and V=%d. Reset to DH. ID=%u\n", session->secrets.rs1->_cachedflag, verifiedflag, stream->id));
break;
}
ZRTP_LOG(3,(_ZTU_,"\tDefine stream mode: user wants PRESHARED and we have RS1,"
" calls_counter=%d. Use preshared. ID=%u\n", calls_counter, stream->id));
return ZRTP_STREAM_MODE_PRESHARED;
} while (0);
}
/* If Preshared not accepted by some reaseon - choose appropriate DH scheme. */
if ( (ZRTP_PKTYPE_PRESH == stream->pubkeyscheme->base.id) ||
(ZRTP_PKTYPE_MULT == stream->pubkeyscheme->base.id) )
{
int i=0, j=0;
zrtp_packet_Hello_t* phello = &stream->messages.peer_hello;
uint8_t comp_id = ZRTP_COMP_UNKN;
while (session->profile.pk_schemes[i])
{
char *cp = (char*)phello->comp + (phello->hc + phello->cc + phello->ac) * ZRTP_COMP_TYPE_SIZE;
comp_id = session->profile.pk_schemes[i++];
if ((comp_id != ZRTP_PKTYPE_PRESH) && (comp_id != ZRTP_PKTYPE_MULT))
{
for (j=0; j<phello->kc; j++, cp+=ZRTP_COMP_TYPE_SIZE) {
if (comp_id == zrtp_comp_type2id(ZRTP_CC_PKT, cp)) {
break;
}
}
if (j != phello->kc) {
break;
}
}
}
stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, comp_id, session->zrtp);
}
return ZRTP_STREAM_MODE_DH;
}
}
/*---------------------------------------------------------------------------*/
int _zrtp_validate_message_hmac( zrtp_stream_t *stream,
zrtp_msg_hdr_t* msg2check,
char* hmackey)
{
zrtp_string32_t hash_str = ZSTR_INIT_EMPTY(hash_str);
zrtp_hash_t *hash = zrtp_comp_find(ZRTP_CC_HASH, ZRTP_HASH_SHA256, stream->session->zrtp);
hash->hmac_truncated_c( hash,
hmackey,
ZRTP_MESSAGE_HASH_SIZE,
(char*)msg2check,
zrtp_ntoh16(msg2check->length)*4 - ZRTP_HMAC_SIZE,
ZRTP_HMAC_SIZE,
ZSTR_GV(hash_str));
if (0 != zrtp_memcmp((char*)msg2check + (zrtp_ntoh16(msg2check->length)*4 - ZRTP_HMAC_SIZE), hash_str.buffer, ZRTP_HMAC_SIZE))
{
if (stream->zrtp->cb.event_cb.on_zrtp_security_event) {
stream->zrtp->cb.event_cb.on_zrtp_security_event(stream, ZRTP_EVENT_WRONG_MESSAGE_HMAC);
}
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_wrong_meshmac, 0);
return -1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_compute_preshared_key( zrtp_session_t *session,
zrtp_stringn_t* rs1,
zrtp_stringn_t* auxs,
zrtp_stringn_t* pbxs,
zrtp_stringn_t* key,
zrtp_stringn_t* key_id)
{
static const zrtp_string8_t presh_key_str = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_COMMIT_HV_KEY_STR);
zrtp_string32_t preshared_key = ZSTR_INIT_EMPTY(preshared_key);
static uint32_t length_rs = ZRTP_RS_SIZE;
static const uint32_t length_zero = 0;
void *hash_ctx = session->hash->hash_begin(session->hash);
if (!hash_ctx) {
return zrtp_status_alloc_fail;
}
length_rs = zrtp_hton32(length_rs);
if (rs1) {
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&length_rs, 4);
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)rs1->buffer, ZRTP_RS_SIZE);
} else {
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&length_zero, 4);
}
if (auxs) {
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&length_rs, 4);
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)auxs->buffer, ZRTP_RS_SIZE);
} else {
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&length_zero, 4);
}
if (pbxs) {
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&length_rs, 4);
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)pbxs->buffer, ZRTP_RS_SIZE);
} else {
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&length_zero, 4);
}
session->hash->hash_end(session->hash, hash_ctx, ZSTR_GV(preshared_key));
if (key) {
zrtp_zstrcpy(ZSTR_GVP(key), ZSTR_GV(preshared_key));
}
if (key_id) {
session->hash->hmac_truncated( session->hash,
ZSTR_GV(preshared_key),
ZSTR_GV(presh_key_str),
ZRTP_HV_KEY_SIZE,
ZSTR_GVP(key_id));
}
return zrtp_status_ok;
}
/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_kdf( zrtp_stream_t* stream,
zrtp_stringn_t* ki,
zrtp_stringn_t* label,
zrtp_stringn_t* context,
uint32_t length,
zrtp_stringn_t* digest)
{
/*KDF(KI, Label, Context, L) = HMAC(KI, i | Label | 0x00 | Context | L) */
uint32_t i = 1;
uint8_t o = 0;
uint32_t L = zrtp_hton32(length*8);
zrtp_hash_t* hash = stream->session->hash;
void* ctx = hash->hmac_begin(hash, ki);
if (!ctx) {
return zrtp_status_alloc_fail;
}
i = zrtp_hton32(i);
hash->hmac_update(hash, ctx, (const char*)&i, sizeof(i));
hash->hmac_update(hash, ctx, label->buffer, label->length);
hash->hmac_update(hash, ctx, (const char*)&o, sizeof(o));
hash->hmac_update(hash, ctx, context->buffer, context->length);
hash->hmac_update(hash, ctx, (const char*)&L, sizeof(L));
hash->hmac_end(hash, ctx, digest, length);
return zrtp_status_ok;
}
/*---------------------------------------------------------------------------*/
zrtp_status_t zrtp_verified_set( zrtp_global_t *zrtp,
zrtp_string16_t *zid1,
zrtp_string16_t *zid2,
uint8_t verified )
{
mlist_t *node = NULL;
if (!zrtp) {
return zrtp_status_bad_param;
}
zrtp_mutex_lock(zrtp->sessions_protector);
mlist_for_each(node, &zrtp->sessions_head)
{
zrtp_session_t *session = mlist_get_struct(zrtp_session_t, _mlist, node);
if ( ( !zrtp_zstrcmp(ZSTR_GV(session->zid), ZSTR_GVP(zid1)) ||
!zrtp_zstrcmp(ZSTR_GV(session->zid), ZSTR_GVP(zid2)) ) &&
( !zrtp_zstrcmp(ZSTR_GV(session->peer_zid), ZSTR_GVP(zid1)) ||
!zrtp_zstrcmp(ZSTR_GV(session->peer_zid), ZSTR_GVP(zid2)) ) )
{
if (session->zrtp->cb.cache_cb.on_set_verified) {
session->zrtp->cb.cache_cb.on_set_verified(ZSTR_GVP(zid1), ZSTR_GVP(zid2), verified);
}
if (session->mitm_alert_detected) {
session->mitm_alert_detected = 0;
if (session->zrtp->cb.cache_cb.on_put) {
session->zrtp->cb.cache_cb.on_put( ZSTR_GV(session->zid),
ZSTR_GV(session->peer_zid),
session->secrets.rs1);
}
}
}
}
zrtp_mutex_unlock(zrtp->sessions_protector);
return zrtp_status_ok;
}
/*----------------------------------------------------------------------------*/
uint32_t _zrtp_get_timeout(uint32_t curr_timeout, zrtp_msg_type_t msg)
{
uint32_t timeout = curr_timeout;
uint32_t base_interval = 0;
uint32_t capping = 0;
#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
uint8_t is_lineral = 1;
capping = 10000;
#else
uint8_t is_lineral = 0;
#endif
switch (msg)
{
case ZRTP_NONE:
case ZRTP_HELLOACK:
case ZRTP_DHPART1:
case ZRTP_CONFIRM1:
case ZRTP_CONFIRM2ACK:
case ZRTP_GOCLEARACK:
case ZRTP_RELAYACK:
return 0;
#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
case ZRTP_HELLO:
base_interval = ZRTP_CSD_T1;
break;
case ZRTP_COMMIT:
base_interval = ZRTP_CSD_T2;
break;
case ZRTP_DHPART2:
base_interval = ZRTP_CSD_T3;
break;
case ZRTP_CONFIRM2:
base_interval = ZRTP_CSD_T4;
break;
case ZRTP_GOCLEAR:
case ZRTP_SASRELAY:
base_interval = ZRTP_CSD_T2;
break;
case ZRTP_ERROR:
base_interval = ZRTP_CSD_ET;
break;
#else
case ZRTP_HELLO:
base_interval = ZRTP_T1;
capping = ZRTP_T1_CAPPING;
break;
case ZRTP_COMMIT:
case ZRTP_DHPART2:
case ZRTP_CONFIRM2:
case ZRTP_GOCLEAR:
case ZRTP_SASRELAY:
base_interval = ZRTP_T2;
capping = ZRTP_T2_CAPPING;
break;
case ZRTP_ERROR:
case ZRTP_ERRORACK:
base_interval = ZRTP_ET;
capping = ZRTP_T2_CAPPING;
break;
#endif
case ZRTP_PROCESS:
base_interval = ZRTP_PROCESS_T1;
break;
default:
return 0;
}
if (0 == timeout) {
timeout = base_interval;
} else if (!is_lineral) {
timeout *= 2;
} else {
timeout += base_interval;
}
if (timeout > capping) {
return capping;
} else {
return timeout;
}
}