libZRTP Developers Guide.

1. Introduction to LIBZRTP

The document structure: The first section provides an overview of libZRTP and some background on the ZRTP protocol as well as a description of the functions and data types that are used. The second section goes into more depth on all of the components in the library. The third section provides a detailed reference to the libZRTP API and its related functions. Note that the reference material was created automatically using the doxygen utility from comments embedded in some of the C header files. For clarity, the documentation is organized into modules, where these modules do not directly correspond to the files. An underlying cryptographic kernel provides much of the basic functionality of libZRTP, though it is primarily undocumented because it is self-contained.

1.1 License and Disclaimer

The libZRTP SDK is Copyright (c) 2006-2007 Philip Zimmermann. All rights reserved. See the libZRTP SDK evaluation license for licensing terms and disclaimers.

1.2 Specification

The full specification of the ZRTP protocol is defined in the "ZRTP Internet Draft".

1.3 Architecture

There are several components that make up the library. Depending on the type of application it can have different configurations. In addition to the modules actually implemented in the library, several external modules are also included. The libZRTP Architecture is shown in Figure 1.1.

zrtp_arch.png

Figure 1.1 libZRTP Architecture.

LibZRTP consists of two basic elements: the library itself and a set of application-specific helper functions. These comprise a set of frameworks for typical applications using libZRTP, where a developer has two options: use pure libZRTP and implement all necessary helper functions for a particular environment or base the development on the libZRTP helper functions that we provide. The supplied helper functions demonstrate how libZRTP is used in different environments.

System interface : taking into account the fact that LibZRTP was developed as a cross-platform library; could be used inside OS kernels and on different mobile platforms, several system-dependent functions were embedded into the library source code and the remaining ones were implemented as interfaces. The intention was to have as few system-dependent functions as possible, but they still exist. A full list of such functions with respective parameters will be provided in the following sections. All default implementations are already included in the Enterprise version of the library

Shared secrets cache: an important part of the encryption system requiring special care, managed by a set of interface functions for storing retained shared secrets and other additional information. The shared secrets cache is implemented as an abstract interface because there are many different ways to store this data in different applications, such as a simple binary file, in a windows registry, as database, etc. The Enterprise version of the library contains an example of the cache implementation as a binary file.

Setup and initialization module: a set of functions for configuring and initializing the library with five main parameters in LibZRTP: cipher type, authentication scheme, hash type, public keys exchange scheme, and length of the srtp authentication tag. The library provides a set of functions and interfaces for flexibility in configuring these parameters. If the developer does not want to use additional configuration options, s/he can initialize the library with the default settings. More detailed information about the library configuration is available in the corresponding section of "Developers Guide" 6. Setup, initialization and deinitialization

Crypto components management: libZRTP provides an interface for expanding the crypto capabilities by adding crypto components. Please note that this facility should not be abused. The built-in components are capable of providing the necessary functionality in 99% of all cases. Each built-in crypto component is logically based, balanced and fully tested, so use your own components judiciously.

Protocol: the library kernel implements ZRTP protocol logic. As soon as the kernel gets control of the RTP stream it begins exchanging protocol packets and informs the user about transitions from one state to another through a system of callbacks. When the key exchange is finished and a secure channel is set up, the RTP traffic gets encrypted. The library kernel also has functions for protecting the client codec against crashing in case of incorrect packet input. A more detailed description of the connection setup and methods of its management is provided in the 7 Connection life-cycle and management.

bnLib and Brian Gladman's AES Libzrtp uses AES ciphers for the encryption of RTP packets and bnlib for mathematical operations with large integers. Slight changes were introduced to support cross-platform and Win32 kernel-mode operation in bnlib and aes libraries. For this reason, libZRTP is distributed with compiled bnlib and Brian Gladman's aes libraries, where these components can be updated only by the libzrtp development team and will be upgraded in future libzrtp versions. Independent updating is not recommended.

1.4 libZRTP setup and building

The library distribution contains installation and configuration files, and project files for several operating systems. To install on Unix-like systems, use the autotools tool set. To install on Windows and Windows CE, Microsoft Visual Studio project are used. Project files for Symbian are available as well.

Configuration of libzrtp sysyem-dependent and protocol behavior options available through several configuration files: zrtp_config_xxx. h zrtp_config_user.h. The first one is a set of predefined for every platform options and adjustments. libzrtp includes default zrtp_config_XXX.h for Linux, OS X, Windows platforms and Symbian OS. In most cases the developer doesn't need to modify this file. The second file zrtp_config_user.h contains set of parameters for protocol related adjustments. See documentation in zrtp_config_user.h for more information.

Further instructions must be followed in order to build and set up the library in any Unix-like operation system (Linux, FreeBSD, MacOS):

  1. Download source codes from zfoneproject.com
  2. Decompress the archive libzrtp-0.7.X.tzr.gz : tar -zxf ./libzrtp-0.7.X.tzr.gz and open the libzrtp-0.7.X directory: cd libzrtp-0.7.X/projects/gnu
  3. Configure the library: ./configure (use necessary compilation flags)
  4. Build the library: make
  5. If you get any errors, please send a full log of the configuration and building process to libzrtp-support@zfoneproject.com Please specify the operating system, hardware platform, compiler version and other environmental parameters.
  6. After successfully building the library, run setup: ./make install
  7. To build test units, run ./configure and "make check"

For library configuration and installation on Windows platform project files from /projects directory can be used.

If you want to build libzrtp in Windows kernel mode you mast use MAKEFILE.WIN32, MAKEFILE.WIN64

Note for Symbian developers: The Libzrtp project supplies both a Symbian OS .mmp project file and a bldmake component description file, bld.inf. The bld.inf file simply specifies the name of the .mmp project file to be built. Using these files the library can be built from the Metrowerks CodeWarrior for Symbian Professional IDE, the Microsoft's Visual C++ IDE, or from the command-line. To build libzrtp itself use the bld.inf and libzrtp.mmp files from the root directory. To test libzrtp on Stmbian we have converted our test-unit from C to C++. Source files can be found in "libzrtp\test\Symbian" folder. This folder also contains the ./bld.inf and test.mmp project files.

By default the library and test-units are built with struct member alignment = 1Byte. If the application uses another value of structure alignment it may cause errors. To avoid problems use the same value of structure alignment while compiling the library that will be used in the application (most tests were implemented with the value of alignment equal to 1byte, so that value is preferable).

For components other than the library itself, tests are provided to determine whether they work on a given platform. The libzrtp tests runs test-vectors and test-cases for all crypto components, creates several parallel ZRTP sessions, initiates a transfer to the protected mode, and displays statistics. If the application test is successfully completed, then the library is configured correctly and all components work correctly.

Possible problems and how to solve them:

  1. It is possible that the automatic definition of architecture and byte-order will cause problems with the environment during the library build Before building libZRTP, we recommend a new program or hardware platform for the test-unit at the end of the file zrtp_system.h. If there is a mistake in the architecture or byte-order definition, use the manual configuration in zrtp_system.h following the comments.

