This howto gives a short introduction to GNU ZRTP. The first part describes the overall structure of GNU ZRTP: the two main components (GNU ZRTP core and GNU ZRTP ccRTP extension), and how they work together with ccRTP and the application. The second part shows some short programming examples, and describes the differences between normal ccRTP RTP sessions and GNU ZRTP SRTP sessions.
A complete GNU ZRTP implementation consists of two components, the GNU ZRTP core, and specific code that binds the GNU ZRTP core to the underlying RTP/SRTP stack and the operating system:
The GNU ZRTP C++ core uses a callback interface class (ZrtpCallback) to access RTP/SRTP or operating specific methods, for example to send data via the RTP/SRTP stack, to access timers and mutex handling, and to report events to the application.
An application uses the RTP/SRTP specific code only. This is the application's interface to control ZRTP behavior and to send and receive data. Therefore, the following paragraphs of this howto mainly describe the GNU ZRTP ccRTP extension. The source distribution of GNU ZRTP C++ also contains the documentation of the core implementation.
ZrtpQueue implements code that is specific to the GNU ccRTP implementation. ZrtpQueue also implements code to provide the mutex and timeout handling to the GNU ZRTP C++ core. Both, the mutex and the timeout handling use the GNU Common C++ library to stay independent of the operating system. For more information, refer to the GNU Common C++ web page.
To perform its tasks, ZrtpQueue
After instantiating a ZrtpQueue object using a specific type (see below for a short example), applications may use the ZRTP-specific methods of ZrtpQueue to control and set up GNU ZRTP C++, for example enable or disable ZRTP processing, or getting ZRTP status information.
GNU ZRTP provides a ZrtpUserCallback class that an application may extend and register with ZrtpQueue. GNU ZRTP C++ and ZrtpQueue use the ZrtpUserCallback methods to report ZRTP events to the application. The application may display this information to the user or act otherwise.
The following figure depicts the relationships between ZrtpQueue, ccRTP RTP/SRTP implementation, the GNU ZRTP C++ core, and an application that provides a ZrtpUserCallback class.
+----------+ | ccRTP | | RTP/SRTP | | | +----------+ ^ | extends | +----------------+ +-----+------+ +-----------------+ | Application | | | | | | instantiates | uses | ZrtpQueue | uses | GNU ZRTP | | a ZRTP Session +------+ implements +------+ core | | and provides | |ZrtpCallback| | implementation | |ZrtpUserCallback| | | | (ZRtp et al) | +----------------+ +------------+ | | +-----------------+
The picture shows that ZrtpQueue has a Janus face: one face to the application and one face to the GNU ZRTP C++ core implementation. As an extension to the standard ccRTP RTP/SRTP implementation, ZrtpQueue can provide the RTP/SRTP services to both the application and the GNU ZRTP C++ core. The use relations are two-way use relations: the application uses the interface of ZrtpQueue to set up and control GNU ZRTP, and ZrtpQueue uses the ZrtpUserCallback methods to report events to the application (if the application registered an own class that extends ZrtpUserCallback). On the other side ZrtpQueue instantiates the GNU ZRTP C++ core class (ZRtp) and uses its interface to control ZRTP, mainly on behalf of the application. ZrtpQueue also registers itself with the GNU ZRTP C++ core providing the internal ZrtpCallback interface. GNU ZRTP C++ uses this internal interface to access various services, for instance to send data via RTP session or timer support
The following paragraphs show some short examples of how to use GNU ZRTP C++. Most code snippets are extracted from the small demo program zrtptest located in the demo folder.
The GNU ZRTP C++ source distribution contains a detailed documentation of the available methods.
Because ZrtpQueue extends the ccRTP RTP/SRTP implementation (AVPQueue), all public methods defined by ccRTP are also available for a ZRTP session. ZrtpQueue overwrites some of the public methods of ccRTP (AVPQueue) to implement ZRTP-specific code.
GNU ZRTP C++ provides a SymmetricZRTPSession type to simplify its use. An application uses this type in the same way as it would use the normal ccRTP SymmetricRTPSession type. The following short code snippets show how an application could instantiate ccRTP and GNU ZRTP C++ sessions. The first snippet shows how to instantiate a ccRTP session:
... #include <ccrtp/rtp.h> ... SymmetricRTPSession tx(ssrc,InetHostAddress("localhost")); ...
The same code as above but using a GNU ZRTP C++ session this time:
... #include <libzrtpcpp/zrtpccrtp.h> ... SymmetricZRTPSession tx(ssrc, InetHostAddress("localhost")); ...
The only differences are the different include statements and the different session types.
An application may use a GNU ZRTP C++ session in the same way as a normal ccRTP RTP session. Because ZRTP requires a two-way communication, both parties of a RTP session must be able to send and receive data. Thus a RTP transmitter has to set a receiver address and a RTP receiver has to set a transmitter address. The following code snippet shows this:
... SymmetricZRTPSession tx(ssrc, InetHostAddress("localhost"), port + 2); tx.addDestination(InetHostAddress("host.destination.org"), port); ...
This example uses different port numbers to set up the RTP receiver and transmitter (just in case the sender and receiver are on the same host). Otherwise the application doesn't see any difference. To use the ZRTP capabilities, the application has to initialize GNU ZRTP first. Continuing the above code snippet:
tx.initialize("test_t.zid");
The parameter test_t.zid
defines the name of a ZRTP Id
file (ZID file) that stores the ZRTP identifier, the ZRTP retained secrets
and some other data used by ZRTP.
Note: Currently, GNU ZRTP requires a ZID file. Later versions may run without one. Running without a ZID file does not compromise security but may be less convenient for an user.
The initialization method opens the ZID file, performs some housekeeping, and enables ZRTP processing if no error occurred during the initialization phase. The application may now start the ZRTP discovery phase.
tx.startZrtp();
This method immediately starts the ZRTP discovery phase, and GNU ZRTP sends Hello packets to its peer. If the peer is already up and has ZRTP enabled, both will exchange Hello packets, negotiate the keys, compute the SRTP cryptographic data, and setup the SRTP session. Depending on the application's design and how the application uses GNU ZRTP C++, the ZRTP discovery and key exchange may overlap with RTP communication. Therefore, the application may send some RTP data packet that are not protected by SRTP.
To avoid sending unprotected data, applications can monitor the ZRTP activities and start data transmission only after GNU ZRTP has established the SRTP session. GNU ZRTP provides a user callback class that an application can extend and register with GNU ZRTP. The GNU ZRTP core and the GNU ZRTP ccRTP extension call the methods of the user callback to report events, for example after secure mode as been reached and SRTP sessions are up. See the following example for a simple user callback class:
/** * Simple User Callback class * * This class overwrites some methods from ZrtpUserCallback to get information * about ZRTP processing and information about ZRTP results. The standard * implementation of this class just performs return, thus effectively * supressing any callback or trigger. */ class MyUserCallback: public ZrtpUserCallback { void secureOn(std::string cipher) { cout << "Using cipher:" << cipher << endl; } void showSAS(std::string sas, bool verified) { cout << "SAS is: " << sas << endl; } }; ... tx.setUserCallback(new MyUserCallback()); ...
This simple user callback class just displays some information. GNU
ZRTP C++ calls the method secureOn()
after secure mode has
been reached and the SRTP session is active. The string parameter contains the
name, the mode, and the key length of the symmetric cipher that SRTP uses to
encrypt the data.
After the Short Authentication String (SAS) has been computed and the
verified flag has been checked, GNU ZRTP calls the showSAS()
method. The string parameter contains the SAS string, and the boolean parameter
is set to true
if both parties verified the SAS during a previous
session (refer to the ZRTP
specification for a detailed description of SAS).
Applications may use these callback methods to control the start of data
transmission, for example start data transmission only after the
secureOn()
event.
CAVEAT
All methods of the user callback class, and classes that extend this class, run
in the context of the RTP thread. Thus it is of paramount importance to keep
the execution time of the methods as short as possible.
GNU ZRTP C++ provides two ways to start the ZRTP discovery phase:
The next paragraphs describe these two mode in some more detail.
If the application disabled auto sensing mode, then the application
must start ZRTP by calling startZrtp()
. GNU ZRTP C++
immediately starts to send Hello packets to discover whether the peer is
available, and whether it supports ZRTP. If the peer supports ZRTP, then both
parties continue with ZRTP handshake, and negotiate the keys to set up
SRTP.
If the peer is either not available or not ZRTP-capable, then GNU
ZRTP C++ calls the user callback method zrtpNotSuppOther()
.
After this callback returns, GNU ZRTP C++ does not disable ZRTP processing
completely, but switches into a detect state. See also Restart of the ZRTP discovery phase.
If ZRTP was initialized with auto sensing mode, the application does
not need to call startZrtp()
. In auto sensing mode, GNU
ZRTP C++ monitors the traffic on the RTP session to decide when to start
the ZRTP discovery phase. GNU ZRTP C++ starts the ZRTP discovery phase
after at least one RTP packet was sent and received on the associated
RTP session.
The auto sensing mode is convenient for applications that open RTP sessions, but the actual RTP traffic starts some time later. This is often the case for RTP applications that use a signaling protocol to exchange the actual RTP parameters (port numbers, IP address) with their peer applications. Even after the signaling was successful, the applications cannot exactly determine when data starts to flow on the RTP sessions. This starting time depends on a lot of factors, for example network delays or delays in the applications.
[TODO]