ZRTP交叉编译与移植

1 ZRTP源码下载

这里采用的是libzrtp来自于freeswitch:libs/libzrtp。

2 ZRTP交叉编译

zrtp编译比较简单,采用configure进行编译在根目录心中zrtp编译脚本,只需要指定交叉编译工具链和安装地址即可。脚本如下所示:

unset CC CFLAGS LDFLAGS CPPFLAGS CPP LD STRIP
./configure --host=arm-linux-androideabi --prefix=`pwd`/../objects/ 

成果物如下所示include和lib库:
ZRTP交叉编译与移植_第1张图片
ZRTP交叉编译与移植_第2张图片

3 ZRTP移植

zrtp移植主要对zrtp库进行封装,对外提供初始化和加密解密能力。接口设计如下:

3.1 API设计

typedef void ZrtpEventObserver(int id, BOOL encrypt);

typedef struct zrtp_handle_t zrtp_handle_t;

typedef struct zrtp_handle_t{
	//加密rtp
 	void (*encrypt_rtp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data,unsigned char* out_data,int bytes_in,	int* bytes_out);
	//解密rtp
 	void (*decrypt_rtp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data, unsigned char* out_data, int bytes_in, int* bytes_out);
	//加密rtcp
 	void (*encrypt_rtcp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data,unsigned char* out_data, int bytes_in,int* bytes_out);
	//解密rtcp
 	void (*decrypt_rtcp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data, unsigned char* out_data, int bytes_in, int* bytes_out);
 	
 	BOOL (*is_enabled)(zrtp_handle_t *pthis);

 	void *priv;
}zrtp_handle_t;

//构建zrtp会话
int zrtp_handle_alloc(zrtp_handle_t **ppthis, const WebRtc_Word32 id, ZrtpEventObserver *observer);
//释放zrtp会话
void zrtp_handle_free(zrtp_handle_t *pthis);
//初始化
int zrtp_handle_init();
//反初始化
void zrtp_handle_denit();

3.2 初始化

初始化只需要初始化一次,初始化需要注册发送回调函数,这里协商发送的数据包构造好后最终是有这个接口on_send_packet返回到应用发送。

int zrtp_handle_init()
{
	zrtp_config_defaults(&g_zrtp_config);
	zrtp_randstr2((unsigned char *)g_zrtp_config.client_id, sizeof(zrtp_client_id_t));
//	zrtp_randstr2((unsigned char *)g_zrtp_config.zid, sizeof(zrtp_zid_t));
	g_zrtp_config.lic_mode = ZRTP_LICENSE_MODE_ACTIVE;
	g_zrtp_config.cb.misc_cb.on_send_packet			= on_send_packet;
	g_zrtp_config.cb.event_cb.on_zrtp_protocol_event	= on_zrtp_protocol_event;
	g_zrtp_config.cb.event_cb.on_zrtp_security_event	= on_zrtp_security_event;
	g_zrtp_config.cb.event_cb.on_zrtp_secure			= on_zrtp_secure;
	g_zrtp_config.cb.event_cb.on_zrtp_not_secure		= on_zrtp_not_secure;

	zrtp_status_t s = zrtp_init(&g_zrtp_config, &g_pzrtp_global);
	if (zrtp_status_ok != s) {
		printf("ZRTP init failed, status = %d \n", s);
		return -1;
	}
	
	return 0;
}

void zrtp_handle_denit()
{
	if (NULL != g_pzrtp_global) {
		zrtp_down(g_pzrtp_global);
		g_pzrtp_global = NULL;
	}
}

3.3 会话实例

每一路会话需要实例化一个zrtphandle对象,需要传入一个随机的zrtpid,和观察者。

int zrtp_handle_alloc(zrtp_handle_t **ppthis, const WebRtc_Word32 id, ZrtpEventObserver *observer)
{
    if(!ppthis)
        return -1;      

	zrtp_handle_t *phl = (zrtp_handle_t *)malloc(sizeof(zrtp_handle_t));	
	if(!phl)
	{
		return -1;
	}

	priv_t *ppriv = (priv_t *)malloc(sizeof(priv_t));	
	if(!ppriv)
	{
	    free(phl);
		return -1;
	}

	memset(phl, 0, sizeof(zrtp_handle_t));
	memset(ppriv, 0, sizeof(priv_t));
	
    phl->priv = ppriv;   

	ppriv->__zrtp_session = NULL;
	ppriv->__zrtp_audio = NULL;
	ppriv->__lSSRC = 0;
	ppriv->__rSSRC = 0;
	ppriv->__stream_seq = 0;
	ppriv->__stream_timestamp = 0;
	ppriv->__is_first_start_stream = 0;
	ppriv->__is_need_send_hello = 0;
	ppriv->__is_zrtp_enable = 0;
	ppriv->__obverserptr = observer;
	ppriv->id = id;
	  
	ppriv->__buffer_out_data = (char *)malloc(IP_PACKET_SIZE);
    phl->audio_encrypt_rtp = encrypt;
    phl->audio_decrypt_rtp = decrypt;
    phl->audio_encrypt_rtcp = encrypt_rtcp;
    phl->audio_decrypt_rtcp = decrypt_rtcp;

    *ppthis = phl;
    
    return 0;
}