If you are faced with some problems during configuration or installing of the library, send a report to the Support Service. If you installed the library on a platform not described here, please contact the Support Service. We are interested in knowing the results of the testing on new platforms. We will carefully examine all proposals and will do our best to implement them in the new library versions.

1.5 Structure

Figure 1.2 shows the structure of the library and how its main components work.

zrtp_struct.png

Figure 1.2 - libzrtp structure.

The library kernel consists of the srtp encryption module and the ZRTP protocol logic module. The details of the srtp functions are hidden from the user behind the libZRTP interface and are not directly accessible.As was stated earlier, the library uses several system-dependent functions to make it easy to migrate across platforms. All interfaces are developed as external functions and only after their implementation can you build and use libzrtp. (Most of these interfaces are already implemented)

Preferences: the second auxiliary block is a configuration module. It lists instructions for library initialization. LibZRTP also gives a set interfaces for integrating new components to make the library more functional, flexible, and multi-purpose. All functions of this block are further described in sections 6. Setup, initialization and deinitialization and Library crypto-components.

As soon as the system-dependent functions are implemented and all crypto-components are configured, the library is ready for use. The interface for connections and RTP stream management is basic and easy to use. It can be divided into three subgroups:

2. General overview

2.1 Source overview

Please pay attention to the concise description of the libzrtp folders structure;
[doc] - documentation and schemes files;
[include] - header files: [projects] [src] - libzrtp source files;
[test] - test suite for libZRTP kernel logic. Includes versions for Unix, Windows, Windows CE and Symbian.

[third_party]

2.2 Naming Conventions

The following naming conventions have been used for functions and variables:
  1. functions and global variables begin with the prefix "zrtp";
  2. the part of the name following the prefix shows the functional value or affiliation with the class;
  3. meaningful parts of the name are divided by the "_" symbol;
  4. uppercase letters are only used in macro names and define declarations;
  5. the names of the type declarations end with the symbols "_t";
  6. declarations of libzrtp internal data begin with prefix "_". Such functions and data types are for libzrtp developers only and shouldn't be used in user applications.

2.3 Callback conventions

Changes in the status of the protocol during an active connection are communicated to the application with a system of callback-functions. All the protocol logic for a single ZRTP stream is handled in a single thread. This limits timeouts when handling the callbacks. The majority of the functions are meant only to alert, but not process any data or wait for any action from the user. The most critical callbacks should be processed in separate threads. More details on call conditions and time limits can be found in each function description.

Most callbacks are initiated by the library in response to state-machine changes in that result from processing incoming ZRTP messages. While incoming ZRTP messages are being processed, the context data is protected from other asynchronous calls by a mutex. Therefore this mutex is blocked at callback initiation. This must be taken into account when system designing. Deadlocks or other recursive locks can be easily avoided by preventing other libzrtp calls from callbacks. In some systems the blocking time is limited, so the stream should not be deferred in the callback handler.

3. Data structures

In accordance with the new protocol, version libzrtp 0.3.1., the data structure is a bit more complicated. The protocol data has a tree-like structure, as shown in figure 1.3. On the top of the tree there is a library global context zrtp_global_ctx. It contains the list of the created sessions and some crypto-data. The global context was created to avoid the use of global variables in the c-files (some compilers, such as Symbian OS ed2, do not support this option.) In addition to using the multi-stream system, storing the list of sessions enables the synchronizing of the ZRTP protocols.

zrtp_datastruct.png

Figure 1.3 libzrtp data structure

Every session context contains all of the necessary information about the ZRTP session and the stream contexts. ZRTP Session contains profile defined crypto options and behavior of every stream within current session. Besides streams, session context contains SAS for the entire call. The number of streams is defined with the types::ZRTP_MAX_STREAMS_PER_SESSION variable. An array of streams is included in the static session context, where the streams can be used with the main::zrtp_attach_stream() function call. The stream context contains all possible information about each ZRTP stream. Since the ZRTP protocol is stream-oriented, it is the stream context which is the main operant for most of the libzrtp functions.

The context is a key parameter in the operation of all library functions. One should abide by the following rules when working with the context:

4. libZRTP interfaces

The library requires external implementation of some system-dependent functions to enable crossplatform operation. The libzrtp distribution contains almost all interface implementations for the following platforms: Windows, Linux, MacOS, Symbian, Windows CE. The Quick Start allows a fast integration of the library

Nevertheless, each platform has its own specific definition, and for maximum efficiency and convenience, its own set of functions should be implemented.This can be accomplished either by starting from the zero-based leaf or by optimizing the built-in implementation.

This section gives an overview of the component functions, and provides a description of their main algorithms. See the API description and the source code of the default components for further implementation details. ("./src/iface/ folfer").

4.1 Utility functions

The following functions belong to this group: Each group contains several trivial functions not demanding a detailed overview. As a rule each operating system has a set of such functions and the implementation of this part of the interface amounts to simple call mapping. Default functions can be found in "src/iface/zrtp_iface.c" and are included in the build using the DBUILD_WITH_CFUNC flag;

4.2 Shared Secrets' Cache

The shared secrets cache eliminates the need to verify SAS strings for every call. It is also used to store previous shared secrets to avoid errors if the connection is interrupted.

Implementation of the cache should not include any processing of the stored values. All business-logic is implemented in libZRTP. The cache should provide only enough storage for the necessary information. The user application should not call any of the cache functions directly.

The shared secrets' cache should be permanent and constantly updated using data storage with a relatively short response time.

Each application which uses libzrtp may have a different format for the shared secrets' cache. The only prerequisite is an interface with the minimum required number of parameters to allow the operation of libzrtp, such as:

  1. ZID1 - first client identifier and ZID2 - second client identifier: The cache is structured in such a way that addressing is done using one compound key (inc ZID, out ZID). This means that the pair of values RS1 and RS2 correspond to the pair of clients (ZIDs) and should not depend on the call direction. The storing/reading of the shared secrets, as well as modification of verification flags, is done after a pair of ZIDs is specified;
  2. Current RS value + verification flag and previous RS value + verification flag: These are the main cache elements used for stream key calculation. The library stores/reads these values using the corresponding calls;
  3. Last usage time stamp - Shared secret's cache last usage timestamp. The library updates this value before storing new value;
  4. Cache ttl - Cache expiration interval in seconds since last update time. The library doesn't remove expired values from the cache automaticaly, but expired cache values may be deleted manualy by developers application.

Remarks:
Because the library is enabled for use in a multi-threaded environment, all read/write operations within one session are synchronized. The user doesn't need to worry about synchronization: everything is already included into the library. Remember, that when a cache function is called the synchronization object is in a signaled state. Calling other libzrtp functions can possibly lead to recursive lockings and should be avoided. It should be taken into account that on some systems, lockings restrict access to some resources (e.g. In Windows kernel mode).
Since version 0.3.2, the library includes a default implementation of the secrets' cache as a simple binary file. This implementation can be used as a "quick start" or replaced with something more intelligent. For additional information see "src/iface/zrtp_cache.c". To build default cache use DBUILD_DEFAULT_CACHE compilation flag.

