1405 lines
51 KiB
C
1405 lines
51 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 protocol"
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* PROTOCOL Logic */
|
|
/*===========================================================================*/
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static zrtp_status_t _attach_secret( zrtp_session_t *session,
|
|
zrtp_proto_secret_t* psec,
|
|
zrtp_shared_secret_t* sec,
|
|
uint8_t is_initiator)
|
|
{
|
|
zrtp_uchar32_t buff;
|
|
static const zrtp_string16_t initiator = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_ROLE_INITIATOR);
|
|
static const zrtp_string16_t responder = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_ROLE_RESPONDER);
|
|
|
|
const zrtp_string16_t* role = is_initiator ? &initiator : &responder;
|
|
const zrtp_string16_t* his_role = is_initiator ? &responder : &initiator;
|
|
|
|
ZSTR_SET_EMPTY(psec->id);
|
|
ZSTR_SET_EMPTY(psec->peer_id);
|
|
psec->secret = sec;
|
|
|
|
/*
|
|
* If secret's value is available (from the cache or from SIP) - use hmac;
|
|
* use zero-strings in other case.
|
|
*/
|
|
if (psec->secret) {
|
|
session->hash->hmac_truncated( session->hash,
|
|
ZSTR_GV(sec->value),
|
|
ZSTR_GVP(role),
|
|
ZRTP_RSID_SIZE,
|
|
ZSTR_GV(psec->id));
|
|
|
|
session->hash->hmac_truncated( session->hash,
|
|
ZSTR_GV(sec->value),
|
|
ZSTR_GVP(his_role),
|
|
ZRTP_RSID_SIZE,
|
|
ZSTR_GV(psec->peer_id));
|
|
} else {
|
|
psec->id.length = ZRTP_RSID_SIZE;
|
|
zrtp_memset(psec->id.buffer, 0, psec->id.length);
|
|
|
|
psec->peer_id.length = ZRTP_RSID_SIZE;
|
|
zrtp_memset(psec->peer_id.buffer, 0, psec->peer_id.length);
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tAttach RS id=%s.\n",
|
|
hex2str((const char*)psec->id.buffer, psec->id.length, (char*)buff, sizeof(buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\tAttach RS peer_id=%s.\n",
|
|
hex2str((const char*)psec->peer_id.buffer, psec->peer_id.length, (char*)buff, sizeof(buff))));
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
zrtp_status_t _zrtp_protocol_init(zrtp_stream_t *stream, uint8_t is_initiator, zrtp_protocol_t **protocol)
|
|
{
|
|
zrtp_protocol_t *new_proto = NULL;
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tInit %s Protocol ID=%u mode=%s...\n",
|
|
is_initiator ? "INITIATOR's" : "RESPONDER's", stream->id, zrtp_log_mode2str(stream->mode)));
|
|
|
|
/* Destroy previous protocol structure (Responder or Preshared) */
|
|
if (*protocol) {
|
|
_zrtp_protocol_destroy(*protocol);
|
|
*protocol = NULL;
|
|
}
|
|
|
|
/* Allocate memory for all branching structures */
|
|
do
|
|
{
|
|
new_proto = zrtp_sys_alloc(sizeof(zrtp_protocol_t));
|
|
if (!new_proto) {
|
|
s = zrtp_status_alloc_fail;
|
|
break;
|
|
}
|
|
zrtp_memset(new_proto, 0, sizeof(zrtp_protocol_t));
|
|
|
|
new_proto->cc = zrtp_sys_alloc(sizeof(zrtp_proto_crypto_t));
|
|
if (!new_proto->cc) {
|
|
s = zrtp_status_alloc_fail;
|
|
break;
|
|
}
|
|
zrtp_memset(new_proto->cc, 0, sizeof(zrtp_proto_crypto_t));
|
|
|
|
/* Create and Initialize DH crypto context (for DH streams only) */
|
|
if (ZRTP_IS_STREAM_DH(stream)) {
|
|
if (stream->dh_cc.initialized_with != stream->pubkeyscheme->base.id) {
|
|
stream->pubkeyscheme->initialize(stream->pubkeyscheme, &stream->dh_cc);
|
|
stream->dh_cc.initialized_with = stream->pubkeyscheme->base.id;
|
|
}
|
|
}
|
|
|
|
/* Initialize main structure at first: functions pointers and generate nonce */
|
|
new_proto->type = is_initiator ? ZRTP_STATEMACHINE_INITIATOR : ZRTP_STATEMACHINE_RESPONDER;
|
|
new_proto->context = stream;
|
|
|
|
/* Initialize protocol crypto context and prepare it for further usage */
|
|
ZSTR_SET_EMPTY(new_proto->cc->kdf_context);
|
|
ZSTR_SET_EMPTY(new_proto->cc->s0);
|
|
ZSTR_SET_EMPTY(new_proto->cc->mes_hash);
|
|
ZSTR_SET_EMPTY(new_proto->cc->hv);
|
|
ZSTR_SET_EMPTY(new_proto->cc->peer_hv);
|
|
|
|
if (ZRTP_IS_STREAM_DH(stream)) {
|
|
_attach_secret(stream->session, &new_proto->cc->rs1, stream->session->secrets.rs1, is_initiator);
|
|
_attach_secret(stream->session, &new_proto->cc->rs2, stream->session->secrets.rs2, is_initiator);
|
|
_attach_secret(stream->session, &new_proto->cc->auxs, stream->session->secrets.auxs, is_initiator);
|
|
_attach_secret(stream->session, &new_proto->cc->pbxs, stream->session->secrets.pbxs, is_initiator);
|
|
}
|
|
|
|
s = zrtp_status_ok;
|
|
*protocol = new_proto;
|
|
} while (0);
|
|
|
|
if (s != zrtp_status_ok) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_protocol_attach() with code %s.\n", zrtp_log_status2str(s)));
|
|
if (new_proto && new_proto->cc) {
|
|
zrtp_sys_free(new_proto->cc);
|
|
}
|
|
if (new_proto) {
|
|
zrtp_sys_free(new_proto);
|
|
}
|
|
*protocol = NULL;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static void clear_crypto_sources(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_protocol_t* proto = stream->protocol;
|
|
if (proto && proto->cc) {
|
|
zrtp_memset(proto->cc, 0, sizeof(zrtp_proto_crypto_t));
|
|
zrtp_sys_free(proto->cc);
|
|
proto->cc = 0;
|
|
}
|
|
}
|
|
|
|
void _zrtp_protocol_destroy(zrtp_protocol_t *proto)
|
|
{
|
|
/* Clear protocol crypto values, destroy SRTP unit, clear and release memory. */
|
|
if (proto) {
|
|
/* if protocol is being destroyed by exception, ->context may be NULL */
|
|
if (proto->context) {
|
|
_zrtp_cancel_send_packet_later(proto->context, ZRTP_NONE);
|
|
if (proto->_srtp) {
|
|
zrtp_srtp_destroy(proto->context->zrtp->srtp_global, proto->_srtp);
|
|
}
|
|
}
|
|
|
|
clear_crypto_sources(proto->context);
|
|
zrtp_memset(proto, 0, sizeof(zrtp_protocol_t));
|
|
zrtp_sys_free(proto);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_protocol_encrypt( zrtp_protocol_t *proto,
|
|
zrtp_rtp_info_t *packet,
|
|
uint8_t is_rtp)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
if (is_rtp) {
|
|
s = zrtp_srtp_protect(proto->context->zrtp->srtp_global, proto->_srtp, packet);
|
|
} else {
|
|
s = zrtp_srtp_protect_rtcp(proto->context->zrtp->srtp_global, proto->_srtp, packet);
|
|
}
|
|
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_UNALIGNED(zrtp_rtp_hdr_t) *hdr = (zrtp_rtp_hdr_t*) packet->packet;
|
|
|
|
ZRTP_LOG(2,(_ZTU_,"ERROR! Encrypt failed. ID=%u:%s s=%s (%s size=%d ssrc=%u seq=%d pt=%d)\n",
|
|
proto->context->id,
|
|
zrtp_log_mode2str(proto->context->mode),
|
|
zrtp_log_status2str(s),
|
|
is_rtp ? "RTP" : "RTCP",
|
|
*packet->length,
|
|
zrtp_ntoh32(hdr->ssrc),
|
|
zrtp_ntoh16(hdr->seq),
|
|
hdr->pt));
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_protocol_decrypt( zrtp_protocol_t *proto,
|
|
zrtp_rtp_info_t *packet,
|
|
uint8_t is_rtp)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
if (is_rtp) {
|
|
s = zrtp_srtp_unprotect(proto->context->zrtp->srtp_global, proto->_srtp, packet);
|
|
} else {
|
|
s = zrtp_srtp_unprotect_rtcp(proto->context->zrtp->srtp_global, proto->_srtp, packet);
|
|
}
|
|
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_UNALIGNED(zrtp_rtp_hdr_t) *hdr = (zrtp_rtp_hdr_t*) packet->packet;
|
|
ZRTP_LOG(2,(_ZTU_,"ERROR! Decrypt failed. ID=%u:%s s=%s (%s size=%d ssrc=%u seq=%u/%u pt=%d)\n",
|
|
proto->context->id,
|
|
zrtp_log_mode2str(proto->context->mode),
|
|
zrtp_log_status2str(s),
|
|
is_rtp ? "RTP" : "RTCP",
|
|
*packet->length,
|
|
zrtp_ntoh32(hdr->ssrc),
|
|
zrtp_ntoh16(hdr->seq),
|
|
packet->seq,
|
|
hdr->pt));
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* CRYPTO Utilites */
|
|
/*===========================================================================*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static zrtp_status_t _derive_s0(zrtp_stream_t* stream, int is_initiator)
|
|
{
|
|
static const zrtp_string32_t zrtp_kdf_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_KDF_STR);
|
|
static const zrtp_string32_t zrtp_sess_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_SESS_STR);
|
|
static const zrtp_string32_t zrtp_multi_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_MULTI_STR);
|
|
static const zrtp_string32_t zrtp_presh_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_PRESH_STR);
|
|
|
|
zrtp_session_t *session = stream->session;
|
|
zrtp_secrets_t* secrets = &session->secrets;
|
|
zrtp_proto_crypto_t* cc = stream->protocol->cc;
|
|
void* hash_ctx = NULL;
|
|
char print_buff[256];
|
|
|
|
switch (stream->mode)
|
|
{
|
|
/*
|
|
* S0 computing for FULL DH exchange
|
|
* S0 computing. s0 is the master shared secret used for all
|
|
* cryptographic operations. In particular, note the inclusion
|
|
* of "total_hash", a hash of all packets exchanged up to this
|
|
* point. This belatedly detects any tampering with earlier
|
|
* packets, e.g. bid-down attacks.
|
|
*
|
|
* s0 = hash( 1 | DHResult | "ZRTP-HMAC-KDF" | ZIDi | ZIDr |
|
|
* total_hash | len(s1) | s1 | len(s2) | s2 | len(s3) | s3 )
|
|
* The constant 1 and all lengths are 32 bits big-endian values.
|
|
* The fields without length prefixes are fixed-witdh:
|
|
* - DHresult is fixed to the width of the DH prime.
|
|
* - The hash type string and ZIDs are fixed width.
|
|
* - total_hash is fixed by the hash negotiation.
|
|
* The constant 1 is per NIST SP 800-56A section 5.8.1, and is
|
|
* a counter which can be incremented to generate more than 256
|
|
* bits of key material.
|
|
* ========================================================================
|
|
*/
|
|
case ZRTP_STREAM_MODE_DH:
|
|
{
|
|
zrtp_proto_secret_t *C[3] = { 0, 0, 0};
|
|
int i = 0;
|
|
uint32_t comp_length = 0;
|
|
zrtp_stringn_t *zidi = NULL, *zidr = NULL;
|
|
struct BigNum dhresult;
|
|
#if (defined(ZRTP_USE_STACK_MINIM) && (ZRTP_USE_STACK_MINIM == 1))
|
|
zrtp_uchar1024_t* buffer = zrtp_sys_alloc( sizeof(zrtp_uchar1024_t) );
|
|
if (!buffer) {
|
|
return zrtp_status_alloc_fail;
|
|
}
|
|
#else
|
|
zrtp_uchar1024_t holder;
|
|
zrtp_uchar1024_t* buffer = &holder;
|
|
#endif
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tDERIVE S0 from DH exchange and RS secrets...\n"));
|
|
ZRTP_LOG(3,(_ZTU_,"\t my rs1ID:%s\n", hex2str(cc->rs1.id.buffer, cc->rs1.id.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t his rs1ID:%s\n", hex2str((const char*)stream->messages.peer_dhpart.rs1ID, ZRTP_RSID_SIZE, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t his rs1ID comp:%s\n", hex2str(cc->rs1.peer_id.buffer, cc->rs1.peer_id.length, print_buff, sizeof(print_buff))));
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\t my rs2ID:%s\n", hex2str(cc->rs2.id.buffer, cc->rs2.id.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t his rs2ID:%s\n", hex2str((const char*)stream->messages.peer_dhpart.rs2ID, ZRTP_RSID_SIZE, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t his rs2ID comp:%s\n", hex2str(cc->rs2.peer_id.buffer, cc->rs2.peer_id.length, print_buff, sizeof(print_buff))));
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\t my pbxsID:%s\n", hex2str(cc->pbxs.id.buffer, cc->pbxs.id.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t his pbxsID:%s\n", hex2str((const char*)stream->messages.peer_dhpart.pbxsID, ZRTP_RSID_SIZE, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\this pbxsID comp:%s\n", hex2str(cc->pbxs.peer_id.buffer, cc->pbxs.peer_id.length, print_buff, sizeof(print_buff))));
|
|
|
|
hash_ctx = session->hash->hash_begin(session->hash);
|
|
if (0 == hash_ctx) {
|
|
ZRTP_LOG(1,(_ZTU_, "\tERROR! can't start hash calculation for S0 computing. ID=%u.\n", stream->id));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/*
|
|
* NIST requires a 32-bit big-endian integer counter to be included
|
|
* in the hash each time the hash is computed, which we have set to
|
|
* the fixed value of 1, because we only compute the hash once.
|
|
*/
|
|
comp_length = zrtp_hton32(1L);
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&comp_length, 4);
|
|
|
|
|
|
switch (stream->pubkeyscheme->base.id) {
|
|
case ZRTP_PKTYPE_DH2048:
|
|
case ZRTP_PKTYPE_DH3072:
|
|
case ZRTP_PKTYPE_DH4096:
|
|
comp_length = stream->pubkeyscheme->pv_length;
|
|
ZRTP_LOG(3,(_ZTU_,"DH comp_length=%u\n", comp_length));
|
|
break;
|
|
case ZRTP_PKTYPE_EC256P:
|
|
case ZRTP_PKTYPE_EC384P:
|
|
case ZRTP_PKTYPE_EC521P:
|
|
comp_length = stream->pubkeyscheme->pv_length/2;
|
|
ZRTP_LOG(3,(_ZTU_,"ECDH comp_length=%u\n", comp_length));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
bnBegin(&dhresult);
|
|
stream->pubkeyscheme->compute(stream->pubkeyscheme,
|
|
&stream->dh_cc,
|
|
&dhresult,
|
|
&stream->dh_cc.peer_pv);
|
|
|
|
bnExtractBigBytes(&dhresult, (uint8_t *)buffer, 0, comp_length);
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)buffer, comp_length);
|
|
bnEnd(&dhresult);
|
|
|
|
#if (defined(ZRTP_USE_STACK_MINIM) && (ZRTP_USE_STACK_MINIM == 1))
|
|
zrtp_sys_free(buffer);
|
|
#endif
|
|
|
|
/* Add "ZRTP-HMAC-KDF" to the S0 hash */
|
|
session->hash->hash_update( session->hash, hash_ctx,
|
|
(const int8_t*)&zrtp_kdf_label.buffer,
|
|
zrtp_kdf_label.length);
|
|
|
|
/* Then Initiator's and Responder's ZIDs */
|
|
if (stream->protocol->type == ZRTP_STATEMACHINE_INITIATOR) {
|
|
zidi = ZSTR_GV(stream->session->zid);
|
|
zidr = ZSTR_GV(stream->session->peer_zid);
|
|
} else {
|
|
zidr = ZSTR_GV(stream->session->zid);
|
|
zidi = ZSTR_GV(stream->session->peer_zid);
|
|
}
|
|
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&zidi->buffer, zidi->length);
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&zidr->buffer, zidr->length);
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&cc->mes_hash.buffer, cc->mes_hash.length);
|
|
|
|
/* If everything is OK - RS1 should much */
|
|
if (!zrtp_memcmp(cc->rs1.peer_id.buffer, stream->messages.peer_dhpart.rs1ID, ZRTP_RSID_SIZE))
|
|
{
|
|
C[0] = &cc->rs1;
|
|
secrets->matches |= ZRTP_BIT_RS1;
|
|
}
|
|
/* If we have lost our RS1 - remote party should use backup (RS2) instead */
|
|
else if (!zrtp_memcmp(cc->rs1.peer_id.buffer, stream->messages.peer_dhpart.rs2ID, ZRTP_RSID_SIZE))
|
|
{
|
|
C[0] = &cc->rs1;
|
|
secrets->matches |= ZRTP_BIT_RS1;
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO! We have lost our RS1 from previous broken exchange"
|
|
" - remote party will use RS2 backup. ID=%u\n", stream->id));
|
|
}
|
|
/* If remote party lost it's secret - we will use backup */
|
|
else if (!zrtp_memcmp(cc->rs2.peer_id.buffer, stream->messages.peer_dhpart.rs1ID, ZRTP_RSID_SIZE))
|
|
{
|
|
C[0] = &cc->rs2;
|
|
cc->rs1 = cc->rs2;
|
|
secrets->matches |= ZRTP_BIT_RS1;
|
|
secrets->cached |= ZRTP_BIT_RS1;
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO! Remote party has lost it's RS1 - use RS2 backup. ID=%u\n", stream->id));
|
|
}
|
|
else
|
|
{
|
|
secrets->matches &= ~ZRTP_BIT_RS1;
|
|
if (session->zrtp->cb.cache_cb.on_set_verified) {
|
|
session->zrtp->cb.cache_cb.on_set_verified( ZSTR_GV(session->zid),
|
|
ZSTR_GV(session->peer_zid),
|
|
0);
|
|
}
|
|
|
|
if (session->zrtp->cb.cache_cb.on_reset_since) {
|
|
session->zrtp->cb.cache_cb.on_reset_since(ZSTR_GV(session->zid), ZSTR_GV(session->peer_zid));
|
|
}
|
|
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO! Our RS1 doesn't equal to other-side's one %s. ID=%u\n",
|
|
cc->rs1.secret->_cachedflag ? " - drop verified!" : "", stream->id));
|
|
}
|
|
|
|
if (!zrtp_memcmp(cc->rs2.peer_id.buffer, stream->messages.peer_dhpart.rs2ID, ZRTP_RSID_SIZE)) {
|
|
secrets->matches |= ZRTP_BIT_RS2;
|
|
if (0 == C[0]) {
|
|
C[0] = &cc->rs2;
|
|
}
|
|
}
|
|
|
|
|
|
if (secrets->auxs &&
|
|
(!zrtp_memcmp(stream->messages.peer_dhpart.auxsID, cc->auxs.peer_id.buffer, ZRTP_RSID_SIZE)) ) {
|
|
C[1] =&cc->auxs;
|
|
secrets->matches |= ZRTP_BIT_AUX;
|
|
}
|
|
|
|
if ( secrets->pbxs &&
|
|
(!zrtp_memcmp(stream->messages.peer_dhpart.pbxsID, cc->pbxs.peer_id.buffer, ZRTP_RSID_SIZE)) ) {
|
|
C[2] = &cc->pbxs;
|
|
secrets->matches |= ZRTP_BIT_PBX;
|
|
}
|
|
|
|
/* Finally hashing matched shared secrets */
|
|
for (i=0; i<3; i++) {
|
|
/*
|
|
* Some of the shared secrets s1 through s5 may have lengths of zero
|
|
* if they are null (not shared), and are each preceded by a 4-octet
|
|
* length field. For example, if s4 is null, len(s4) is 00 00 00 00,
|
|
* and s4 itself would be absent from the hash calculation, which
|
|
* means len(s5) would immediately follow len(s4).
|
|
*/
|
|
comp_length = C[i] ? zrtp_hton32(ZRTP_RS_SIZE) : 0;
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)&comp_length, 4);
|
|
if (C[i]) {
|
|
session->hash->hash_update( session->hash,
|
|
hash_ctx,
|
|
(const int8_t*)C[i]->secret->value.buffer,
|
|
C[i]->secret->value.length );
|
|
ZRTP_LOG(3,(_ZTU_,"\tUse S%d in calculations.\n", i+1));
|
|
}
|
|
}
|
|
|
|
session->hash->hash_end(session->hash, hash_ctx, ZSTR_GV(cc->s0));
|
|
} break; /* S0 for for DH and Preshared streams */
|
|
|
|
/*
|
|
* Compute all possible combinations of preshared_key:
|
|
* hash(len(rs1) | rs1 | len(auxsecret) | auxsecret | len(pbxsecret) | pbxsecret)
|
|
* Find matched preshared_key and derive S0 from it:
|
|
* s0 = KDF(preshared_key, "ZRTP Stream Key", KDF_Context, negotiated hash length)
|
|
*
|
|
* INFO: Take into account that RS1 and RS2 may be swapped.
|
|
* If no matched were found - generate DH commit.
|
|
* ========================================================================
|
|
*/
|
|
case ZRTP_STREAM_MODE_PRESHARED:
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
zrtp_string32_t presh_key = ZSTR_INIT_EMPTY(presh_key);
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tDERIVE S0 for PRESHARED from cached secret. ID=%u\n", stream->id));
|
|
|
|
/* Use the same hash as we used for Commitment */
|
|
if (is_initiator)
|
|
{
|
|
s = _zrtp_compute_preshared_key( session,
|
|
ZSTR_GV(session->secrets.rs1->value),
|
|
(session->secrets.auxs->_cachedflag) ? ZSTR_GV(session->secrets.auxs->value) : NULL,
|
|
(session->secrets.pbxs->_cachedflag) ? ZSTR_GV(session->secrets.pbxs->value) : NULL,
|
|
ZSTR_GV(presh_key),
|
|
NULL);
|
|
if (zrtp_status_ok != s) {
|
|
return s;
|
|
}
|
|
|
|
secrets->matches |= ZRTP_BIT_RS1;
|
|
if (session->secrets.auxs->_cachedflag) {
|
|
secrets->matches |= ZRTP_BIT_AUX;
|
|
}
|
|
if (session->secrets.pbxs->_cachedflag) {
|
|
secrets->matches |= ZRTP_BIT_PBX;
|
|
}
|
|
}
|
|
/*
|
|
* Let's find appropriate hv key for Responder:
|
|
* <RS1, 0, 0>, <RS1, AUX, 0>, <RS1, 0, PBX>, <RS1, AUX, PBX>.
|
|
*/
|
|
else
|
|
{
|
|
int res=-1;
|
|
char* peer_key_id = (char*)stream->messages.peer_commit.hv+ZRTP_HV_NONCE_SIZE;
|
|
zrtp_string8_t key_id = ZSTR_INIT_EMPTY(key_id);
|
|
|
|
do {
|
|
/* RS1 MUST be available at this stage.*/
|
|
s = _zrtp_compute_preshared_key( session,
|
|
ZSTR_GV(secrets->rs1->value),
|
|
NULL,
|
|
NULL,
|
|
ZSTR_GV(presh_key),
|
|
ZSTR_GV(key_id));
|
|
if (zrtp_status_ok == s) {
|
|
res = zrtp_memcmp(peer_key_id, key_id.buffer, ZRTP_HV_KEY_SIZE);
|
|
if (0 == res) {
|
|
secrets->matches |= ZRTP_BIT_RS1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (session->secrets.pbxs->_cachedflag)
|
|
{
|
|
s = _zrtp_compute_preshared_key( session,
|
|
ZSTR_GV(secrets->rs1->value),
|
|
NULL,
|
|
ZSTR_GV(secrets->pbxs->value),
|
|
ZSTR_GV(presh_key),
|
|
ZSTR_GV(key_id));
|
|
if (zrtp_status_ok == s) {
|
|
res = zrtp_memcmp(peer_key_id, key_id.buffer, ZRTP_HV_KEY_SIZE);
|
|
if (0 == res) {
|
|
secrets->matches |= ZRTP_BIT_PBX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (session->secrets.auxs->_cachedflag)
|
|
{
|
|
s = _zrtp_compute_preshared_key( session,
|
|
ZSTR_GV(secrets->rs1->value),
|
|
ZSTR_GV(secrets->auxs->value),
|
|
NULL,
|
|
ZSTR_GV(presh_key),
|
|
ZSTR_GV(key_id));
|
|
if (zrtp_status_ok == s) {
|
|
res = zrtp_memcmp(peer_key_id, key_id.buffer, ZRTP_HV_KEY_SIZE);
|
|
if (0 == res) {
|
|
secrets->matches |= ZRTP_BIT_AUX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((session->secrets.pbxs->_cachedflag) && (session->secrets.auxs->_cachedflag))
|
|
{
|
|
s = _zrtp_compute_preshared_key( session,
|
|
ZSTR_GV(secrets->rs1->value),
|
|
ZSTR_GV(secrets->auxs->value),
|
|
ZSTR_GV(secrets->pbxs->value),
|
|
ZSTR_GV(presh_key),
|
|
ZSTR_GV(key_id));
|
|
if (zrtp_status_ok == s) {
|
|
res = zrtp_memcmp(peer_key_id, key_id.buffer, ZRTP_HV_KEY_SIZE);
|
|
if (0 == res) {
|
|
secrets->matches |= ZRTP_BIT_AUX;
|
|
secrets->matches |= ZRTP_BIT_PBX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} while (0);
|
|
|
|
if (0 != res) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tINFO! Matched Key wasn't found - initate DH exchange.\n"));
|
|
secrets->cached = 0;
|
|
secrets->rs1->_cachedflag = 0;
|
|
|
|
_zrtp_machine_start_initiating_secure(stream);
|
|
return zrtp_status_ok;
|
|
}
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tUse RS1, %s, %s in calculations.\n",
|
|
(session->secrets.matches & ZRTP_BIT_AUX) ? "AUX" : "NULL",
|
|
(session->secrets.matches & ZRTP_BIT_PBX) ? "PBX" : "NULL"));
|
|
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(presh_key),
|
|
ZSTR_GV(zrtp_presh_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
session->hash->digest_length,
|
|
ZSTR_GV(cc->s0));
|
|
} break;
|
|
|
|
|
|
/*
|
|
* For FAST Multistream:
|
|
* s0n = KDF(ZRTPSess, "ZRTP Multistream Key", KDF_Context, negotiated hash length)
|
|
* ========================================================================
|
|
*/
|
|
case ZRTP_STREAM_MODE_MULT:
|
|
{
|
|
ZRTP_LOG(3,(_ZTU_,"\tDERIVE S0 for MULTISTREAM from ZRTP Session key... ID=%u\n", stream->id));
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(session->zrtpsess),
|
|
ZSTR_GV(zrtp_multi_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
session->hash->digest_length,
|
|
ZSTR_GV(cc->s0));
|
|
} break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute ZRTP session key for FULL streams only:
|
|
* ZRTPSess = KDF(s0, "ZRTP Session Key", KDF_Context, negotiated hash length)
|
|
*/
|
|
if (!ZRTP_IS_STREAM_MULT(stream)) {
|
|
if (session->zrtpsess.length == 0) {
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GV(zrtp_sess_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
session->hash->digest_length,
|
|
ZSTR_GV(session->zrtpsess));
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_set_public_value( zrtp_stream_t *stream,
|
|
int is_initiator)
|
|
{
|
|
/*
|
|
* This function performs the following actions according to ZRTP draft 5.6
|
|
* a) Computes total hash;
|
|
* b) Calculates DHResult;
|
|
* c) Computes final stream key S0, based on DHSS and retained secrets;
|
|
* d) Computes HMAC Key and ZRTP key;
|
|
* e) Computes srtp keys and salts and creates srtp session.
|
|
*/
|
|
|
|
zrtp_session_t *session = stream->session;
|
|
zrtp_proto_crypto_t* cc = stream->protocol->cc;
|
|
void* hash_ctx = NULL;
|
|
|
|
static const zrtp_string32_t hmac_keyi_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_INITIATOR_HMAKKEY_STR);
|
|
static const zrtp_string32_t hmac_keyr_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_RESPONDER_HMAKKEY_STR);
|
|
|
|
static const zrtp_string32_t srtp_mki_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_INITIATOR_KEY_STR);
|
|
static const zrtp_string32_t srtp_msi_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_INITIATOR_SALT_STR);
|
|
static const zrtp_string32_t srtp_mkr_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_RESPONDER_KEY_STR);
|
|
static const zrtp_string32_t srtp_msr_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_RESPONDER_SALT_STR);
|
|
|
|
static const zrtp_string32_t zrtp_keyi_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_INITIATOR_ZRTPKEY_STR);
|
|
static const zrtp_string32_t zrtp_keyr_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_RESPONDER_ZRTPKEY_STR);
|
|
|
|
uint32_t cipher_key_length = (ZRTP_CIPHER_AES128 == session->blockcipher->base.id) ? 16 : 32;
|
|
|
|
const zrtp_string32_t *output_mk_label;
|
|
const zrtp_string32_t *output_ms_label;
|
|
const zrtp_string32_t *input_mk_label;
|
|
const zrtp_string32_t *input_ms_label;
|
|
const zrtp_string32_t *hmac_key_label;
|
|
const zrtp_string32_t *peer_hmac_key_label;
|
|
const zrtp_string32_t *zrtp_key_label;
|
|
const zrtp_string32_t *peer_zrtp_key_label;
|
|
|
|
/* Define roles and prepare structures */
|
|
if (is_initiator) {
|
|
output_mk_label = &srtp_mki_label;
|
|
output_ms_label = &srtp_msi_label;
|
|
input_mk_label = &srtp_mkr_label;
|
|
input_ms_label = &srtp_msr_label;
|
|
hmac_key_label = &hmac_keyi_label;
|
|
peer_hmac_key_label = &hmac_keyr_label;
|
|
zrtp_key_label = &zrtp_keyi_label;
|
|
peer_zrtp_key_label = &zrtp_keyr_label;
|
|
} else {
|
|
output_mk_label = &srtp_mkr_label;
|
|
output_ms_label = &srtp_msr_label;
|
|
input_mk_label = &srtp_mki_label;
|
|
input_ms_label = &srtp_msi_label;
|
|
hmac_key_label = &hmac_keyr_label;
|
|
peer_hmac_key_label = &hmac_keyi_label;
|
|
zrtp_key_label = &zrtp_keyr_label;
|
|
peer_zrtp_key_label = &zrtp_keyi_label;
|
|
}
|
|
|
|
ZRTP_LOG(3, (_ZTU_,"---------------------------------------------------\n"));
|
|
ZRTP_LOG(3,(_ZTU_,"\tSWITCHING TO SRTP. ID=%u\n", zrtp_log_mode2str(stream->mode), stream->id));
|
|
ZRTP_LOG(3,(_ZTU_,"\tI %s\n", is_initiator ? "Initiator" : "Responder"));
|
|
|
|
/*
|
|
* Compute total messages hash:
|
|
* total_hash = hash(Hello of responder | Commit | DHPart1 | DHPart2) for DH streams
|
|
* total_hash = hash(Hello of responder | Commit ) for Fast modes.
|
|
*/
|
|
{
|
|
uint8_t* tok = NULL;
|
|
uint16_t tok_len = 0;
|
|
|
|
hash_ctx = session->hash->hash_begin(session->hash);
|
|
if (0 == hash_ctx) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
tok = is_initiator ? (uint8_t*)&stream->messages.peer_hello : (uint8_t*) &stream->messages.hello;
|
|
tok_len = is_initiator ? stream->messages.peer_hello.hdr.length : stream->messages.hello.hdr.length;
|
|
tok_len = zrtp_ntoh16(tok_len)*4;
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)tok, tok_len);
|
|
|
|
tok = is_initiator ? (uint8_t*)&stream->messages.commit : (uint8_t*)&stream->messages.peer_commit;
|
|
tok_len = is_initiator ? stream->messages.commit.hdr.length : stream->messages.peer_commit.hdr.length;
|
|
tok_len = zrtp_ntoh16(tok_len)*4;
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)tok, tok_len);
|
|
|
|
if (ZRTP_IS_STREAM_DH(stream))
|
|
{
|
|
tok = (uint8_t*) (is_initiator ? &stream->messages.peer_dhpart : &stream->messages.dhpart);
|
|
tok_len = is_initiator ? stream->messages.peer_dhpart.hdr.length : stream->messages.dhpart.hdr.length;
|
|
tok_len = zrtp_ntoh16(tok_len)*4;
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)tok, tok_len);
|
|
|
|
tok = (uint8_t*)(is_initiator ? &stream->messages.dhpart : &stream->messages.peer_dhpart);
|
|
tok_len = is_initiator ? stream->messages.dhpart.hdr.length : stream->messages.peer_dhpart.hdr.length;
|
|
tok_len = zrtp_ntoh16(tok_len)*4;
|
|
session->hash->hash_update(session->hash, hash_ctx, (const int8_t*)tok, tok_len);
|
|
}
|
|
|
|
session->hash->hash_end(session->hash, hash_ctx, ZSTR_GV(cc->mes_hash));
|
|
hash_ctx = NULL;
|
|
} /* total hash computing */
|
|
|
|
/* Total Hash is ready and we can create KDF_Context */
|
|
zrtp_zstrcat(ZSTR_GV(cc->kdf_context), is_initiator ? ZSTR_GV(session->zid) : ZSTR_GV(session->peer_zid));
|
|
zrtp_zstrcat(ZSTR_GV(cc->kdf_context), is_initiator ? ZSTR_GV(session->peer_zid) : ZSTR_GV(session->zid));
|
|
zrtp_zstrcat(ZSTR_GV(cc->kdf_context), ZSTR_GV(cc->mes_hash));
|
|
|
|
/* Derive stream key S0 according to key exchange scheme */
|
|
if (zrtp_status_ok != _derive_s0(stream, is_initiator)) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/*
|
|
* Compute HMAC keys. These values will be used after confirmation:
|
|
* hmackeyi = KDF(s0, "Initiator HMAC key", KDF_Context, negotiated hash length)
|
|
* hmackeyr = KDF(s0, "Responder HMAC key", KDF_Context, negotiated hash length)
|
|
*/
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(hmac_key_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
session->hash->digest_length,
|
|
ZSTR_GV(stream->cc.hmackey));
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(peer_hmac_key_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
session->hash->digest_length,
|
|
ZSTR_GV(stream->cc.peer_hmackey));
|
|
|
|
/*
|
|
* Computing ZRTP keys for protection of the Confirm packet:
|
|
* zrtpkeyi = KDF(s0, "Initiator ZRTP key", KDF_Context, negotiated AES key length)
|
|
* zrtpkeyr = KDF(s0, "Responder ZRTP key", KDF_Context, negotiated AES key length)
|
|
*/
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(zrtp_key_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
cipher_key_length,
|
|
ZSTR_GV(stream->cc.zrtp_key));
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(peer_zrtp_key_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
cipher_key_length,
|
|
ZSTR_GV(stream->cc.peer_zrtp_key));
|
|
#if (defined(ZRTP_DEBUG_ZRTP_KEYS) && ZRTP_DEBUG_ZRTP_KEYS == 1)
|
|
{
|
|
char print_buff[256];
|
|
ZRTP_LOG(3,(_ZTU_,"\t Messages hash:%s\n", hex2str(cc->mes_hash.buffer, cc->mes_hash.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t S0:%s\n", hex2str(cc->s0.buffer, cc->s0.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t ZRTP Sess:%s\n", hex2str(session->zrtpsess.buffer, session->zrtpsess.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t hmackey:%s\n", hex2str(stream->cc.hmackey.buffer, stream->cc.hmackey.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t peer_hmackeyr:%s\n", hex2str(stream->cc.peer_hmackey.buffer, stream->cc.peer_hmackey.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t ZRTP key:%s\n", hex2str(stream->cc.zrtp_key.buffer, stream->cc.zrtp_key.length, print_buff, sizeof(print_buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t Peer ZRTP key:%s\n", hex2str(stream->cc.peer_zrtp_key.buffer, stream->cc.peer_zrtp_key.length, print_buff, sizeof(print_buff))));
|
|
}
|
|
#endif
|
|
/*
|
|
* Preparing SRTP crypto engine:
|
|
* srtpkeyi = KDF(s0, "Initiator SRTP master key", KDF_Context, negotiated AES key length)
|
|
* srtpsalti = KDF(s0, "Initiator SRTP master salt", KDF_Context, 112)
|
|
* srtpkeyr = KDF(s0, "Responder SRTP master key", KDF_Context, negotiated AES key length)
|
|
* srtpsaltr = KDF(s0, "Responder SRTP master salt", KDF_Context, 112)
|
|
*/
|
|
{
|
|
zrtp_srtp_profile_t iprof;
|
|
zrtp_srtp_profile_t oprof;
|
|
|
|
ZSTR_SET_EMPTY(iprof.salt);
|
|
ZSTR_SET_EMPTY(iprof.key);
|
|
|
|
iprof.rtp_policy.cipher = session->blockcipher;
|
|
iprof.rtp_policy.auth_tag_len = session->authtaglength;
|
|
iprof.rtp_policy.hash = zrtp_comp_find(ZRTP_CC_HASH, ZRTP_SRTP_HASH_HMAC_SHA1, session->zrtp);
|
|
iprof.rtp_policy.auth_key_len = 20;
|
|
iprof.rtp_policy.cipher_key_len = cipher_key_length;
|
|
|
|
zrtp_memcpy(&iprof.rtcp_policy, &iprof.rtp_policy, sizeof(iprof.rtcp_policy));
|
|
iprof.dk_cipher = session->blockcipher;
|
|
|
|
zrtp_memcpy(&oprof, &iprof, sizeof(iprof));
|
|
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(input_mk_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
cipher_key_length,
|
|
ZSTR_GV(iprof.key));
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(input_ms_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
14,
|
|
ZSTR_GV(iprof.salt));
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(output_mk_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
cipher_key_length,
|
|
ZSTR_GV(oprof.key));
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GVP(output_ms_label),
|
|
ZSTR_GV(stream->protocol->cc->kdf_context),
|
|
14,
|
|
ZSTR_GV(oprof.salt));
|
|
|
|
stream->protocol->_srtp = zrtp_srtp_create(session->zrtp->srtp_global, &iprof, &oprof);
|
|
|
|
/* Profiles and keys in them are not needed anymore - clear them */
|
|
zrtp_memset(&iprof, 0, sizeof(iprof));
|
|
zrtp_memset(&oprof, 0, sizeof(oprof));
|
|
|
|
if (!stream->protocol->_srtp) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! Can't initialize SRTP engine. ID=%u\n", stream->id));
|
|
return zrtp_status_fail;
|
|
}
|
|
} /* SRTP initialization */
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_enter_secure(zrtp_stream_t* stream)
|
|
{
|
|
/*
|
|
* When switching to SECURE all ZRTP crypto values were already computed by
|
|
* state-machine. Then we need to have logic to manage SAS value and shared
|
|
* secrets only. So: we compute SAS, refresh secrets flags and save the
|
|
* secrets to the cache after RS2 and RS1 swapping. We don't need any
|
|
* crypto sources any longer - destroy them.
|
|
*/
|
|
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
zrtp_proto_crypto_t* cc = stream->protocol->cc;
|
|
zrtp_session_t *session = stream->session;
|
|
zrtp_secrets_t *secrets = &stream->session->secrets;
|
|
uint8_t was_exp = 0;
|
|
uint64_t exp_date = 0;
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tEnter state SECURE (%s).\n", zrtp_log_mode2str(stream->mode)));
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
|
|
/*
|
|
* Compute the SAS value if it isn't computed yet. If there are several
|
|
* streams running in parallel - stream with the biggest hvi should
|
|
* generate the SAS.
|
|
*/
|
|
if (!session->sas1.length) {
|
|
s = session->sasscheme->compute(session->sasscheme, stream, session->hash, 0);
|
|
if (zrtp_status_ok != s) {
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_software, 1);
|
|
return s;
|
|
}
|
|
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tThis is the very first stream in sID GENERATING SAS value.\n", session->id));
|
|
ZRTP_LOG(3,(_ZTU_,"\tSAS computed: <%.16s> <%.16s>.\n", session->sas1.buffer, session->sas2.buffer));
|
|
}
|
|
|
|
/*
|
|
* Compute a new value for RS1 and store the prevoious one.
|
|
* Compute result secrets' flags.
|
|
*/
|
|
if (ZRTP_IS_STREAM_DH(stream))
|
|
{
|
|
ZRTP_LOG(3,(_ZTU_,"\tCheck expiration interval: last_use=%u ttl=%u new_ttl=%u exp=%u now=%u\n",
|
|
secrets->rs1->lastused_at,
|
|
secrets->rs1->ttl,
|
|
stream->cache_ttl,
|
|
(secrets->rs1->lastused_at + secrets->rs1->ttl),
|
|
zrtp_time_now()/1000));
|
|
|
|
if (secrets->rs1->ttl != 0xFFFFFFFF) {
|
|
exp_date = secrets->rs1->lastused_at;
|
|
exp_date += secrets->rs1->ttl;
|
|
|
|
if (ZRTP_IS_STREAM_DH(stream) && (exp_date < zrtp_time_now()/1000)) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tUsing EXPIRED secrets: last_use=%u ttl=%u exp=%u now=%u\n",
|
|
secrets->rs1->lastused_at,
|
|
secrets->rs1->ttl,
|
|
(secrets->rs1->lastused_at + secrets->rs1->ttl),
|
|
zrtp_time_now()/1000));
|
|
was_exp = 1;
|
|
}
|
|
}
|
|
|
|
if (!was_exp) {
|
|
secrets->wrongs = secrets->matches ^ secrets->cached;
|
|
secrets->wrongs &= ~ZRTP_BIT_RS2;
|
|
secrets->wrongs &= ~ZRTP_BIT_PBX;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We going to update RS1 and change appropriate secrets flags. Let's back-up current values.
|
|
* Back-upped values could be used in debug purposes and in the GUI to reflect current state of the call
|
|
*/
|
|
if (!ZRTP_IS_STREAM_MULT(stream)) {
|
|
secrets->cached_curr = secrets->cached;
|
|
secrets->matches_curr = secrets->matches;
|
|
secrets->wrongs_curr = secrets->wrongs;
|
|
}
|
|
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tFlags C=%x M=%x W=%x ID=%u\n",
|
|
secrets->cached, secrets->matches, secrets->wrongs, stream->id));
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_SECURE);
|
|
/*
|
|
* Alarm user if the following condition is TRUE for both RS1 and RS2:
|
|
* "secret is wrong if it has been restored from the cache but hasn't matched
|
|
* with the remote one".
|
|
*/
|
|
if (session->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
session->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_SECURE);
|
|
}
|
|
if (session->zrtp->cb.event_cb.on_zrtp_secure) {
|
|
session->zrtp->cb.event_cb.on_zrtp_secure(stream);
|
|
}
|
|
|
|
/* Alarm user if possible MiTM attack detected */
|
|
if (secrets->wrongs) {
|
|
session->mitm_alert_detected = 1;
|
|
|
|
if (session->zrtp->cb.event_cb.on_zrtp_security_event) {
|
|
session->zrtp->cb.event_cb.on_zrtp_security_event(stream, ZRTP_EVENT_MITM_WARNING);
|
|
}
|
|
}
|
|
|
|
/* Check for unenrollemnt first */
|
|
if ((secrets->cached & ZRTP_BIT_PBX) && !(secrets->matches & ZRTP_BIT_PBX)) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO! The user requires new un-enrolment - the nedpint may clear"
|
|
" the cache or perform other action. ID=%u\n", stream->id));
|
|
|
|
if (session->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
session->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_USER_UNENROLLED);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle PBX registration, if required: If PBX already had a shared secret
|
|
* for the ZID it leaves the cache entry unmodified. Else, it computes a new
|
|
* one. If the PBX detects cache entry for the static shared secret, but the
|
|
* phone does not have a matching cache entry - the PBX generates a new one.
|
|
*/
|
|
if (ZRTP_MITM_MODE_REG_SERVER == stream->mitm_mode)
|
|
{
|
|
if (secrets->matches & ZRTP_BIT_PBX) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO! User have been already registered - skip enrollment ritual. ID=%u\n", stream->id));
|
|
if (session->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
session->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_USER_ALREADY_ENROLLED);
|
|
}
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO! The user requires new enrolment - generate new MiTM secret. ID=%u\n", stream->id));
|
|
zrtp_register_with_trusted_mitm(stream);
|
|
if (session->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_NEW_USER_ENROLLED);
|
|
}
|
|
|
|
}
|
|
}
|
|
else if (ZRTP_MITM_MODE_REG_CLIENT == stream->mitm_mode)
|
|
{
|
|
if (session->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
session->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_CLIENT_ENROLLMENT);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Compute new RS for FULL DH streams only. Don't update RS1 if cache TTL is 0
|
|
*/
|
|
if (ZRTP_IS_STREAM_DH(stream))
|
|
{
|
|
static const zrtp_string32_t rss_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_RS_STR);
|
|
|
|
if (stream->cache_ttl > 0) {
|
|
/* Replace RS2 with RS1 */
|
|
zrtp_sys_free(secrets->rs2);
|
|
secrets->rs2 = secrets->rs1;
|
|
|
|
secrets->rs1 = _zrtp_alloc_shared_secret(session);
|
|
if (!secrets->rs1) {
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_software, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/*
|
|
* Compute new RS1 based on previous one and S0:
|
|
* rs1 = KDF(s0, "retained secret", KDF_Context, negotiated hash length)
|
|
*/
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(cc->s0),
|
|
ZSTR_GV(rss_label),
|
|
ZSTR_GV(cc->kdf_context),
|
|
ZRTP_HASH_SIZE,
|
|
ZSTR_GV(secrets->rs1->value));
|
|
|
|
/*
|
|
* Mark secrets as cached: RS1 have been just generated and cached;
|
|
* RS2 is cached if previous secret was cached as well.
|
|
*/
|
|
secrets->rs1->_cachedflag = 1;
|
|
secrets->cached |= ZRTP_BIT_RS1;
|
|
secrets->matches |= ZRTP_BIT_RS1;
|
|
if (secrets->rs2->_cachedflag) {
|
|
secrets->cached |= ZRTP_BIT_RS2;
|
|
}
|
|
|
|
/* Let's update the TTL interval for the new secret */
|
|
secrets->rs1->ttl = stream->cache_ttl;
|
|
secrets->rs1->lastused_at = (uint32_t)(zrtp_time_now()/1000);
|
|
|
|
/* If possible MiTM attach detected - postpone storing the cache until after the user verify the SAS */
|
|
if (!session->mitm_alert_detected) {
|
|
if (session->zrtp->cb.cache_cb.on_put) {
|
|
session->zrtp->cb.cache_cb.on_put( ZSTR_GV(session->zid),
|
|
ZSTR_GV(session->peer_zid),
|
|
secrets->rs1);
|
|
}
|
|
}
|
|
|
|
{
|
|
uint32_t verifiedflag = 0;
|
|
char buff[128];
|
|
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);
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tNew secret was generated:\n"));
|
|
ZRTP_LOG(3,(_ZTU_,"\t\tRS1 value:<%s>\n",
|
|
hex2str(secrets->rs1->value.buffer, secrets->rs1->value.length, buff, sizeof(buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t\tTTL=%u, flags C=%x M=%x W=%x V=%d\n",
|
|
secrets->rs1->ttl, secrets->cached, secrets->matches, secrets->wrongs, verifiedflag));
|
|
}
|
|
} /* for TTL > 0 only */
|
|
else {
|
|
if (session->zrtp->cb.cache_cb.on_put) {
|
|
secrets->rs1->ttl = 0;
|
|
session->zrtp->cb.cache_cb.on_put( ZSTR_GV(session->zid),
|
|
ZSTR_GV(session->peer_zid),
|
|
secrets->rs1);
|
|
}
|
|
}
|
|
} /* For DH mode only */
|
|
|
|
|
|
if (session->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
session->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_SECURE_DONE);
|
|
}
|
|
|
|
/* We have computed all subkeys from S0 and don't need it any longer. */
|
|
zrtp_wipe_zstring(ZSTR_GV(cc->s0));
|
|
|
|
/* Clear DH crypto context */
|
|
if (ZRTP_IS_STREAM_DH(stream)) {
|
|
bnEnd(&stream->dh_cc.peer_pv);
|
|
bnEnd(&stream->dh_cc.pv);
|
|
bnEnd(&stream->dh_cc.sv);
|
|
zrtp_wipe_zstring(ZSTR_GV(stream->dh_cc.dhss));
|
|
}
|
|
|
|
/*
|
|
* Now, let's check if the transition to CLEAR was caused by Active/Passive rules.
|
|
* If local endpoint is a MitM and peer MiTM linked stream is Unlimited, we
|
|
* could break the rules and send commit to Passive endpoint.
|
|
*/
|
|
if (stream->zrtp->is_mitm && stream->peer_super_flag) {
|
|
if (stream->linked_mitm && stream->linked_mitm->peer_passive) {
|
|
if (stream->linked_mitm->state == ZRTP_STATE_CLEAR) {
|
|
ZRTP_LOG(2,(_ZTU_,"INFO: Linked Peer stream id=%u suspended in CLEAR-state due to"
|
|
" Active/Passive restrictions, but we are running in MiTM mode and "
|
|
"current peer endpoint is Super-Active. Let's Go Secure for the linked stream.\n", stream->id));
|
|
|
|
/* @note: don't use zrtp_secure_stream() wrapper as it checks for Active/Passive stuff. */
|
|
_zrtp_machine_start_initiating_secure(stream->linked_mitm);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Increase calls counter for Preshared mode and reset it on DH
|
|
*/
|
|
if (session->zrtp->cb.cache_cb.on_presh_counter_get && session->zrtp->cb.cache_cb.on_presh_counter_set) {
|
|
uint32_t calls_counter = 0;
|
|
session->zrtp->cb.cache_cb.on_presh_counter_get( ZSTR_GV(session->zid),
|
|
ZSTR_GV(session->peer_zid),
|
|
&calls_counter);
|
|
if (ZRTP_IS_STREAM_DH(stream)) {
|
|
session->zrtp->cb.cache_cb.on_presh_counter_set( ZSTR_GV(session->zid),
|
|
ZSTR_GV(session->peer_zid),
|
|
0);
|
|
} else if ZRTP_IS_STREAM_PRESH(stream) {
|
|
session->zrtp->cb.cache_cb.on_presh_counter_set( ZSTR_GV(session->zid),
|
|
ZSTR_GV(session->peer_zid),
|
|
++calls_counter);
|
|
}
|
|
}
|
|
|
|
clear_crypto_sources(stream);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* Shared functions */
|
|
/*===========================================================================*/
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_create_confirm( zrtp_stream_t *stream,
|
|
zrtp_packet_Confirm_t* confirm)
|
|
{
|
|
void* cipher_ctx = NULL;
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
zrtp_session_t *session = stream->session;
|
|
uint32_t verifiedflag = 0;
|
|
|
|
/* hash + (padding + sig_len + flags) + ttl */
|
|
const uint8_t encrypted_body_size = ZRTP_MESSAGE_HASH_SIZE + (2 + 1 + 1) + 4;
|
|
|
|
/*
|
|
* Create the Confirm packet according to draft 6.7
|
|
* AES CFB vector at first, SIG length and flags octet and cache TTL at the end
|
|
* This version doesn't support signatures so sig_length=0
|
|
*/
|
|
if (ZRTP_CFBIV_SIZE != zrtp_randstr(session->zrtp, confirm->iv, ZRTP_CFBIV_SIZE)) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
zrtp_memcpy(confirm->hash, stream->messages.h0.buffer, ZRTP_MESSAGE_HASH_SIZE);
|
|
|
|
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);
|
|
}
|
|
|
|
confirm->expired_interval = zrtp_hton32(session->profile.cache_ttl);
|
|
confirm->flags = 0;
|
|
confirm->flags |= session->profile.disclose_bit ? 0x01 : 0x00;
|
|
confirm->flags |= session->profile.allowclear ? 0x02 : 0x00;
|
|
confirm->flags |= verifiedflag ? 0x04 : 0x00;
|
|
confirm->flags |= (ZRTP_MITM_MODE_REG_SERVER == stream->mitm_mode) ? 0x08 : 0x00;
|
|
|
|
/* Then we need to encrypt Confirm before Hmac computing. Use AES CFB */
|
|
do
|
|
{
|
|
cipher_ctx = session->blockcipher->start( session->blockcipher,
|
|
(uint8_t*)stream->cc.zrtp_key.buffer,
|
|
NULL,
|
|
ZRTP_CIPHER_MODE_CFB);
|
|
if (!cipher_ctx) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->set_iv(session->blockcipher, cipher_ctx, (zrtp_v128_t*)confirm->iv);
|
|
if (zrtp_status_ok != s) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->encrypt( session->blockcipher,
|
|
cipher_ctx,
|
|
(uint8_t*)&confirm->hash,
|
|
encrypted_body_size );
|
|
} while(0);
|
|
if (cipher_ctx) {
|
|
session->blockcipher->stop(session->blockcipher, cipher_ctx);
|
|
}
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"ERROR! failed to encrypt Confirm. s=%d ID=%u\n", s, stream->id));
|
|
return s;
|
|
}
|
|
|
|
/* Compute Hmac over encrypted part of Confirm */
|
|
{
|
|
zrtp_string128_t hmac = ZSTR_INIT_EMPTY(hmac);
|
|
s = session->hash->hmac_c( session->hash,
|
|
stream->cc.hmackey.buffer,
|
|
stream->cc.hmackey.length,
|
|
(const char*)&confirm->hash,
|
|
encrypted_body_size,
|
|
ZSTR_GV(hmac) );
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"ERROR! failed to compute Confirm hmac. s=%d ID=%u\n", s, stream->id));
|
|
return s;
|
|
}
|
|
|
|
zrtp_memcpy(confirm->hmac, hmac.buffer, ZRTP_HMAC_SIZE);
|
|
|
|
{
|
|
char buff[512];
|
|
ZRTP_LOG(3,(_ZTU_,"HMAC TRACE. COMPUTE.\n"));
|
|
ZRTP_LOG(3,(_ZTU_,"\tcipher text:%s. size=%u\n",
|
|
hex2str((const char*)&confirm->hash, encrypted_body_size, buff, sizeof(buff)), encrypted_body_size));
|
|
ZRTP_LOG(3,(_ZTU_,"\t key:%s.\n",
|
|
hex2str(stream->cc.hmackey.buffer, stream->cc.hmackey.length, buff, sizeof(buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t comp hmac:%s.\n",
|
|
hex2str(hmac.buffer, hmac.length, buff, sizeof(buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t hmac:%s.\n",
|
|
hex2str((const char*)confirm->hmac, ZRTP_HMAC_SIZE, buff, sizeof(buff))));
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_confirm( zrtp_stream_t *stream,
|
|
zrtp_packet_Confirm_t *confirm)
|
|
{
|
|
/* Compute Hmac over encrypted part of Confirm and reject malformed packets */
|
|
void* cipher_ctx = NULL;
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
zrtp_session_t *session = stream->session;
|
|
zrtp_string128_t hmac = ZSTR_INIT_EMPTY(hmac);
|
|
|
|
/* hash + (padding + sig_len + flags) + ttl */
|
|
const uint8_t encrypted_body_size = ZRTP_MESSAGE_HASH_SIZE + (2 + 1 + 1) + 4;
|
|
s = session->hash->hmac_c( session->hash,
|
|
stream->cc.peer_hmackey.buffer,
|
|
stream->cc.peer_hmackey.length,
|
|
(const char*)&confirm->hash,
|
|
encrypted_body_size,
|
|
ZSTR_GV(hmac) );
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! failed to compute Incoming Confirm hmac. s=%d ID=%u\n", s, stream->id));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
|
|
// MARK: TRACE CONFIRM HMAC ERROR
|
|
#if 0
|
|
{
|
|
char buff[512];
|
|
ZRTP_LOG(3,(_ZTU_,"HMAC TRACE. VERIFY\n"));
|
|
ZRTP_LOG(3,(_ZTU_,"\tcipher text:%s. size=%u\n",
|
|
hex2str((const char*)&confirm->hash, encrypted_body_size, buff, sizeof(buff)), encrypted_body_size));
|
|
ZRTP_LOG(3,(_ZTU_,"\t key:%s.\n",
|
|
hex2str(stream->cc.peer_hmackey.buffer, stream->cc.peer_hmackey.length, buff, sizeof(buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t comp hmac:%s.\n",
|
|
hex2str(hmac.buffer, hmac.length, buff, sizeof(buff))));
|
|
ZRTP_LOG(3,(_ZTU_,"\t hmac:%s.\n",
|
|
hex2str((const char*)confirm->hmac, ZRTP_HMAC_SIZE, buff, sizeof(buff))));
|
|
}
|
|
#endif
|
|
|
|
|
|
if (0 != zrtp_memcmp(confirm->hmac, hmac.buffer, ZRTP_HMAC_SIZE)) {
|
|
/*
|
|
* Weird. Perhaps a bug in our code or our peer's code. Or it could be an attacker
|
|
* who doesn't realize that Man-In-The-Middling the Diffie-Hellman key generation
|
|
* but allowing the correct rsIds to pass through accomplishes nothing more than
|
|
* forcing us to fallback to cleartext mode. If this attacker had gone ahead and deleted
|
|
* or replaced the rsIds, then he would have been able to stay in the middle (although
|
|
* he would of course still face the threat of a Voice Authentication Check). On the
|
|
* other hand if this attacker wanted to force us to fallback to cleartext mode, he could
|
|
* have done that more simply, for example by intercepting our ZRTP HELLO packet and
|
|
* replacing it with a normal non-ZRTP comfort noise packet. In any case, we'll do our
|
|
* "switch to cleartext fallback" behavior.
|
|
*/
|
|
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING!" ZRTP_VERIFIED_RESP_WARNING_STR "ID=%u\n", stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_auth_decrypt, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Then we need to decrypt Confirm body */
|
|
do {
|
|
cipher_ctx = session->blockcipher->start( session->blockcipher,
|
|
(uint8_t*)stream->cc.peer_zrtp_key.buffer,
|
|
NULL,
|
|
ZRTP_CIPHER_MODE_CFB);
|
|
if (!cipher_ctx) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->set_iv( session->blockcipher,
|
|
cipher_ctx,
|
|
(zrtp_v128_t*)confirm->iv);
|
|
if (zrtp_status_ok != s) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->decrypt( session->blockcipher,
|
|
cipher_ctx,
|
|
(uint8_t*)&confirm->hash,
|
|
encrypted_body_size);
|
|
} while(0);
|
|
if (cipher_ctx) {
|
|
session->blockcipher->stop(session->blockcipher, cipher_ctx);
|
|
}
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tERROR! failed to decrypt incoming Confirm. s=%d ID=%u\n", s, stream->id));
|
|
return s;
|
|
}
|
|
|
|
/* We have access to hash field and can check hmac of the previous message */
|
|
{
|
|
zrtp_msg_hdr_t *hdr = NULL;
|
|
char *key=NULL;
|
|
zrtp_string32_t tmphash_str = ZSTR_INIT_EMPTY(tmphash_str);
|
|
zrtp_hash_t *hash = zrtp_comp_find( ZRTP_CC_HASH, ZRTP_HASH_SHA256, stream->zrtp);
|
|
|
|
if (ZRTP_IS_STREAM_DH(stream)) {
|
|
hdr = &stream->messages.peer_dhpart.hdr;
|
|
key = (char*)confirm->hash;
|
|
} else {
|
|
hash->hash_c(hash, (char*)confirm->hash, ZRTP_MESSAGE_HASH_SIZE, ZSTR_GV(tmphash_str));
|
|
|
|
if (ZRTP_STATEMACHINE_INITIATOR == stream->protocol->type) {
|
|
hdr = &stream->messages.peer_hello.hdr;
|
|
hash->hash_c( hash,
|
|
tmphash_str.buffer,
|
|
ZRTP_MESSAGE_HASH_SIZE,
|
|
ZSTR_GV(tmphash_str) );
|
|
} else {
|
|
hdr = &stream->messages.peer_commit.hdr;
|
|
}
|
|
key = tmphash_str.buffer;
|
|
}
|
|
|
|
if (0 != _zrtp_validate_message_hmac(stream, hdr, key)) {
|
|
return zrtp_status_fail;
|
|
}
|
|
}
|
|
|
|
/* Set evil bit if other-side shared session key */
|
|
stream->peer_disclose_bit = (confirm->flags & 0x01);
|
|
|
|
/* Enable ALLOWCLEAR option if only both sides support it */
|
|
stream->allowclear = (confirm->flags & 0x02) && session->profile.allowclear;
|
|
|
|
/* Drop RS1 VERIFIED flag if other side didn't verified key exchange */
|
|
if (0 == (confirm->flags & 0x04)) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO: Other side Confirm V=0 - set verified to 0! ID=%u\n", stream->id));
|
|
zrtp_verified_set(session->zrtp, &session->zid, &session->peer_zid, 0);
|
|
}
|
|
|
|
/* Look for Enrollment replay flag */
|
|
if (confirm->flags & 0x08)
|
|
{
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO: Confirm PBX Enrolled flag is set - it is a Registration call! ID=%u\n", stream->id));
|
|
|
|
if (stream->mitm_mode != ZRTP_MITM_MODE_CLIENT) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tERROR: PBX enrollment flag was received in wrong MiTM mode %s."
|
|
" ID=%u\n", zrtp_log_mode2str(stream->mode), stream->id));
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Passive endpoint should ignore PBX Enrollment. */
|
|
if (ZRTP_LICENSE_MODE_PASSIVE != stream->zrtp->lic_mode) {
|
|
stream->mitm_mode = ZRTP_MITM_MODE_REG_CLIENT;
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO: Ignore PBX Enrollment flag as we are Passive ID=%u\n", stream->id));
|
|
}
|
|
}
|
|
|
|
stream->cache_ttl = ZRTP_MIN(session->profile.cache_ttl, zrtp_ntoh32(confirm->expired_interval));
|
|
|
|
/* Copy packet for future hashing */
|
|
zrtp_memcpy(&stream->messages.peer_confirm, confirm, zrtp_ntoh16(confirm->hdr.length)*4);
|
|
|
|
return zrtp_status_ok;
|
|
}
|