How to Get Up and Running Quickly with libZRTP

1. Introduction

The libzrtp library is a cross-platform implementation of ZRTP, a VoIP encryption protocol developed by Phil Zimmermann. libzrtp is suitable for inclusion in software VoIP clients, firmware for hardware VoIP phones, VoIP PBX servers, mobile VoIP clients, and SIP border control servers, enabling a VoIP application to interoperate and make secure calls with the rest of the ZRTP community.

The libzrtp library consists of three main components: the protocol module responsible for the safe connection of a call, the encryption module, and a set of interfaces. ZRTP works by assuming control of the VoIP traffic and initiating an encrypted connection between two ZRTP endpoints after a safe mode is achieved. To integrate the library, please review our documentation on the ZRTP interfaces, connections management, and integration plan.

2. Preparation

libzrtp is relatively easy-to-use. After reading the documentation on both libzrtp and the ZRTP protocol, the next step is installing the library, where it can be used on platforms such as Linux, MacOS, Windows, Windows CE, Symbian and Windows kernel mode.

To provide portability, libzrtp uses a set of system-dependent functions as a set of interfaces, which are divided into the following groups:

libzrtp requires some retrofit work, where the developer will need to write about 15 to 20 lines of code. To see how the library works, please refer to the libzrtp_test application, described in detail in Guide to the libZRTP Test Suite.

3. Quick start

An overview for creating an encrypted channel using libzrtp:

3.1 Initialization

The library supports profiling and dictating different channel parameters, though the initialization can be performed by one function call with default parameters.

typedef struct testcon
{
    zrtp_conn_ctx_t    zsession;
    zrtp_stream_ctx_t* audio;   // ZRTP stream for voice encr
    zrtp_stream_ctx_t* video;   // ZRTP stream for video encr.
    
} testcon_t;

testcon_t safe_connection;


zrtp_global_t zrtp_global;      // persistent storage for libzrtp data


if ( zrtp_status_ok != zrtp_init(&zrtp_global, "zrtp_testapp") )
{
    // Some error during initialization
}

// library has been initialized and is ready to use
 . . 

3.2 Sessions/Streams

The library operates with the ZRTP streams concept, where each packet is encrypted within this stream. The streams are created before the start of the encryption process, which requires: allocation of memory for a session, session initialization, and adding the appropriate number of streams.

if (zrtp_status_ok == zrtp_init_session_ctx( &safe_connection.zsession,
                                             &zrtp_global,
                                             NULL;
                                             zid) )
{
    //
    // New ZRTP session with default options has been created - 
    // attaching streams. (Voice and then video)
    //
    safe_connection->audio = zrtp_attach_stream(safe_connection->zsession,
audio_ssrc);
    safe_connection->video = zrtp_attach_stream(safe_connection->zsession,
video_ssrc);

    if ( !safe_connection->video || !safe_connection->audio )
    {
        // Session stack is overflow or some other error
    }
}

3.3 Protocol Initiation

To create an encrypted channel, run the ZRTP engine for each stream added to the session. In our case we have two streams. The library will notify when achieving safe mode through the feedback path interface.

// Starting protocol for both streams
if( ztyp_status_ok != zrtp_start_stream(safe_connection->audio) )
{
    // ZRTP stream can't be started: see log file
}

if( ztyp_status_ok != zrtp_start_stream(safe_connection->video) )
{
    // ZRTP stream can't be started: see log file
}

The three steps above create the encrypted channel. After entering the "Secure" state, you provide a plain packet to the library and receive an encrypted packet ready to be sent. Decryption works in the analogous way.

zrtp_status_t status = zrtp_status_fail;
char packetp[MAX_RTP_SIZE];
int  size = 0;

// some abstract function for packets receiving
size = get_packet(packet);

 //
 // Processing incoming packets. 
 // You must determine media type and choose corresponding ZRTP stream
 //
status = zrtp_process_srtp(safe_connection->voice/video, packet, &size);
switch (status)
{
    case zrtp_status_forward:
    case zrtp_status_ok:
        //
        // Packet was successfully decrypted. Dont forget that packet
        // size was changed during decryption. New size now in size 
        //
    
    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 
         // media codec
    
    case zrtp_status_fail:
        //
        // This is some kind of error - see logs for more information
        //
}

3.4 Callbacks handling

libzrtp informs the user application about all changes in protocol state through a system of callback functions. The developer's guide considers this question in detail in 2.3 Callback conventions and 8 Stream usage and options. In most cases we need to display the SAS string and some other stream options after switching to the Secure state. An example of doing this is follow:

void zrtp_event_callback(zrtp_event_t event, zrtp_stream_ctx_t *ctx)
{
    test_options_t* info; // some user-defined stream options    

    switch (event)
    {
    case ZRTP_EVENT_IS_SECURE:
        // Get shared secrets creation time-stamp. Can be used for displaying at // GUI or for other purposes.      
        info->secure_since = ctx->_session_ctx->secrets._rs1->crated_at;
        
        // Get verification flag. MUST be displayed if application supports
        // secrets' cache. Use the zrtp_set_verified() function to change this
        // value.
        info->is_verified  = ctx->_session_ctx->secrets.verifieds & ZRTP_BIT_RS0;

        // Copy shot authentication strings.  As we used default stream options
        // the SAS type should be base256 and rendered as a 2-part string.
        memcpy(&info->sas1, &ctx->_session_ctx->sas_values.str1.buffer,
ctx->_session_ctx->sas_values.str1.length);
        memcpy(&info->sas2, &ctx->_session_ctx->sas_values.str2.buffer,
ctx->_session_ctx->sas_values.str2.length);

        // Obtain other stream options:
        // "Allow Clear" - to enable "go clear button" if our GUI has it;
        // "Disclose bit" - to alert user if remote peer has a shared stream key.       
        info->allowclear = ctx->allowclear; 
        info->disclose_bit  = ctx->peer_disclose_bit;

        break;
    }
    
    // ...
    // handle other events there

    default: 
       break;
   } // end events case
}

An overview for closing an secure channel using libzrtp:

3.5 Session(Streams) Closing

The uninstall session permits libzrtp to dispose of all engaged resources and release memory for session context storage. ZRTP streams will be also released, so you don't need to call separate functions.

zrtp_done_session_ctx(&safe_connection->zsession);

Library Uninstallation

When you no longer need the library, dispose of all resources allocated before the beginning of the operation.

zrtp_down(&zrtp_global);

4. Summary

Integration of libzrtp requires familiarity with the protocol and the library operation features. While the encryption of VoIP is not a trivial task, we have attempted to simplify as much as possible the work required to integrate libzrtp.
This file is part of the documentation for Zfone.
Copyright ©  2006-2008 Philip R. Zimmermann. All rights reserved.
Generated on Mon November 10 2008 by doxygen 1.5.7-20060202. Written by Viktor Krikun, © 2006-2008