4.3 Alerts

Audio alerts are used by the library to warn clients about significant changes in the protocol status. Most alerts are duplicated with zrtp_event_callback() calls, but unlike events, alerts are to be processed in no fail. Processing is understood as playing a definite audio file through the system API or through the RTP stream.

Audio alerts are of major importance on systems without graphical interfaces. For instance, alerting a transition from protected mode to non-protected mode is the only way to notify the responder ZRTP endpoint about the interruption in traffic encryption. The events to be alerted are:

While the sound is being played, the library blocks outgoing RTP traffic in order to avoid "echo" effects if the clients use loudspeakers for sound output and multidirectional microphones for sound input devices. The traffic blocking technique is very simple: Before the function is called, the flag of the relevant alert is set in the context zrtp_conn_ctx_t::need_play_alert. As soon as the recording has finished playing, the function drops this flag and libzrtp unblocks the outgoing traffic.

Warning:
Don't forget to ublock outgoing stream after playing an alert. (set zrtp_conn_ctx::need_play_alert to zrtp_play_no)

4.4 Packet retries

The module of timed-out packet sending is necessary for resending undelivered protocol packets. The delay between each resending of the same packet increases in a geometrical progression until the threshold value is reached.

The packet resending scheme is as follows: libzrtp constructs a packet, to be sent to the remote party. The constructed packet is cached and sent to the network through the zrtp_send_rtp () function. If the packet is lost it must be resent after the time period. Therefore libzrtp calculates the time interval and enqueues the packet for resending zrtp_send_packet_later(). When the required interval has passed the user's process calls a handler function (zrtp_retry_task::callback) and the packet is resent, a new interval is calculated and the task is enqueued for resending again. When the ZRTP remote party's response is received, the zrtp_cancel_send_packet_later() call is initiated and resending is overridden.

The "Send packet later" module should function in the following way: each ZRTP stream should be provided with a tasks pool. The send task is created by calling zrtp_send_packet_later(). If the pool already contains the adding task for that stream it should be replaced with the current one. If don't - new task should be added to the pool. When the determined time-out is reached, the user's stream makes a callback and removes the task from the pool. The call of the cancel_send_packet_later() removes the task from the communications queue. If cancel_send_packet_later() called with NULL tasks - all tasks should be removed from the pool. Thus all logic is integrated to the library. The only thing the module does is make the callbacks after a defined time period.

Special features of the implementation: as the library is designed to be able to work in multi-threaded environments, special attention should be paid to synchronization. Each callback called by the "Send packet later" module is implemented in parallel to the main stream. The synchronization of this libzrtp operation is provided by the zrtp_stream_protector mutex. Cancel/add task synchronization is implemented on the libzrtp kernel level as well. The only thing the user should be careful about when implementing the "Send packet later" module is the synchronization of operations with the tasks list for several contexts. The callback's recursive nature should be taken into account (a callback may initiate the adding of a new task or canceling of an existing one).

The library is comes with standard default "Send packet later" module implementation. To install the library with the default "Send packet later" module implementation use the flag DBUILD_DEFAULT_TIMER. See implementation details in "./src/iface/zrtp_scheduler.c" or "src/iface/symbian/ZrtpIScheduler.cpp" for Symbian. To use the default scheduler on Symbian - just add C++ class to your project.

5 Random number generation

The generation of cryptographic key material is a highly sensitive process. To do this, you need high entropy random numbers that an attacker cannot predict. This section discusses the random number generator used by libzrtp, and how suitable entropy can be collected on different hardware platforms.

Failure to use true entropy from the physical environment as a basis for generating random cryptographic key material would lead to a disastrous loss of security.

5.1 Deterministic Random Bit Generator

libzrtp uses a cryptographically strong Deterministic Random Bit Generator (DRBG), based on running the AES-256 block cipher in counter mode. The output of this DRBG is used for key material by libzrtp for the Diffie-Hellman private keys, and other random protocol components such as nonces. The 256-bit AES key and 128-bit initialization vector for the DRBG are drawn from an entropy pool created by a SHA-512 hash of raw entropy sources. These raw entropy sources are highly platform dependent and thus are not included in libzrtp. The library provides only a set of interfaces for adding the entropy to the entropy pool. We will discuss the entropy collection in the next section.

When a random number is required by the ZRTP protocol, the library kernel calls the Deterministic Random Bit Generator interface function zrtp_randstr(). That function requires the existance of an entropy pool that has already been seeded with sufficient entropy. This entropy pool must be seeded by calling zrtp_add_entropy().

The zrtp_add_entropy() function takes a buffer of raw unprocessed entropy provided by the caller and adds it to the entropy pool via the SHA-512 hash function. But the zrtp_add_entropy() function also adds in some other entropy from the operating system by calling the zrtp_add_system_state(), which is discussed in the next section.

See also:
zrtp_add_entropy() description.

zrtp_add_system_state() description.

5.2 Entropy accumulation

Random numbers for cryptographic key material must be derived from a physical entropy source, such as RF noise, acoustic noise, thermal noise, high resolution timings of environmental events, or other unpredictable physical sources of entropy. For a detailed explanation of cryptographic grade random numbers and guidance for collecting suitable entropy, see RFC 4086 and Chapter 10 of "Practical Cryptography" by Ferguson and Schneier. The raw entropy must be distilled and processed through a Deterministic Random Bit Generator (DRBG). We supply a suitable DRBG in libzrtp, which is accessed through the zrtp_randstr() function.

To add entropy to the entropy pool maintained by the libzrtp random number generator, the application calls the zrtp_add_entropy() function. This entropy accumulation function may be called whenever new entropy becomes available.

The entropy pool builds up more precious entropy each time you call zrtp_add_entropy(). Once in a while, it is a good idea to save the entropy in nonvolatile storage, by calling zrtp_randstr() and writing the output to a file, or to flash memory, or to some nonvolatile system storage area. This can be done whenever the VoIP application shuts down, or perhaps at the end of each secure VoIP call. A minimum of 512 bits (64 bytes) of output from zrtp_randstr() should be stored this way, but there is no need to store more than 256 bytes. When the VoIP application starts back up again, the contents of this nonvolatile entropy file should be added back into the active entropy pool by passing it to the zrtp_add_entropy() function.

The zrtp_add_entropy() function augments the application-supplied entropy by adding in some entropy from the operating system, by calling the zrtp_add_system_state() function. The application may call the zrtp_add_entropy() function, not the zrtp_add_system_state() function, which is called only by zrtp_add_entropy(). However, the developer may have to implement the zrtp_add_system_state() function for his own platform if the default implementation is not sufficient for that platform.