void zrtp_handle_free(zrtp_handle_t *pthis) 
{
	if(!pthis)
		return;
	
	DisableZsrtp(pthis);
	
	priv_t *ppriv = pthis->priv;
	
	if (NULL != ppriv->__buffer_out_data) {
		free(ppriv->__buffer_out_data);
		ppriv->__buffer_out_data = NULL;
	}

	if (pthis->priv){
		free(pthis->priv);
		pthis->priv = NULL;
	}	
	free(pthis);
	pthis = NULL;
}

3.4 加解密实现

加解密会用首先会进入到协商流程zrtp_stream_start完成协商,之后再进入加解密流程zrtp_process_rtp。

int encrypt(zrtp_handle_t *pthis,
		int channel,
		unsigned char* in_data,
		unsigned char* out_data,
		int bytes_in,
		int* bytes_out) {

	
    priv_t *ppriv = pthis->priv;
    
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        return 0;
    }

	if(ppriv->__is_need_send_hello){
	   
		zrtp_stream_start(ppriv->__zrtp_audio, ppriv->__lSSRC);
		   
		ppriv->__is_need_send_hello = RL_FALSE;
	}
   
	if(!ppriv->__buffer_out_data_consumed){
		++ppriv->__buffer_out_data_retry;
		
		memcpy(out_data, ppriv->__buffer_out_data, ppriv->__buffer_out_data_bytes);
		*bytes_out = ppriv->__buffer_out_data_bytes;
		ppriv->__buffer_out_data_consumed = RL_TRUE;
		return 0;
	}

	if (-1 == ppriv->__voice_encrypt_status && ppriv->__buffer_out_data_retry == ZRTP_COUNT_THRESHOLD_DEFAULT){
		// LinKy: Rollback to unecrypt, this operation will close zrtp session!
		
		UpdateEncryptStatus(pthis, RL_FALSE);
		//return 0;
	}

	if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE) {
		// Not ready, use original data	
		
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return 1;
	}

	char packet[IP_PACKET_SIZE];
	ZRTP_UNALIGNED(zrtp_rtp_hdr_t) *rtp_hdr = (zrtp_rtp_hdr_t*)packet;
	
	/* Fill RTP Header according to the specification */
	zrtp_memset(rtp_hdr, 0, sizeof(zrtp_rtp_hdr_t));
	rtp_hdr->version = 2;						/* Current RTP version 2 */
	rtp_hdr->pt = 0;							/* PCMU padding type */
	rtp_hdr->ssrc = zrtp_hton32(ppriv->__lSSRC);		/* Use stream Identifier as it's SSRC */
	if (ppriv->__stream_seq >= 0xFFFF) {
		ppriv->__stream_seq = 0;
	}
	
	rtp_hdr->seq = libzrtp_swap16(ppriv->__stream_seq++);
	rtp_hdr->ts = zrtp_hton32(ppriv->__stream_timestamp);
	ppriv->__stream_timestamp += 20;					// LinKy: Assume the interval is 20ms
	zrtp_memcpy(packet + sizeof(zrtp_rtp_hdr_t), in_data, bytes_in);
	unsigned int size = sizeof(zrtp_rtp_hdr_t) + bytes_in;

	zrtp_status_t s = zrtp_process_rtp(ppriv->__zrtp_audio, packet, &size);
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			memcpy(out_data, packet, size);
			*bytes_out = size;
			return 2;
		}
 		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec

			// LinKy: Shall we rollback to unencrypt here?
			return 3;
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
			return 4;
		}
 		break;
	}
	return 0;
}

