1481 lines
47 KiB
C
1481 lines
47 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 engine"
|
|
|
|
/*!
|
|
* Data type for state-handlers: every state has a state handler
|
|
* function which is called by zrtp_process_srtp().
|
|
*/
|
|
typedef zrtp_status_t state_handler_t( zrtp_stream_t* stream, zrtp_rtp_info_t* packet );
|
|
extern state_handler_t* state_handler[ZRTP_STATE_COUNT];
|
|
|
|
extern zrtp_status_t _zrtp_machine_process_sasrelay(zrtp_stream_t *stream, zrtp_rtp_info_t *packet);
|
|
|
|
static void _zrtp_machine_switch_to_error(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_enter_initiatingclear(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_enter_clear(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_enter_pendingerror(zrtp_stream_t *stream, zrtp_protocol_error_t code);
|
|
|
|
zrtp_status_t _zrtp_machine_process_hello(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
zrtp_status_t _zrtp_machine_process_goclear(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
static void _send_helloack(zrtp_stream_t* stream);
|
|
static void _send_goclearack(zrtp_stream_t* stream);
|
|
|
|
zrtp_status_t _zrtp_machine_start_send_and_resend_hello(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_goclear(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_errorack(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_error(zrtp_stream_t* stream);
|
|
|
|
void _clear_stream_crypto(zrtp_stream_t* stream);
|
|
|
|
|
|
/*===========================================================================*/
|
|
// MARK: ===> Main ZRTP interfaces
|
|
/*===========================================================================*/
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_process_rtcp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
|
|
/*
|
|
* In transition states, drop outgoing packets. In SECURE state, encrypt
|
|
outgoing packets. In all other states leave them unchanged.
|
|
*/
|
|
|
|
if (stream) {
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_START_INITIATINGSECURE:
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM1:
|
|
case ZRTP_STATE_WAIT_CONFIRMACK:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM2:
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
return zrtp_status_drop;
|
|
|
|
case ZRTP_STATE_SASRELAYING:
|
|
case ZRTP_STATE_SECURE:
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
|
|
if (*length < RTCP_HDR_SIZE) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
zrtp_memset(&info, 0, sizeof(info));
|
|
info.packet = packet;
|
|
info.length = length;
|
|
info.seq = 0; /*sequence number will be generated in zrtp_srtp_protect_rtcp()*/
|
|
info.ssrc = (uint32_t) *(packet+sizeof(uint32_t));
|
|
|
|
return _zrtp_protocol_encrypt(stream->protocol, &info, 0);
|
|
}
|
|
|
|
default:
|
|
return zrtp_status_ok;
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_process_srtcp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
|
|
/*
|
|
* In transition states, drop incoming packets. In SECURE state, decrypt
|
|
* incoming packets. In all other states leave them unchanged.
|
|
*/
|
|
|
|
if (stream) {
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_INITIATINGCLEAR:
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
return zrtp_status_drop;
|
|
|
|
case ZRTP_STATE_SECURE:
|
|
case ZRTP_STATE_SASRELAYING:
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
|
|
if (*length < RTCP_HDR_SIZE) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
zrtp_memset(&info, 0, sizeof(info));
|
|
info.packet = packet;
|
|
info.length = length;
|
|
info.seq = 0; /*sequence number will be determined from packet in zrtp_srtp_unprotect_rtcp()*/
|
|
info.ssrc = (uint32_t) *(packet+sizeof(uint32_t));
|
|
|
|
return _zrtp_protocol_decrypt(stream->protocol, &info, 0);
|
|
}
|
|
|
|
default:
|
|
return zrtp_status_ok;
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_process_rtp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
|
|
if (!stream || !packet || !length) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Skip packet processing within uninitiated stream */
|
|
if ((stream->state < ZRTP_STATE_START) || (stream->state > ZRTP_STATE_NO_ZRTP)) {
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/* Prepare RTP packet: detect type and other options */
|
|
if (zrtp_status_ok != _zrtp_packet_preparse(stream, packet, length, &info, 0)) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Drop packets in transition states and encrypt in SECURE state */
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_START_INITIATINGSECURE:
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM1:
|
|
case ZRTP_STATE_WAIT_CONFIRMACK:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM2:
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
if (ZRTP_NONE == info.type) {
|
|
/* Add dropped media to the entropy hash */
|
|
ZRTP_LOG(1,(_ZTU_,"Add %d bytes of entropy to the RNG pool.\n", *length));
|
|
zrtp_entropy_add(stream->zrtp, (unsigned char*)packet, *length);
|
|
|
|
return zrtp_status_drop;
|
|
}
|
|
break;
|
|
|
|
case ZRTP_STATE_SASRELAYING:
|
|
case ZRTP_STATE_SECURE:
|
|
if (ZRTP_NONE == info.type) {
|
|
return _zrtp_protocol_encrypt(stream->protocol, &info, 1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
extern int _send_message(zrtp_stream_t* stream, zrtp_msg_type_t type, const void* message, uint32_t ssrc);
|
|
zrtp_status_t zrtp_process_srtp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
if (!stream || !packet || !length) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
if (*length <= RTP_HDR_SIZE) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Preparse RTP packet: detect type and other options */
|
|
s = _zrtp_packet_preparse(stream, packet, length, &info, 1);
|
|
if (zrtp_status_ok != s) {
|
|
return s;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* For Zfone3 Compatibility */
|
|
if (ZRTP_ZFONEPING == info.type) {
|
|
zrtp_packet_zfoneping_t* ping = (zrtp_packet_zfoneping_t*) info.message;
|
|
zrtp_packet_zfonepingack_t pingack;
|
|
|
|
zrtp_memcpy(pingack.version, ZRTP_ZFONE_PROTOCOL_VERSION, 4);
|
|
zrtp_memcpy(pingack.endpointhash, stream->session->zid.buffer, sizeof(pingack.endpointhash));
|
|
zrtp_memcpy(pingack.peerendpointhash, ping->endpointhash, sizeof(pingack.endpointhash));
|
|
pingack.peerssrc = info.ssrc;
|
|
|
|
_zrtp_packet_fill_msg_hdr( stream,
|
|
ZRTP_ZFONEPINGACK,
|
|
sizeof(zrtp_packet_zfonepingack_t) - sizeof(zrtp_msg_hdr_t),
|
|
&pingack.hdr);
|
|
|
|
_zrtp_packet_send_message(stream, ZRTP_ZFONEPINGACK, &pingack);
|
|
return zrtp_status_drop;
|
|
}
|
|
/*************************************************************************/
|
|
|
|
/* Skip packet processing within non-started stream */
|
|
if ((stream->state < ZRTP_STATE_START) || (stream->state > ZRTP_STATE_NO_ZRTP)) {
|
|
return (ZRTP_NONE == info.type) ? zrtp_status_ok : zrtp_status_drop;
|
|
}
|
|
|
|
/*
|
|
* This mutex should protect stream data against asynchr. calls e.g.:
|
|
* zrtp_stream_secure(), zrtp_stream_clear() etc. Media packet handlers
|
|
* don't change any internal data, so this applies only to ZRTP messages.
|
|
*/
|
|
if (info.type != ZRTP_NONE) {
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
}
|
|
|
|
/* Extra protection. We need protocol to handle ZRTP messages in following states. */
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM1:
|
|
case ZRTP_STATE_WAIT_CONFIRMACK:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM2:
|
|
case ZRTP_STATE_SECURE:
|
|
case ZRTP_STATE_SASRELAYING:
|
|
if (!stream->protocol) {
|
|
if (info.type != ZRTP_NONE) {
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
}
|
|
return zrtp_status_fail;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Handle Error packet from any state */
|
|
if (ZRTP_ERROR == info.type && stream->state > ZRTP_STATE_START)
|
|
{
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_NONE:
|
|
case ZRTP_STATE_ACTIVE:
|
|
case ZRTP_STATE_SECURE:
|
|
case ZRTP_STATE_PENDINGERROR:
|
|
case ZRTP_STATE_INITIATINGERROR:
|
|
case ZRTP_STATE_NO_ZRTP:
|
|
break;
|
|
default:
|
|
{
|
|
zrtp_packet_Error_t* error = (zrtp_packet_Error_t*) info.message;
|
|
_zrtp_machine_enter_pendingerror(stream, zrtp_ntoh32(error->code));
|
|
} break;
|
|
}
|
|
}
|
|
|
|
/* Process packet by state-machine according to packet type and current protocol state */
|
|
if (state_handler[stream->state]) {
|
|
s = state_handler[stream->state](stream, &info);
|
|
}
|
|
|
|
/* Unlock stream mutex for a ZRTP message packet. See comments above. */
|
|
if (info.type != ZRTP_NONE) {
|
|
s = zrtp_status_drop;
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_stream_start(zrtp_stream_t* stream, uint32_t ssrc)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
/*
|
|
* (ZRTP stream starts from START state and HELLO packets resending.
|
|
* Stream can be started from START, ERROR or NOZRTP states only.)
|
|
*/
|
|
ZRTP_LOG(3,(_ZTU_,"START STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
if ( (ZRTP_STATE_ACTIVE != stream->state) &&
|
|
(ZRTP_STATE_ERROR != stream->state) &&
|
|
(ZRTP_STATE_NO_ZRTP != stream->state)) {
|
|
ZRTP_LOG(1,(_ZTU_,"ERROR! Can't start Stream ID=%u from %s state.\n",
|
|
stream->id, zrtp_log_state2str(stream->state)));
|
|
s = zrtp_status_wrong_state;
|
|
} else {
|
|
stream->media_ctx.ssrc = zrtp_hton32(ssrc);
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_START);
|
|
_zrtp_machine_start_send_and_resend_hello(stream);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_stream_stop(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
/*
|
|
* Stop all packet replays, deinitialize crypto data and prepare the stream
|
|
* for the next use. The stream can be terminated from any protocol state.
|
|
*/
|
|
ZRTP_LOG(3,(_ZTU_,"STOP STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
/*
|
|
* Unlink deleted stream for the peer MiTM stream if necessary. It may
|
|
* prevent some recae-conditions as we always test for NULL before
|
|
* accessing linked_mitm.
|
|
*/
|
|
if (stream->linked_mitm) {
|
|
stream->linked_mitm->linked_mitm = NULL;
|
|
}
|
|
|
|
if (stream->state != ZRTP_STATE_NONE) {
|
|
/*
|
|
* This function can be called in parallel to the main processing loop
|
|
* - protect internal stream data.
|
|
*/
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
if (stream->zrtp->cb.sched_cb.on_wait_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_wait_call_later(stream);
|
|
}
|
|
|
|
_clear_stream_crypto(stream);
|
|
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
zrtp_mutex_destroy(stream->stream_protector);
|
|
|
|
zrtp_memset(stream, 0, sizeof(zrtp_stream_t));
|
|
|
|
stream->mode = ZRTP_STREAM_MODE_UNKN;
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_NONE);
|
|
} else {
|
|
s = zrtp_status_wrong_state;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_stream_clear(zrtp_stream_t *stream)
|
|
{
|
|
/*
|
|
* This function can be called for two reasons: either our user is
|
|
* initiating the go-clear ritual or we accepting that ritual as
|
|
* initiated by the other end of the line. If our user initiates the
|
|
* go-clear process libzrtp switches to INITIATING_CLEAR and runs
|
|
* GoClear replays. The go-clear ritual can be started from SECURE state
|
|
* only. If the other end of the line is initiating and this function is
|
|
* being called to accept the go-clear procedure - protocol transites to
|
|
* CLEAR state imediately. One can accept go-clear from PENDING CLEAR
|
|
* state only. See state-macine diagram for more information.
|
|
*/
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
|
|
/* This function can be called in parallel to the main processing loop - protect stream data. */
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"CLEAR STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_SECURE:
|
|
/* Clearing ritual can't be started if "allow clear" is disabled */
|
|
if (stream->session->profile.allowclear) {
|
|
s = _zrtp_machine_enter_initiatingclear(stream);
|
|
}
|
|
break;
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
s = _zrtp_machine_enter_clear(stream);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void _initiating_secure(zrtp_stream_t *stream, zrtp_retry_task_t* task)
|
|
{
|
|
/*
|
|
* In accordance with the ZRTP standard, there can be multiple simultaneous
|
|
* DH streams, as well as preshared streams.
|
|
*
|
|
* Before entering the INITIATING_SECURE state, we check several conditions.
|
|
* For details see \doc\img\odg\zrtp_streams.odg and zrtp_statemach.odg)
|
|
*/
|
|
|
|
/* The first call to this function is already protected by a mutex in zrtp_process_srtp() */
|
|
uint8_t use_mutex = (task->_retrys > 0);
|
|
|
|
if (!task->_is_enabled) {
|
|
return;
|
|
}
|
|
|
|
if (use_mutex) {
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure iteration... ID=%u.\n", stream->id));
|
|
|
|
/* Skip the last replay after switching to another state to avoid unwanted replays */
|
|
if (stream->state <= ZRTP_STATE_START_INITIATINGSECURE)
|
|
{
|
|
stream->mode = _zrtp_define_stream_mode(stream);
|
|
ZRTP_LOG(3,(_ZTU_,"\tGot mode=%s. Check approval of starting.\n", zrtp_log_mode2str(stream->mode)));
|
|
if (!_zrtp_can_start_stream(stream, &stream->concurrent, stream->mode))
|
|
{
|
|
if (task->_retrys > ZRTP_PROCESS_T1_MAX_COUNT) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure. Max retransmissions count reached"
|
|
"for stream ID=%u.\n", stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_timeout, 0);
|
|
} else {
|
|
ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure. stream ID=%u is DH but one more DH"
|
|
" stream is in progress - waiting...\n", stream->id));
|
|
|
|
task->_retrys++;
|
|
if (stream->zrtp->cb.sched_cb.on_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_call_later(stream, task);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ZRTP_LOG(3,(_ZTU_,"\tMode=%s Cccepted. Starting ZRTP Initiator Protocol.\n", zrtp_log_mode2str(stream->mode)));
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_PROCESS);
|
|
_zrtp_machine_enter_initiatingsecure(stream);
|
|
}
|
|
}
|
|
|
|
if (use_mutex) {
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
}
|
|
}
|
|
|
|
zrtp_status_t _zrtp_machine_start_initiating_secure(zrtp_stream_t *stream)
|
|
{
|
|
/*
|
|
* This function creates a task to do retries of the first packet in the
|
|
* "Going secure" procedure, and then _initiating_secure() will start
|
|
* protocol.
|
|
*/
|
|
zrtp_retry_task_t* task = &stream->messages.dh_task;
|
|
task->_is_enabled = 1;
|
|
task->_retrys = 0;
|
|
task->callback = _initiating_secure;
|
|
task->timeout = ZRTP_PROCESS_T1;
|
|
|
|
/*
|
|
* Prevent race conditions on starting multiple streams.
|
|
*/
|
|
zrtp_mutex_lock(stream->session->init_protector);
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_START_INITIATINGSECURE);
|
|
_initiating_secure(stream, task);
|
|
|
|
zrtp_mutex_unlock(stream->session->init_protector);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
zrtp_status_t zrtp_stream_secure(zrtp_stream_t *stream)
|
|
{
|
|
/*
|
|
* Wrapper function for going into secure mode. It can be initiated in
|
|
* parallel to the main processing loop. The internal stream data has to
|
|
* be protected by mutex.
|
|
*/
|
|
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"SECURE STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
|
|
/* Limit ZRTP Session initiation procedure according to the license */
|
|
if ( (stream->state == ZRTP_STATE_CLEAR) && ZRTP_PASSIVE1_TEST(stream)) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
ZRTP_LOG(1,(_ZTU_,"\tWARNING! Can't Start Stream from %s state and with %d license mode. ID=%u\n",
|
|
zrtp_log_state2str(stream->state), stream->zrtp->lic_mode, stream->id));
|
|
|
|
if (!ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event ) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
}
|
|
}
|
|
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* State handlers */
|
|
/*===========================================================================*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_start( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
s = _zrtp_machine_process_hello(stream, packet);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello() failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in START state. */
|
|
}
|
|
|
|
/* Now we have ZIDs for both sides and can upload secrets from the cache */
|
|
s = _zrtp_prepare_secrets(stream->session);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_prepare_secrets() failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in START state. */
|
|
}
|
|
|
|
_send_helloack(stream);
|
|
_zrtp_change_state(stream, ZRTP_STATE_WAIT_HELLOACK);
|
|
break;
|
|
|
|
case ZRTP_HELLOACK:
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
_zrtp_change_state(stream, ZRTP_STATE_WAIT_HELLO);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_wait4hello( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
s = _zrtp_machine_process_hello(stream, packet);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello()2 failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in the current state. */
|
|
}
|
|
|
|
/* Now we have ZIDs for both sides and can upload secrets from the cache */
|
|
s = _zrtp_prepare_secrets(stream->session);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_prepare_secrets()2 failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in the current state. */
|
|
}
|
|
|
|
/* Start initiating the secure state if "autosecure" is enabled */
|
|
if ((stream->session->profile.autosecure) && ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (!stream->session->profile.discovery_optimization) {
|
|
_send_helloack(stream); /* Response with HelloAck before start computing DH value */
|
|
}
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
_send_helloack(stream);
|
|
|
|
if (!ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO: Switching to Clear due to Active/Passive restrictions.\n"));
|
|
}
|
|
|
|
s = _zrtp_machine_enter_clear(stream);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_wait4helloack( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t status = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
_send_helloack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
/* Passive Initiator can't talk to anyone */
|
|
if (ZRTP_PASSIVE2_TEST(stream))
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
status = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
status = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
status = zrtp_status_fail;
|
|
}
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tERROR: The endpoint is in passive mode and Signaling Initiator -"
|
|
" can't handle connections from anyone. ID=%u\n", stream->id));
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_service_unavail, 1);
|
|
}
|
|
} break;
|
|
|
|
case ZRTP_HELLOACK:
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
|
|
/* Start initiating the secure state if "autosecure" is enabled */
|
|
if ((stream->session->profile.autosecure) && ZRTP_PASSIVE1_TEST(stream)) {
|
|
status = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
if (!ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO: Switching to Clear due to Active/Passive restrictions.\n"));
|
|
}
|
|
status = _zrtp_machine_enter_clear(stream);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_clear( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_GOCLEAR:
|
|
_send_goclearack(stream);
|
|
break;
|
|
|
|
case ZRTP_HELLO:
|
|
_send_helloack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_initiatingclear( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_GOCLEARACK:
|
|
case ZRTP_COMMIT:
|
|
s = _zrtp_machine_enter_clear(stream);
|
|
break;
|
|
|
|
case ZRTP_NONE:
|
|
s = zrtp_status_drop;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_pendingclear( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_GOCLEAR:
|
|
_send_goclearack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_start_initiatingsecure( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
_send_helloack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_PROCESS);
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_secure( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_CONFIRM2:
|
|
_zrtp_packet_send_message(stream, ZRTP_CONFIRM2ACK, NULL);
|
|
break;
|
|
|
|
case ZRTP_SASRELAY:
|
|
/*
|
|
* _zrtp_machine_process_sasrelay() updates SAS, sends events and does
|
|
* other things if SAS transferring is allowed
|
|
*/
|
|
s = _zrtp_machine_process_sasrelay(stream, packet);
|
|
if (zrtp_status_ok == s) {
|
|
_zrtp_packet_send_message(stream, ZRTP_RELAYACK, NULL);
|
|
}
|
|
break;
|
|
|
|
case ZRTP_GOCLEAR:
|
|
s = _zrtp_machine_process_goclear(stream, packet);
|
|
if (zrtp_status_ok == s) {
|
|
s = _zrtp_machine_enter_pendingclear(stream);
|
|
_send_goclearack(stream);
|
|
}
|
|
break;
|
|
|
|
case ZRTP_NONE:
|
|
s = _zrtp_protocol_decrypt(stream->protocol, packet, 1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_initiatingerror( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_ERROR:
|
|
_zrtp_machine_enter_pendingerror(stream, ((zrtp_packet_Error_t*) packet->message)->code );
|
|
break;
|
|
|
|
case ZRTP_ERRORACK:
|
|
_zrtp_machine_switch_to_error(stream);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_nozrtp( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
s = _zrtp_machine_process_hello(stream, packet);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello()3 failed with status=%d ID=%u.\n", s, stream->id));
|
|
break;
|
|
}
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_START);
|
|
_zrtp_machine_start_send_and_resend_hello(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT: /* this logic should be similar to Commit handler in ZRTP_STATE_WAIT_HELLOACK state */
|
|
{
|
|
/* Passive Initiator can't talk to anyone */
|
|
if (ZRTP_PASSIVE2_TEST(stream))
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tERROR: The endpoint is in passive mode and Signaling Initiator -"
|
|
" can't handle connections from anyone. ID=%u\n", stream->id));
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event ) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_service_unavail, 1);
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/* Initiator logic */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_initiatingsecure(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirmack(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirm1(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
/* Responder logic */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_pendingsecure(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirm2(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
/* PBX transferring logic */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_sasrelaying(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
|
|
/* Driven Discovery state-machine */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_driven_initiator(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_driven_responder(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_driven_pending(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
#endif
|
|
|
|
state_handler_t* state_handler[ZRTP_STATE_COUNT] =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
_zrtp_machine_process_while_in_start,
|
|
_zrtp_machine_process_while_in_wait4helloack,
|
|
_zrtp_machine_process_while_in_wait4hello,
|
|
_zrtp_machine_process_while_in_clear,
|
|
_zrtp_machine_process_while_in_start_initiatingsecure,
|
|
_zrtp_machine_process_while_in_initiatingsecure,
|
|
_zrtp_machine_process_while_in_waitconfirm1,
|
|
_zrtp_machine_process_while_in_waitconfirmack,
|
|
_zrtp_machine_process_while_in_pendingsecure,
|
|
_zrtp_machine_process_while_in_waitconfirm2,
|
|
_zrtp_machine_process_while_in_secure,
|
|
_zrtp_machine_process_while_in_sasrelaying,
|
|
_zrtp_machine_process_while_in_initiatingclear,
|
|
_zrtp_machine_process_while_in_pendingclear,
|
|
_zrtp_machine_process_while_in_initiatingerror,
|
|
NULL,
|
|
NULL,
|
|
#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
|
|
_zrtp_machine_process_while_in_driven_initiator,
|
|
_zrtp_machine_process_while_in_driven_responder,
|
|
_zrtp_machine_process_while_in_driven_pending,
|
|
#endif
|
|
_zrtp_machine_process_while_in_nozrtp
|
|
};
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* State switchers */
|
|
/*===========================================================================*/
|
|
|
|
static void _zrtp_machine_switch_to_error(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_clear_stream_crypto(stream);
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_ERROR);
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_security_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_security_event(stream, ZRTP_EVENT_PROTOCOL_ERROR);
|
|
}
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_not_secure) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_not_secure(stream);
|
|
}
|
|
stream->last_error = 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_enter_pendingclear(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_PENDINGCLEAR);
|
|
|
|
/*
|
|
* We have to destroy the ZRTP Session Key because user may not press "clear
|
|
* button", and the remote endpoint may subsequently initiate a new secure
|
|
* session. Other secret values will be destroyed in Clear state or
|
|
* rewritten with new.
|
|
*/
|
|
{
|
|
zrtp_string64_t new_zrtpsess = ZSTR_INIT_EMPTY(new_zrtpsess);
|
|
// TODO: hash
|
|
stream->session->hash->hash( stream->session->hash,
|
|
ZSTR_GV(stream->session->zrtpsess),
|
|
ZSTR_GV(new_zrtpsess));
|
|
zrtp_zstrcpy(ZSTR_GV(stream->session->zrtpsess), ZSTR_GV(new_zrtpsess));
|
|
}
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PENDINGCLEAR);
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static zrtp_status_t _zrtp_machine_enter_initiatingclear(zrtp_stream_t* stream)
|
|
{
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_INITIATINGCLEAR);
|
|
|
|
{
|
|
zrtp_string64_t new_zrtpsess = ZSTR_INIT_EMPTY(new_zrtpsess);
|
|
// TODO: hash
|
|
stream->session->hash->hash( stream->session->hash,
|
|
ZSTR_GV(stream->session->zrtpsess),
|
|
ZSTR_GV(new_zrtpsess));
|
|
zrtp_zstrcpy(ZSTR_GV(stream->session->zrtpsess), ZSTR_GV(new_zrtpsess));
|
|
}
|
|
|
|
return _zrtp_machine_start_send_and_resend_goclear(stream);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static zrtp_status_t _zrtp_machine_enter_clear(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_clear_stream_crypto(stream);
|
|
_zrtp_change_state(stream, ZRTP_STATE_CLEAR);
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_CLEAR);
|
|
}
|
|
|
|
/*
|
|
* 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_passive) {
|
|
if (stream->linked_mitm && stream->linked_mitm->peer_super_flag) {
|
|
ZRTP_LOG(2,(_ZTU_,"INFO: Current stream ID=%u was switched to CLEAR-mode due to Active/Passive"
|
|
" restrictions, but we are running in MiTM mode and peer linked stream is"
|
|
" Super-active. Go Secure!\n", stream->id));
|
|
|
|
/* @note: don't use zrtp_secure_stream() wrapper as it checks for Active/Passive stuff. */
|
|
_zrtp_machine_start_initiating_secure(stream);
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_enter_initiatingerror( zrtp_stream_t *stream,
|
|
zrtp_protocol_error_t code,
|
|
uint8_t notif)
|
|
{
|
|
if ( (ZRTP_STATE_ERROR != stream->state) &&
|
|
(ZRTP_STATE_INITIATINGERROR != stream->state) &&
|
|
(ZRTP_STATE_PENDINGERROR != stream->state) )
|
|
{
|
|
stream->last_error = code;
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tEnter InitiatingError State with ERROR:<%s>, notification %s. ID=%u\n",
|
|
zrtp_log_error2str(stream->last_error), (notif?"Enabled":"Disabled"), stream->id));
|
|
|
|
/* If we can't deliver a ZRTP message, just switch to the ERROR state. */
|
|
if (notif) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_INITIATINGERROR);
|
|
_zrtp_machine_start_send_and_resend_error(stream);
|
|
} else {
|
|
_zrtp_machine_switch_to_error(stream);
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
zrtp_status_t _zrtp_machine_enter_pendingerror(zrtp_stream_t *stream, zrtp_protocol_error_t code)
|
|
{
|
|
ZRTP_LOG(3,(_ZTU_,"\tEnter PendingError State with ERROR:<%s>. ID=%u\n",
|
|
zrtp_log_error2str(stream->last_error), stream->id));
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_PENDINGERROR);
|
|
|
|
stream->last_error = code;
|
|
_zrtp_machine_start_send_and_resend_errorack(stream);
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* Packet handlers */
|
|
/*===========================================================================*/
|
|
|
|
zrtp_status_t _zrtp_machine_process_goclear(zrtp_stream_t* stream, zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_packet_GoClear_t *goclear = (zrtp_packet_GoClear_t*) packet->message;
|
|
zrtp_string128_t clear_hmac = ZSTR_INIT_EMPTY(clear_hmac);
|
|
static const zrtp_string16_t clear_hmac_str = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_CLEAR_HMAC_STR);
|
|
|
|
if (!stream->allowclear) {
|
|
ZRTP_LOG(2, (_ZTU_,"\tWARNING! Allowclear is disabled but GoClear was received. ID=%u.\n", stream->id));
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_goclear_unsp, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
stream->session->hash->hmac( stream->session->hash,
|
|
ZSTR_GV(stream->cc.peer_hmackey),
|
|
ZSTR_GV(clear_hmac_str),
|
|
ZSTR_GV(clear_hmac));
|
|
clear_hmac.length = ZRTP_HMAC_SIZE;
|
|
|
|
if (0 != zrtp_memcmp(clear_hmac.buffer, goclear->clear_hmac, ZRTP_HMAC_SIZE)) {
|
|
ZRTP_LOG(2, (_ZTU_,"\tWARNING! Wrong GoClear hmac. ID=%u.\n", stream->id));
|
|
return zrtp_status_fail; /* EH: Just ignore malformed packets */
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_hello(zrtp_stream_t* stream, zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_session_t* session = stream->session;
|
|
zrtp_packet_Hello_t* peer_hello = NULL;
|
|
uint32_t comp_block_len = 0;
|
|
uint8_t id = 0;
|
|
|
|
/* Size of HELLO packet must be bigger then <RTP+static HELLO part>. */
|
|
if (*(packet->length) < (ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE)) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO static size=%d must be=%d. ID=%u\n", *packet->length,
|
|
ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
peer_hello = (zrtp_packet_Hello_t*) packet->message;
|
|
|
|
/* Now we can verify packet size according to size of its parts */
|
|
comp_block_len = ( peer_hello->hc + peer_hello->cc +
|
|
peer_hello->ac + peer_hello->kc +
|
|
peer_hello->sc) * ZRTP_COMP_TYPE_SIZE;
|
|
|
|
if (*packet->length < (ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + comp_block_len + ZRTP_HMAC_SIZE))
|
|
{
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO dynamic size=%d must be=%d. ID=%u\n", *packet->length,
|
|
comp_block_len+ ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Every component quantity must be less than or equal to 7 */
|
|
if ( (peer_hello->hc > ZRTP_MAX_COMP_COUNT) || (peer_hello->cc > ZRTP_MAX_COMP_COUNT) ||
|
|
(peer_hello->ac > ZRTP_MAX_COMP_COUNT) || (peer_hello->kc > ZRTP_MAX_COMP_COUNT) ||
|
|
(peer_hello->sc > ZRTP_MAX_COMP_COUNT) )
|
|
{
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO packet data. Components count can't be greater"
|
|
" then 7. ID=%u\n", stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Print out ZRTP Hello message for debug purposes */
|
|
{
|
|
char print_buffer[ZRTP_MAX_COMP_COUNT*20];
|
|
zrtp_memcpy(print_buffer, peer_hello->comp, comp_block_len);
|
|
print_buffer[comp_block_len] = 0;
|
|
ZRTP_LOG(3,(_ZTU_,"\tProcessing HELLO from %.16s V=%.4s, P=%d, M=%d.\n",
|
|
peer_hello->cliend_id, peer_hello->version, peer_hello->pasive, peer_hello->mitmflag));
|
|
ZRTP_LOG(3,(_ZTU_,"\t\tac=%d cc=%d sc=%d kc=%d\n",
|
|
peer_hello->ac, peer_hello->cc, peer_hello->sc, peer_hello->kc));
|
|
ZRTP_LOG(3,(_ZTU_,"\t\t%s\n", print_buffer));
|
|
}
|
|
|
|
/*
|
|
* Check protocol version. Try to resolve versions missmatch according to ZRTP Draft sec. 5.1
|
|
*/
|
|
{
|
|
uint32_t peer_version = 0;
|
|
peer_version = (char)((*peer_hello->version) - '0') *10; /* only 3 first octets are significant */
|
|
peer_version += (char)(*(peer_hello->version+2) - '0');
|
|
|
|
if ((ZRTP_PROTOCOL_VERSION_VALUE/10) == peer_version) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tReceived HELLO had the same protocol V.\n"));
|
|
}
|
|
else if ((ZRTP_PROTOCOL_VERSION_VALUE/10) < peer_version) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO greater ZRTP V=%d - wait for other party"
|
|
" to resolve this issue. ID=%u.\n", peer_version, stream->id));
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received a ZRTP_HELLO smaller ZRTP V=%d and we don't"
|
|
" support it - terminate session. ID=%u\n", peer_version, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_version, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
}
|
|
|
|
/* Close session if ZID duplication */
|
|
if (!zrtp_memcmp(stream->messages.hello.zid, peer_hello->zid, sizeof(zrtp_zid_t))) {
|
|
ZRTP_LOG(2,(_ZTU_,ZRTP_EQUAL_ZID_WARNING_STR));
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_equal_zid, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* All streams within a single session MUST have the same ZID */
|
|
if (session->peer_zid.length > 0) {
|
|
if (0 != zrtp_memcmp(session->peer_zid.buffer, peer_hello->zid, sizeof(zrtp_zid_t))) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO which had a different ZID from that of the"
|
|
" previous stream within the same session. sID=%u ID=%u\n", session->id, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_wrong_zid, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
} else {
|
|
zrtp_zstrncpyc(ZSTR_GV(session->peer_zid), (const char*) peer_hello->zid, sizeof(zrtp_zid_t));
|
|
}
|
|
|
|
/*
|
|
* Process Remote flags.
|
|
*/
|
|
if (peer_hello->pasive && peer_hello->uflag) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO which both P and U flags set.\n"));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
stream->peer_passive = peer_hello->pasive;
|
|
stream->peer_super_flag = peer_hello->uflag;
|
|
|
|
stream->peer_mitm_flag = peer_hello->mitmflag;
|
|
if (stream->peer_mitm_flag) {
|
|
stream->mitm_mode = ZRTP_MITM_MODE_CLIENT;
|
|
}
|
|
|
|
/* Current version doesn't support Digital Signatures. Ignore peer Hello with S flag enabled. */
|
|
if (peer_hello->sigflag) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received a ZRTP_HELLO with S flag enabled. We don't support Digital Signatures - ignore message.\n"));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Copy packet for future hashing */
|
|
zrtp_memcpy(&stream->messages.peer_hello, peer_hello, zrtp_ntoh16(peer_hello->hdr.length)*4);
|
|
stream->is_hello_received = 1;
|
|
|
|
/*
|
|
* Choose PK exchange scheme and PK mode.
|
|
* We do this right after receiving Hello to speedup DH calculations.
|
|
*/
|
|
stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, ZRTP_PKTYPE_DH3072, session->zrtp);
|
|
id = _zrtp_choose_best_comp(&session->profile, peer_hello, ZRTP_CC_PKT);
|
|
if (id != ZRTP_COMP_UNKN) {
|
|
stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, id, session->zrtp);
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tReceived HELLO Accepted\n"));
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* Packet senders */
|
|
/*===========================================================================*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_hello(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if ((task->_retrys == ZRTP_NO_ZRTP_FAST_COUNT) && !stream->is_hello_received) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING! HELLO have been resent %d times without a response."
|
|
" Raising ZRTP_EVENT_NO_ZRTP_QUICK event. ID=%u\n", task->_retrys, stream->id));
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_NO_ZRTP_QUICK);
|
|
}
|
|
}
|
|
|
|
if (task->_retrys >= (uint32_t)((ZRTP_STATE_WAIT_HELLOACK==stream->state)?ZRTP_T1_MAX_COUNT_EXT:ZRTP_T1_MAX_COUNT)) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING! HELLO Max retransmissions count reached (%d retries). ID=%u\n", task->_retrys, stream->id));
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_clear_stream_crypto(stream);
|
|
_zrtp_change_state(stream, ZRTP_STATE_NO_ZRTP);
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_NO_ZRTP);
|
|
}
|
|
} else if (task->_is_enabled) {
|
|
zrtp_status_t s = _zrtp_packet_send_message(stream, ZRTP_HELLO, &stream->messages.hello);
|
|
task->timeout = _zrtp_get_timeout((uint32_t)task->timeout, ZRTP_HELLO);
|
|
if (zrtp_status_ok == s) {
|
|
task->_retrys++;
|
|
}
|
|
|
|
|
|
if (stream->zrtp->cb.sched_cb.on_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_call_later(stream, task);
|
|
}
|
|
}
|
|
}
|
|
|
|
zrtp_status_t _zrtp_machine_start_send_and_resend_hello(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.hello_task;
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_hello;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_hello(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
static void _send_helloack(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_packet_send_message(stream, ZRTP_HELLOACK, NULL);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_goclear(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if (task->_is_enabled) {
|
|
if (task->_retrys > ZRTP_T2_MAX_COUNT) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING!: GOCLEAR Nax retransmissions count reached. ID=%u\n", stream->id));
|
|
_zrtp_machine_enter_clear(stream);
|
|
} else {
|
|
zrtp_packet_GoClear_t* goclear = (zrtp_packet_GoClear_t*) &stream->messages.goclear;
|
|
|
|
_zrtp_packet_send_message(stream, ZRTP_GOCLEAR, goclear);
|
|
task->_retrys++;
|
|
if (stream->zrtp->cb.sched_cb.on_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_call_later(stream, task);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_goclear(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.goclear_task;
|
|
zrtp_string128_t clear_hmac = ZSTR_INIT_EMPTY(clear_hmac);
|
|
static const zrtp_string16_t clear_hmac_str = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_CLEAR_HMAC_STR);
|
|
|
|
zrtp_memset(&stream->messages.goclear, 0, sizeof(zrtp_packet_GoClear_t));
|
|
|
|
/* Compute Clear HMAC as: HMAC(hmackey, "Clear hmac") */
|
|
stream->session->hash->hmac( stream->session->hash,
|
|
ZSTR_GV(stream->cc.hmackey),
|
|
ZSTR_GV(clear_hmac_str),
|
|
ZSTR_GV(clear_hmac));
|
|
clear_hmac.length = ZRTP_HMAC_SIZE;
|
|
|
|
zrtp_memcpy(stream->messages.goclear.clear_hmac, clear_hmac.buffer, clear_hmac.length);
|
|
_zrtp_packet_fill_msg_hdr( stream,
|
|
ZRTP_GOCLEAR,
|
|
sizeof(zrtp_packet_GoClear_t) - sizeof(zrtp_msg_hdr_t),
|
|
&stream->messages.goclear.hdr);
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_goclear;
|
|
task->timeout = ZRTP_T2;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_goclear(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
static void _send_goclearack(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_packet_send_message(stream, ZRTP_GOCLEARACK, NULL);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_error(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if (task->_retrys >= ZRTP_ETI_MAX_COUNT) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! ERROR Max retransmissions count reached. ID=%u\n", stream->id));
|
|
_zrtp_machine_switch_to_error(stream);
|
|
} else if (task->_is_enabled) {
|
|
if (zrtp_status_ok == _zrtp_packet_send_message(stream, ZRTP_ERROR, &stream->messages.error)) {
|
|
task->_retrys++;
|
|
}
|
|
if (stream->zrtp->cb.sched_cb.on_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_call_later(stream, task);
|
|
}
|
|
}
|
|
}
|
|
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_error(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.error_task;
|
|
|
|
zrtp_memset(&stream->messages.error, 0, sizeof(zrtp_packet_Error_t));
|
|
stream->messages.error.code = zrtp_hton32(stream->last_error);
|
|
|
|
_zrtp_packet_fill_msg_hdr( stream,
|
|
ZRTP_ERROR,
|
|
sizeof(zrtp_packet_Error_t) - sizeof(zrtp_msg_hdr_t),
|
|
&stream->messages.error.hdr);
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_error;
|
|
task->timeout = ZRTP_ET;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_error(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_errorack(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if (task->_retrys >= ZRTP_ETR_MAX_COUNT) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! ERRORACK Max retransmissions count reached. ID=%u\n", stream->id));
|
|
_zrtp_machine_switch_to_error(stream);
|
|
} else if (task->_is_enabled) {
|
|
if (zrtp_status_ok == _zrtp_packet_send_message(stream, ZRTP_ERRORACK, NULL)) {
|
|
task->_retrys++;
|
|
}
|
|
if (stream->zrtp->cb.sched_cb.on_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_call_later(stream, task);
|
|
}
|
|
}
|
|
}
|
|
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_errorack(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.errorack_task;
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_errorack;
|
|
task->timeout = ZRTP_ET;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_errorack(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
void _clear_stream_crypto(zrtp_stream_t* stream)
|
|
{
|
|
if (stream->protocol) {
|
|
_zrtp_protocol_destroy(stream->protocol);
|
|
stream->protocol = 0;
|
|
}
|
|
|
|
zrtp_wipe_zstring(ZSTR_GV(stream->cc.hmackey));
|
|
zrtp_wipe_zstring(ZSTR_GV(stream->cc.peer_hmackey));
|
|
zrtp_wipe_zstring(ZSTR_GV(&stream->cc.zrtp_key));
|
|
zrtp_wipe_zstring(ZSTR_GV(stream->cc.peer_zrtp_key));
|
|
}
|