The SDK library provides a default implementation of zrtp_add_system_state() for Windows Kernel and Unix based platforms. For the Windows kernel mode zrtp_add_system_state() gathers current system state information as an entropy source. Among them are the performance counter, the current value of the system interrupt-time count, the count of the interval timer interrupts, and the values of some CPU registers. For Unix platforms, zrtp_add_system_state() calls /dev/urandom.

On a desktop or laptop PC running Linux, FreeBSD, NetBSD, or OpenBSD, a good source of entropy may be found by reading from /dev/random or /dev/urandom. This is because /dev/random is seeded by entropy from keyboard timings, mouse movements, disk latency measurements, or other physical noise sources, some of them involving unpredictable human interaction. However, some low cost embedded Linux systems have no keyboard, no mouse or trackpad, no disk drive, and are starving for high quality entropy. There are some low cost Asterisk PBX boxes that are built this way. Or hardware Analog Telephone Adapters. Or low cost consumer routers. Many of them have no /dev/random implemented, or worse, have only a stub for /dev/random that does not actually collect any environmental entropy. This creates a dangerous illusion that entropy is available, because /dev/urandom appears to work, but is not backed by true entropy. This is bad, and not only for ZRTP. Platforms like these might not be able to generate strong cryptographic key material for SSH or SSL.

If you are an OEM that builds hardware like this, and you wish to implement the ZRTP protocol with our libzrtp SDK, you really should provide a properly implemented /dev/random and /dev/urandom, properly supplied with true environmental entropy. If you are building a telephone, you can easily collect entropy from raw audio samples from the microphone. If the phone includes a video camera, you can collect entropy by sampling a few raw uncompressed video frames. If it's a mobile phone or a cordless phone, you can collect entropy from the RF noise in your wireless circuitry. If it's an embedded box like a router or low cost PBX, you can do high resolution timings of packet arrivals and use the timer readings as entropy sources. A PBX might include an analog interface to PSTN phone lines, and those interfaces usually include registers that measure analog voltage levels, which can serve as a source of entropy. The entropy sources do not need to produce much entropy, just a few bits at a time, but it can build up slowly until you have accumulated a few hundred bits of entropy. That's enough to generate cryptographically useful keys. Even if it takes some seconds or even minutes to accumulate this much entropy the first time your product is activated, it can be stored in nonvolatile storage so that it will be ready to reseed the entropy pool instantly the next time your product is powered up.

In the ideal case, if you are designing the embedded hardware yourself, you could provide a good source of entropy by including a simple ring oscillator in the hardware. A ring oscillator is a circular chain (a ring) of NOT gates, and has nothing whatsoever to do with a telephone ring generator. The oscillation frequency drifts from thermal noise, and sampling the output at some low sampling rate is a good way to get some entropy. However, most designers have to work with existing hardware designs, and don't have the luxury of adding special hardware to generate entropy, which means you have to improvise with whatever you can collect from the environment, using any of the methods described above.

If the library is used on another platform, the potential entropy sources should be thoroughly analyzed and a custom implementation of zrtp_add_system_state() must be developed for that platform. You can get your entropy collection ideas by looking at the default implementation of zrtp_add_system_state() provided in zrtp_rng.c. Again, microphone noise can be a good entropy source for VoIP clients. Raw, uncompressed, unfiltered audio samples should be used.

If you have entropy gathering schemes for platforms not already supported in libzrtp, or if you doubt the correctness of your entropy collection approach, contact us to discuss how it may be done. We will do our best to provide you with technical assistance.

See also:
zrtp_add_entropy() description.

zrtp_add_system_state() description.

6. Setup, initialization and deinitialization

The first step of libzrtp usage is its initialization and configuration. The chosen settings determine libzrtp behavior and options for the established ZRTP connection. This section describes the list of available settings and peculiarities of their use.

ZRTP is a stream-oriented protocol. The session concept has been included merely to provide an analogy to the SIP connection. All library functions operate only with streams. Each stream can be supplied with its own settings and is processed by the library with respect to them.

6.1 Library initialization

The library should be initialized by a zrtp_init() call before any other ibzrtp library function call. This libzrtp function call initializes global data and synchronization objects and uploads crypto-modules. Only after zrtp_init() successfully completes the session creation, can ZRTP streams be added and started. At the end of the library use, the resources allocated during initialization should be released with the zrtp_down() call.
Warning:
:

6.2 Session configuration

Since libzrtp v0.6.1 ZRTP profile is applied to the entire session instead of every stream. On zrtp_attach_stream() new stream will be configured according to the session profile. All stream parameters can be divided in two groups: "behavior settings" and "crypto-settings". The first group is responsible for the stream's "behavior" and the other one determines secure stream cryptographic parameters. The stream is configured using a "profile", a special structure of type zrtp_profile_t which is configured in advance. A profile is applied to the ZRTP stream at it's creation (zrtp_attach_stream()) and is valid during the entire stream operation cycle. Indeed, it is not possible to change stream parameters once they are created, but at the same time each newly created stream can have its own set of options.

"Behavior" settings are represented by boolean flags with "true" and "false" values. They include:

  1. "active" (zrtp_profile_t::active) - indicates secure channel initialization mode. The passive streams are not able to initiate a secure channel, but they are able to receive initiation queries from active streams.
    Note:
    This operation is included to separate commercial and individual users and may by substituted with something more intelligent later.
  2. "staysecure" (zrtp_profile_t::staysecure) - prohibits moving to non-protected mode. If this option is on, the libzrtp ignores all attempts to transition to non-protected mode, both from the local user's side using zrtp_clear_stream()) and from the side of the remote user (receiving GoClear).
  3. "autosecure" (zrtp_profile_t::autosecure) automatically activates transition to the secure mode right after the "discovery phase", omitting any Clear state. I.e., if this operation is on, the zrtp_start_stream() moves the stream to the protected mode. If it is off, the stream moves to the "Clear" state and waits for manual activation by a zrtp_secure_stream() call.
  4. "cache_ttl" (zrtp_profile_t::cache_ttl) defines the operation-life time of retained shared secrets in the cache.

"Crypto-settings": the protocol defines 5 crypto-component types used in calculations:

  1. cipher type is a the type of block cipher used for encryption of RTP traffic and some ZRTP packets. At the moment the following ciphers are available: AES block cipher AES128 and AES256 with a 128-bit and 256-bit key size correspondingly;
  2. pk scheme is the public key exchange scheme used in establishing the secure channel. The available modes are DH3K, DH4K (deprecated), EC25, EC38, EC52, and a Preshared mode using the retained shared secrets from a previous call.
  3. hash type hash type is the type of hash function used in libzrtp. At this time only the SHA256 algorithm is supported for the ZRTP protocol packets. Note that this is not to be confused with the SHA1 hash used by the SRTP media authentication tags.
  4. SRTP authentication scheme used by libZRTP in traffic encryption. The ZRTP Internet draft defines HS32 and HS80 values;
  5. SAS scheme defines the information provision scheme for verifying retained shared secrets. Since v0.3.5 libzrtp supports two SAS types: base 32 and base 256.