int decrypt(zrtp_handle_t *pthis,
	    int channel,
	    unsigned char* in_data,
	    unsigned char* out_data,
	    int bytes_in,
	    int* bytes_out) {
	unsigned int size = bytes_in;
	
	
	priv_t *ppriv = pthis->priv;
	
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        
        return 0;
    }

	
	if(ppriv->__is_need_send_hello){
		zrtp_stream_start(ppriv->__zrtp_audio, ppriv->__lSSRC);
		
		ppriv->__is_need_send_hello = RL_FALSE;
	}

	
	//if zrtp success,ppriv->__buffer_in_data_retry Approximately equals 18;
	if (-1 == ppriv->__voice_encrypt_status && ppriv->__buffer_in_data_retry >= ZRTP_COUNT_THRESHOLD_DEFAULT){
	//if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE){
		// Not ready, use original data
		
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return 1;
	}
	else if(ppriv->__voice_encrypt_status == 0)
	{
		
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return 1;
	}
	
	ppriv->__buffer_in_data_retry++;
	
	char packet[IP_PACKET_SIZE];
	memcpy(packet, in_data, bytes_in);
	zrtp_status_t s = zrtp_process_srtp(ppriv->__zrtp_audio, packet, &size);

	
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			
			char *body = packet + sizeof(zrtp_rtp_hdr_t);
			memcpy(out_data, body, size - sizeof(zrtp_rtp_hdr_t));
			*bytes_out = size - sizeof(zrtp_rtp_hdr_t);
			return 2;
		}
		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec

			// LinKy: Yep, we drop it, use original data to play!
			
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
			return 3;
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//
			// LinKy: Be careful! This may cause noise if data is encrypted actually!
			
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
			return 4;
		}
		break;
	}
	return 0;
}

void encrypt_rtcp(zrtp_handle_t *pthis,
    int channel,
    unsigned char* in_data,
    unsigned char* out_data,
    int bytes_in,
    int* bytes_out) {
        
    priv_t *ppriv = pthis->priv;
	unsigned int size = bytes_in;
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        return;
    }

	if(ppriv->__is_need_send_hello){
		zrtp_stream_start(ppriv->__zrtp_audio, ppriv->__lSSRC);
		ppriv->__is_need_send_hello = RL_FALSE;
	}
	
	if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE) {
		// Not ready, use original data
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return;
	}
	
	char packet[IP_PACKET_SIZE];
	ZRTP_UNALIGNED(zrtp_rtcp_hdr_t) *rtcp_hdr = (zrtp_rtcp_hdr_t*)packet;
	
	/* Fill RTCP Header according to the specification */
	rtcp_hdr->rc = 0;
	rtcp_hdr->version = 2;
	rtcp_hdr->ssrc = zrtp_hton32(ppriv->__lSSRC);

	
	/* Get RTP body from PGP words lists. Put RTCP marker at the beginning */
	zrtp_memcpy(packet + sizeof(zrtp_rtcp_hdr_t), "RTCP", 4);		
	zrtp_memcpy(packet + sizeof(zrtp_rtcp_hdr_t) + 4, in_data, bytes_in);
	size = sizeof(zrtp_rtcp_hdr_t) + bytes_in + 4;
	/* RTCP packets sould be 32 byes aligned */
	size += (size % 4) ? (4 - size % 4) : 0;
	
	zrtp_status_t s = zrtp_process_rtcp(ppriv->__zrtp_audio, packet, &size);
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			memcpy(out_data, packet, size);
			*bytes_out = size;
		}
		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec
			
			// LinKy: Shall we rollback to unencrypt here?
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
		}
		break;
	}
}

void decrypt_rtcp(zrtp_handle_t *pthis,
	    int channel,
	    unsigned char* in_data,
	    unsigned char* out_data,
	    int bytes_in,
	    int* bytes_out) {
	        
	priv_t *ppriv = pthis->priv;
	unsigned int size = bytes_in;
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        return;
    }

	if(ppriv->__is_first_start_stream){
		zrtp_stream_start(ppriv->__zrtp_audio, channel);
		ppriv->__is_first_start_stream = RL_FALSE;
	}

	if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE) {
		// Not ready, use original data
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return;
	}
	
	char packet[IP_PACKET_SIZE];
	memcpy(packet,in_data,bytes_in);
	zrtp_status_t s = zrtp_process_srtcp(ppriv->__zrtp_audio, packet, &size);
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			char *body = packet + sizeof(zrtp_rtp_hdr_t);
			memcpy(out_data, body, size - sizeof(zrtp_rtp_hdr_t));
			*bytes_out = size - sizeof(zrtp_rtp_hdr_t);
		}
		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec

			// LinKy: Yep, we drop it, use original data to play!
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//

			// LinKy: Be careful! This may cause noise if data is encrypted actually!
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
		}
		break;
	}
}

4 ZRTP抓包分析

一个完整的ZRTP协商流程,首先是hello进双方加密方式的交换,之后会进行一个收到的应答,然后是commit消息确认HMAC秘钥,DHPart消息交换公钥,Confirm消息确认签名,最后是ConfACK应答。下面是wareshark抓包显示的交互流程。
ZRTP交叉编译与移植_第3张图片

你可能感兴趣的:(音视频传输安全,音视频,安全,zrtp)