The "crypto-settings" configuration contributes to the choice of each component type by providing a priority ranking. It serves a a request, in that the component chosen for the first priority is not necessarily used in the connection installation. Rather, the resulting component type depends also on the opposite side's settings. The component choosing mechanism is as follows: both sides communicated their supported components during the "discovery phase". After that the initiator chooses the optimal intersection of components. In this way it is possible to set condition such as: "use component type A; if A is not supported by the other side, then use B; never use C".

If, when the discovery phase is finished, no common components of at least one type have been discovered, the connection installation will be aborted.

For components identification the numerical values of the following types are used: zrtp_hash_id_t, zrtp_cipher_id_t, zrtp_atl_id_t, and zrtp_sas_id_t, which are defined in zrtp_crypto.h. The profile field responsible for components of a particular type setting is an integer-valued array where component identifiers should be placed in order of priority. 0-element is of the first priority. The list should end with ZRTP_COMP_UNKN=0. Return to the previous example for configuration of the public data exchange scheme: zrtp_profile_t::pk_schemes={ZRTP_PKTYPE_DH4096, ZRTP_PKTYPE_DH3072, 0,...0} means that the library will try to set the connection in DH4K mode or DH3K mode if the first is not supported. Attempts to use "Preshared" will be discarded.

As described above the profile is used in the zrtp_init_session_ctx(). To simplify the library use, the default configuration is available. The default profile contains the set of components defined by the ZRTP protocol standard. For detailed information see the description of zrtp_init_session_ctx(). The following functions of the protocol usage are available as well:

Note:
Remember, to simplify the library usage it is always possible to use the default profile by just passing NULL to zrtp_init_session_ctx() and the library will do the rest.
Warning:
When designing an application, do not allow the user to configure his own profile if it omits compulsory components. Of course, the profile correctness is checked when it used, but nevertheless it breaks clarity and may cause errors.

6.3 How to develop original components

A detailed description of original component creation coming soon.

zrtp_comp.png

Figure 1.4 libzrtp crypto-components structure

7 Connection life-cycle and management

This section gives definitions for the ZRTP session and stream, and also describes the main operations they are used in. As iterated above, ZRTP is a stream-oriented protocol. The session concept is included merely to draw an analogy to SIP connections. Logically , a session is a channel between two ZRTP endpoints in the frames of which several streams can be opened. Physically, a session is a collection of ZRTP streams, profile for their configuration, synchronization objects for managing these streams and Short Authentication String. Logically, ZRTP session is established when at least one Full Public Key exchange is performed. A ZRTP stream contains all the cryptographic data and other information for the connection installation. Most libzrtp functions operate with streams.

7.1 Operations with ZRTP sessions and streams

The main ZRTP sessions and streams management operations are determined in the \"zrtp.h"\ file. For better clarity we use the simplified ZRTP protocol state diagram shown at figure 1.5.

zrtp_state.png

Figure 1.5 libzrtp state-machine diagram

session creation ( zrtp_init_session_ctx() ). This call connects the session with the side's ZRTP identifier and prepares it for further use. The creation and start of ZRTP streams are possible only after this function call. After initialization the session is placed in the list of sessions, which is stored in zrtp_global_ctx. The maximum number of sessions is limited only by the operating memory size. It is also possible to create several sessions between two ZRTP endpoints. Such sessions will be interpreted by the scheduler as a single one with many streams, since the synchronization of the stream work is realized not in the frames of a session but between two ZRTP endpoints. After this session's stream has completed, the resources allocated in initialization should be released by the call zrtp_done_session_ctx().

session deletion ( zrtp_done_session_ctx() ). This call removes a session from the list of active sessions and releases all allocated resources. Deinitialization of the session leads to deinitialization of all attached streams and further use of these streams will have unpredictable results.

stream adding ( zrtp_attach_stream() ). This function separates the ZRTP stream in the frames of the session indicated and prepares it for further use. When a stream is created it is bound to the appropriate RTP media by SSRC and this SSRC value can be used for traffic assignment between ZRTP streams. The stream is ready for use and transition to the protected mode immediately after creation.

stream removing ( zrtp_free_stream() ). This function stops the ZRTP protocol for this stream. It releases all allocated resources and marks the stream structure as ready for reuse.
Note:
As the stream removing function also initiates its stop, there is no need to first call zrtp_stop_stream().
stream starting ( zrtp_start_stream() ). This function initiates a protected channel set up and further traffic encryption. After the call zrtp_start_stream() libzrtp starts a protocol packet exchange with the remote side, chooses optimal channel parameters and starts encryption. Both protocol packet exchange and encryption itself are implemented by calling zrtp_process_xxx() functions. So, after calling zrtp_start_stream() you just pass all incoming and outgoing packets in the frames of this stream for processing to zrtp_process_xx() and the library will implement all appropriate actions. The library informs the user's application about the protocol exchange through the zrtp_event_callback(). Packet processing is described in more detail in the next section.

stream stopping ( zrtp_stop_stream() ). This function will stop the protocol at any stage: all delayed tasks are canceled, and the protocol packet exchange and encryption is stopped. After this function call it is necessary to stop traffic processing using the zrtp_process_xxx() function. When the stream is stopped the remote side is not informed in any way about the encryption break (don't confuse with the transition to non-protected mode) so the stream should be stopped right before closing the communication channel (RTP steam/whole SIP session).

7.2 Protocol scenarios

7.2.1 Streams scenarios

Having described stream operations, here are some typical uses. For deep understanding we use the extended ZRTP protocol state diagrams shown at figures 1.6 and 1.7

zrtp_stateext_main.png

Figure 1.6 libzrtp extended

state-machine diagram

zrtp_stateext_clear.png

Figure 1.7 libzrtp extended

state-machine diagram

Start-Clear. After the protocol starts, both sides start exchanging Hello packets and the ZRTP sides exchange ZIDs and lists of supported crypto-components. If one side uses the "autosecure" option, then right after exchanging Hello packets, transition to the protected mode (clear-secure) will be initiated. Otherwise the state-machine moves to the Clear state and informs the user's application about that through a zrtp_event_callback() of type ZRTP_EVENT_IS_CLEAR. The transition to the protected mode can be initiated manually by calling zrtp_secure_stream()

Clear-Secure. Regardless of which initiation typeis used, autosecure or zrtp_start_stream(), one side becomes the "initiator" and moves to the "Initiating Secure" state (ZRTP_EVENT_IS_INITIATINGSECURE event). The other side , having received a Commit packet becomes rthe esponder (ZRTP_EVENT_IS_PENDINGSECURE). The Commit contains the crypto-components list, chosen by the initiator for use at connection initiation and for further traffic encryption. Further along the stream, the keys calculation is performed using the Diffie-Hellman algorithm and then confirmed. After the keys calculation and the srtp encryption ibzrtp moves to the Secure state (ZRTP_EVENT_IS_SECURE) and is ready for traffic encryption. Having moved to Secure mode the user gets access to all information about the connection parameter settings and all generated keys for verifying data: SAS values. 8.2 SAS values and secrets options describes how these values are obtained and used.

Note:
Remember, the zrtp_secure_stream() just initiates the start of transition to the protected mode. There may be a noticeable period between the transition to protected mode and the start of traffic encryption. This period depends on the physical state of the communications channel, the performance of the remote ZRTP endpoint device, etc.
Warning:
all outgoing RTP traffic is blocked whiole moving to the Clear-Secure state to prevent data transfer through the non-protected channel.
Secure-Start. The ZRTP protocol does not provide options for closing an already established channel. This is the task of the SIP protocol or other connection protocol. Having agreed to close the RTP channel (using SIP for instance), the ZRTP side can just stop encryption using the zrtp_stop_stream() call. After stopping the protocol the resources and steams/sessions should be released with the zrtp_free_stream()/zrtp_done_session_ctx() call.
Note:
stop/start stream cycle is possible. I.e., if a ZRTP channel with the same initialization parameters is necessary again, it is possible to use zrtp_start_stream() once more. However, this scheme is not often used in real applications.
Clear-Secure-Clear... After Hello packets having been exchanged and the connection with the ZRTP remote point has been established (Clear state), the cyclic transition to the protected mode and back becomes possible. This may be useful for readdressing calls, and stopping device loading when transferring non-confidential data, etc. I.e., encryption can be stopped and renewed at any time without destroying and reinitializing the stream context. In this case the zrtp_clear_stream()/zrtp_secure_stream() function should be used.

Secure-Clear. Transition to the non-protected mode: let side A initiate the transition by means of zrtp_clear_stream() call. This function call initiates the sending of GoClear packets. Side B, having received a GoClear packet, moves to "Pending Clear" (ZRTP_EVENT_IS_PENDINGCLEAR) and informs the user about the remote side's intention to break the protected communication session. In the Pending Clear state, the responder's libzrtp blocks outgoing RTP traffic until the transition to the non-protected mode is confirmed by a zrtp_clear_stream() call. After that, side B moves to the Clear state, renews the RTP media and notifies side A that it accepts a protected connection break.

Note:
Because completing the move to the Secure Clear mode requires that the initiator receive the ClearACK packet, traffic presence is required (it is not possible to move to Clear after closing the media channel).
Warning:
: After ZRTP_EVENT_IS_PENDINGCLEAR is received the application has to notify the user of the encryption break to prevent secret data transfer through the non-protected channel;

7.2.2 Session scenarios

Since libzrtp v0.6.1, the concept of the ZRTP Session has been extended. The ZRTP session is established by establishing at least one DH stream and computing the ZRTP Session key. When the session key is computed the ZRTP session can be extended with more streams using Multistream exchanges.

Here are the basic algorithms of the ZRTP Session life-cycle. For single stream peer sessions the algorithm looks like this:

When the user wants to attach another stream to an already existing ZRTP session he calls zrtp_attach_stream() and starts it with zrtp_start_stream(). zrtp_start_stream() checks whether the ZRTP session is already established between two endpoints. If so, the stream goes into the Secure state using Multistream mode, eliminating a full DH exchange.

It is possible to start several streams within a single session simultaneously. Imagine that we have a ZRTP session with two streams: Audio and Video. In order to go secure we start both of them: zrtp_start_stream(Audio) and zrtp_start_stream(Video). To handle this case correctly libzrtp starts one stream in Full, DH mode and keeps the other one. When the first, Full stream has switched to the Secure state and the Session key is computed, the second one will be resumed in Multistream mode.

To switch to Clear and then go Secure again, use zrtp_clear_stream() and zrtp_secure_stream(). The library will destroy the Session key and repeat the algorithm outlined above.

7.3 RTP/ZRTP packets processing

As detailed above, after the protocol has started, all outgoing and incoming RTP/RTCP packets should be processed using zrtp_process_xx() functions. Interfaces for processing ZRTP protocol packets and for ZRTP media traffic encryption are identical. You just transmit the packet to the library and perform the instructions as indicated. These functions are:

a) zrtp_process_rtp/rtcp(): processes the outgoing RTP/RTCP media traffic. In accordance with the state of the protocol state-machine, the following activitions are performed:

b) zrtp_process_srtp/srtcp(): processes incoming traffic. Operations performed depend on the packet type and the state of the protocol state-machine: See the specifications for other features of these functions.

7.4. Error handling

During transitions to the protected mode and back, different unforeseen situations and errors may occur. The response to an error is always the same and doesn't depend on the error type; it is a transition to the non-protected mode and a notification to the user. If the transition to non-protected mode is urgent, the remote side is notified about the reason for the such transition.

To conclude, if the error has occurred on the local side the library makes a zrtp_event_callback() of the ZRTP_EVENT_ERROR event type. The error code is contained in the zrtp_stream_ctx_t::last_error context variable and may be analyzed by the user's application. The list of possible errors is defined in the zrtp_protocol_error_t definition. If the error has occurred on the remote side, the transition to Clear mode will be initiated and the zrtp_stream_ctx_t::last_error will contain the code of the remote error.

The protocol and the library are developed such that when an error occurs there is no threat of secret information disclosure. The error handlers are necessary for debugging and provide detailed information about the reason for the error.

The list of errors and a detailed description of the terms of their occurrence can be found at 7.4. Error handling zrtp_error.h.

7.5 State-machine implementation

For developers and experienced users - coming soon.

8 Stream usage and options

This section describes the main ZRTP stream parameters: the state of their activity, the access conditions and the aims of their use in the application.

The library is designed such that the user has access to all context data at all stages of establishing a connection. While this adds flexibility to the designed application, it does demand extra attention when working with context fields. No context field should be changed by the user's application directly. Only public fields and methods which do not begin with the "_" character are accessible for reading.

As the libzrtp works in a multi-threaded environment, the call for context fields should be performed in definite time intervals, otherwise the wrong value could be read or non-existing structure could be addressed. As a rule, necessary information from the context structure should be read with the zrtp_event_callback() call.

The following describes the main data available to users and recommendations for its use.

8.1 Protocol State

The zrtp_event_callback() is used to show the connection establishing/closing process. There is a zrtp_event_t type corresponding to each significant state change . The type documentation contains additional information on each state. Transition processes can be shown as pictograms.

The protocol state-machine stores its current state in the zrtp_stream_ctx::_state context variable. The advanced user can use this variable for stream state analyses. The previous state is stored in zrtp_stream_ctx::_prev_state. Access to these variables is allowed in all zrtp_event_callback() and zrtp_packet_callback() callback types.

8.2 SAS values and secrets options

The key concept of ZRTP protocol use and its libzrtp implementation is the Shot Authentication String. The SAS is a particular binary sequence used to authenticate the remote side's key.

It works as follows: The SAS is calculated for each of the two sides separately on the basis of the session key. A symbol/string SAS value is displayed to each user. Then both users have to tell each other their SAS's orally. If they are identical it guarantees the key's authenticity and absence of a MitM. When both users have been assured of the key's authenticity, they can verify the remote side's key. In the ZRTP protocol each new stream key is calculated on the basis of the previous one, so it is sufficient to perform the verification procedure only once (at the first call). If the first call was MitM free, the next one will also be MitM free. If it is not, the protocol engine will detect it. The Verification Flag is stored in the cache and is restored automatically at the next call.

Implementation: after the transition into protected mode (ZRTP_EVENT_IS_SECURE) the developer gets access to the SAS and flag values. Various SAS forms are stored in zrtp_conn_ctx_t::sas_values, and flag values are accessible in the zrtp_conn_ctx_t::secrets structure. The GUI of the designed application has to display the SAS in at least one of the forms (sas_values::str1 and/or sas_values::str2) and the current common verification flag value (verifieds & ZRTP_BIT_RS0). If the user wants to change the verification flag (check/uncheck), the zrtp_cache_set_verified() function should be used.

8.3 Peer Information

After the protected connection is installed (ZRTP_EVENT_IS_SECURE) some information about the remote side becomes available.

8.4 Crypto options

After the discovering phase is completed and the transition to the protected mode, the list of crypto-components used during the life of the stream becomes visible. This list is formed based on the preferences of both sides. The variables zrtp_conn_ctx::_blockcipher, zrtp_conn_ctx::_hash, zrtp_conn_ctx::_authtaglength, zrtp_conn_ctx::_pubkeyscheme, and zrtp_conn_ctx::_sasscheme contain the chosen components. Each component structure has an identifier id and symbol type name. The component list information can be displayed as a set of connection statistics.

9 SRTP crypto engine

9.1. Introduction

libzrtp is an extension of the the SRTP encryption protocol. The library itself implements secret key exchange, and encryption is performed by the SRTP engine. The library contains a programming interface for encrypting RTP traffic. It can be used as an extended implementation of SRTP, or in conjunction with any other implementation of SRTP.

RTP packets can be encrypted and authenticated (using the zrtp_srtp_protect() function), turning them into SRTP packets. Similarly, SRTP packets can be decrypted and have their authentication verified (using the zrtp_srtp_unprotect() function), turning them into RTP packets. To work with RTCP packets use zrtp_srtp_protect_rtcp() and zrtp_srtp_unprotect_rtcp() analogously.

In the implementation of the SRTP interface, the following datatypes are used:

The SRTP context defines two streams, incoming and outgoing. When a stream is initialized, its structure is defined by the srtp profile type, zrtp_srtp_profile_t. The profile specifies the crypto policies for RTP and RTCP, and the values of SSRC, the master key and the master salt.

A crypto policy is defined by the zrtp_srtp_policy_t type. It specifies the following packet processing options:

9.2 Interface usage

This section provides a simple example of how to use the SRTP engine. The example code lacks error checking, but is functional.

Here we assume that the ssrc value is already set to describe the SSRC of the stream that we are sending, and that the functions zrtp_get_rtp_packet() and zrtp_send_srtp_packet() are available to us. The former puts an RTP packet into the buffer. The latter sends the RTP packet in the buffer.

    zrtp_srtp_profile_t inc_profile, out_profile;
    if(zrtp_status_ok != zrtp_srtp_init(zrtp_global, zrtp_srtp_global))
    {
        //handle error code
    }
        
    //initialize profiles
    //
    // TODO: code for profiles init
    //

    //allocate and initialize SRTP session context
    zrtp_srtp_ctx *srtp_ctx = zrtp_srtp_create(zrtp_srtp_global, &inc_profile,
&out_profile);
    if(NULL == srtp_ctx)
    {
        //handle error code
    }
    
    //main loop: get rtp packets, send srtp packets
    while(1)
    {
        zrtp_rtp_info_t packet;
        
        zrtp_get_rtp_packet(&packet);
        
        if(zrtp_status_ok != zrtp_srtp_protect(zrtp_srtp_global, srtp_ctx,
&packet)){
            //handle error code
        }
        
        zrtp_send_srtp_packet(&packet);
    }

9.3. Built-in SRTP implementation

libzrtp uses is own SRTP encryption engine for several reasons:

The implementation of the SRTP algorithm was modelled according to RFC 3711 and tested thoroughly for compatability with David McGrew's implementation.

9.3.1. The replay protection engine

The libzrtp implementation of SRTP includes a replay protection mechanism, which prevents the repeated processing of packets that are received more than once.

The implementation of the replay protection mechanism is based on a sliding window defined by the zrtp_srtp_rp_t type. It consists of a bit string, where each bit corresponds to a packet sequence number. The position of the corresponding bit in the window is determined by the value of zrtp_srtp_rp_t::seq and the sequence number.

                         +-----------------------+
-------------------------|                       |-----------------
        A                |           B           |        C
-------------------------|                       |-----------------
                         +-----------------------+
                      <---window moving direction---
:

The position of a packet relative to the sliding window is determined during its processing by the replay protection mechanism, and depending on which interval that result falls in, the following actions occur:

The length of the window is defined by the constant ZRTP_SRTP_WINDOW_WIDTH in number of bits.

Consider the following tree:

                 SSRC
                 /  \
                /    \
               /      \
       in stream  out stream
           /  \       /  \
          /    \     /    \
         RTP  RTCP  RTP  RTCP

The uniqueness of the sequence number of a packet is preserved only within a leaf of this tree.

Two lists of nodes (zrtp_rp_node_t), are maintained by the library. One of these contains replay protection nodes for incoming packets, and the other for outgoing ones. The list is searched by SSRC value.

9.4. Different SRTP Implementations

libzrtp can be integrated with any alternative implementation of SRTP. This choice is activated by setting the ZRTP_USE_EXTERN_SRTP flag. Otherwise, the builtin implementation will be used.

Additional steps to use an alternative implementation of SRTP:

  1. Define the type zrtp_srtp_global_t, described in the global context of the SRTP implementation. Every SRTP function interface must reflect this change. If no global context is necessary, the type must be defined as void.
  2. Define the type zrtp_srtp_ctx, described in the SRTP context. This context must be reflected in the incoming and outgoing profiles zrtp_srtp_profile_t;
  3. Implement the programming interface consisting of the following functions:

This interface connects libzrtp and libsrtp.

Warning:
After integrating an external SRTP implementation, it is essential that the test suite Guide to the libZRTP Test Suite is performed to verify that it works correctly when integrated.

10 Multi-threading and concurrent streams

Adding multi-threading to the architecture has added complexity to the protocol. Accordingly, the library data structure has become more complicated, requiring the synchronization of share resources. In this section we will cover all of the shared resources, their protection issues, and questions of their implementation.

As described in the 3. Data structures, the library data structure may be compared with a tree, where the global context zrtp_global_ctx is at the root. The following items are among the shared data of this structure:

  1. zrtp_global_ctx::rand_ctx is a hash context for gathering entropy. Several streams may require access to the random number generator simultaneously. The mutex _rng_protector is used for context protection;
  2. zrtp_global_ctx::_sessions_head is a list of created ZRTP sessions. All operations with the session list (creation, removing, search) are also synchronized using zrtp_global_ctx::_sessions_protector.

The next structure in the hierarchy, zrtp_conn_ctx_t sessions context, is shared among all of its streams. By convention, they may be divided into two groups:
ZRTP sessions protocol data. The protocol data is encapsulated by a great number of fields: the flags set, the set of shared secrets, the SAS strings, DH related data, etc. All of this data is modified by the DH streams, and PRESHARED streams are used for reading only. The presence of only one DH stream between two ZRTP endpoints at the same time is required by the protocol. This implementation helps protect all session data automatically as follows:during the user's DH stream initialization attempt, the presence of other activated DH streams is checked across all sessions between the ZRTP endpoints. If such a stream is detected, the initialization attempt is delayed until the end of the current DH stream. If the DH stream is initialized by the remote ZRTP side and the DH stream that is processing the initialization query was declined, the active DH streams search process uses the mutex _dh_mult_protector for synchronization;

Streams list _streams. Operations with streams are protected with the item zrtp_conn_ctx_t::_streams_protector.

Lastly, the stream contexts are represented by the tree leaves. All operations of the upper level structures have been synchronized already. For encryption/decryption the context data is not used. Libzrtp synchronizes only the protocol packet processing: zrtp_process_srtp() takes protocol packets and zrtp_secure_stream()/zrtp_clear_stream() is synchronized. For instance, if the main processing stream calls zrtp_process_srtp() for each incoming packet and at same time the control stream calls zrtp_secure_stream(), it is processed correctly.

Mutexes are used for synchronization in libzrtp. The mutex type is defined by zrtp_mutex_t type in the zrtp_types.h file. Like all of the system dependent functions, they are implemented as a set of interfaces:

Additional information on the implementation of these components can be found in the API description. These default interface implementations are already included in the library. POSIX mutexes are used for Unix platforms and windows mutexes for Windows. See zrtp_iface.c for more information. Flag DBUILD_WITH_CFUNC is used for default.

11 Trusted MitM and libzrtp

11.1 The registration ritual

As described in section 8.3 of the ZRTP Internet Draft, registration is necessary and sufficient for securely sending an SAS value through a trusted MitM. By registering, the client establishes trust in the PBX, enabling the client to send its ZRTP machine's authentication code. The shared secret used for authenticating a trusted MitM is generated and stored in the cache. There is a branch of server and client functions in libzrtp to operate with trusted MitMs. PBX related functions and data types.

The process from the client side consists of calling the PBX number, confirming the SAS, and then tagging the current call as "registration" using the zrtp_register_with_trusted_mitm() function. To separate MiTM registration calls from general VoIP calls, the server-side ZRTP endpoint should mark a registration call using a special flag in the Confirm packet sent to the client-side. The user-space application is informed of the registration ritual by a zrtp_event_t::ZRTP_EVENT_IS_CLIENT_ENROLLMENT event.

The server, having received a call to the registration number, authenticates the client using a password scheme, and then tags the call as a registration. To do this, the server ZRTP peer should start the ZRTP exchange in a special way, using one of the functions zrtp_start_registration_stream() or zrtp_secure_registration_stream(). On calling one of these functions, the PBX then generates a shared secret and associates it with that client for subsequent authentication and SAS transfer. In registration stream handling the following different scenarios are possible:

The user may also cancel registration to the PBX by removing the other MiTM shared secret with the PBX. If this occurs, libzrtp raises the zrtp_event_t::ZRTP_EVENT_USER_UNENROLLED event on the PBX side and it may remove it's own cache with this user (but this isn't recomended because it may create en platform for DOS attacks)

It would be inefficient to store the corresponding PBX shared secrets of every pair of ZID's in the cache used for regular calls, so PBX shared secrets are stored separately. The two functions in libzrtp for reading and writing the PBX cache are zrtp_mitmcache_get(), and zrtp_mitmcache_put(), respectively. The function interfaces are analogous to those of the functions zrtp_cache_put() and zrtp_cache_get() (see the API for further details). The cache is implemented as a single file with the list of PBX shared secrets at the beginning, and the list of regular shared secrets at the end.

11.2 Relaying the SAS

This section describes the libzrtp implementation for sending an SAS through a PBX (trusted MitM) as described in section 8.3 of the ZRTP Internet draft.

The two known problems in the PBX MitM model are synchronizing the SAS value and synchronizing its rendering scheme. The ZRTP protocol solves both of these using SAS Relaying. After all three sides have switched to Secure, in common for ZRTP endpoint way, the SAS value or its rendering scheme may be initiated and/or renewed from either side using the function zrtp_update_remote_options().

To use the new SAS Relaying Scheme, in the first stage of setting up a PBX connection, there is nothing different to do; having received an incoming call, the PBX establishes a secure channel with both sides using the zrtp_start_stream() function. Only after the transition to the Secure state does the PBX have enough information to synchronize the rendering scheme and SAS value to the registered user.

Obviously, the SAS may be transferred only to a registered user. Whether the user can receive an transferred SAS can be determined by the zrtp_is_user_enrolled() function. In this case, the function zrtp_update_remote_options() is used. These functions move the stream::mitm_m ode into the ZRTP_MITM_MODE_RECONFIRM_SERVER mode and allow the SAS value to be sent. Otherwise, a regular call is running without SAS transferring. When the PBX runs a call in MiTM mode it's possible that both sides are registered. In this case the PBX should choose one to resolve the ambiguity. This may be done with function zrtp_schoose_one_enrolled().

In order to maximize the simplicity of setting up a secure channel in MitM mode, libzrtp provides the function zrtp_resolve_mitm_call(). Upon the transition of both sides into the secure state this function is necessary and sufficient to synchronize the SAS value. After completing the process, libzrtp generates the zrtp_event_t::ZRTP_EVENT_REMOTE_SAS_UPDATED event.

Having received the SAS Relay packet, the client looks up the shared secret for the given PBX. If the shared secret matches, the client's ZRTP machine accepts the SAS and goes into zrtp_stream_t::mitm_mode ZRTP_MITM_MODE_RECONFIRM_CLIENT and generates the zrtp_event_t::ZRTP_EVENT_LOCAL_SAS_UPDATED event. The user-space application should handle this event and update the SAS value according to the specified rendering scheme. If the client receives a request for tranferring an SAS from an unverified user (the MitM shared secret is not found, or doesn't match), this results in the error zrtp_protocol_error_t::zrtp_error_possible_mitm3. Then the process of establishing a secure channel is terminated. The client's ZRTP machine cannot request any further action from the user except a temporary registration.

Warning:
Because the process of SAS transfer is transparent to the end user, the client must have some way to keep track of the fact that a call is sent through a MitM.

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