A ZRTP session is established by a single DH stream. The session may be extended by using "Multistream" mode. Since the ZRTP protocol is stream-oriented, it is the stream context which is the main operant for most ZRTP functions.
ZRTP Asterisk module architecture
1) Loading res_zrtp.so module
load_module() // res/res_zrtp.c -> ast_zrtp_lib_initialize() // res/res_zrtp.c ...
2) Releasing res_zrtp.so module
-> unload_module() // res/res_zrtp.c st_zrtp_lib_release() // res/res_zrtp.c ...
3) Enabling ZRTP
This procedure is used to separate MiTM and PassThrough modes of Asterisk. ZRTP processing should be present only in MiTM mode.
.. ast_bridge_call() // res/res_features.c ast_channel_bridge() // main/channel.c ast_generic_bridge() // main/channel.c ... //enable ZRTP for both cannels -> g_zrtp_enable->ast_zrtp_enable_zrtp(c0) g_zrtp_enable->ast_zrtp_enable_zrtp(c1) ...
4) Detecting ZRTP-capability of endpoints
We consider an endpoint to be ZRTP-capable if a special SDP tag has been received from this endpoint.
.. sipsock_read() // channels/chan_sip.c handle_request() // channels/chan_sip.c process_sdp() // channels/chan_sip.c ... //set zrtp_capable flag -> p->zsession.zrtp_capable=1 ...
Also we consider an endpoint to be ZRTP-capable if a ZRTP-hello packet has been received from this endpoint.
.. ast_read() // main/channel.c __ast_read() // main/channel.c sip_read() // channels/chan_sip.c sip_rtp_read() // channels/chan_sip.c ... ast_rtp_read() ... //analyze packet type and set zrtp_capable flag -> get_packet_type() p->zsession.zrtp_capable=1 ...
5) Initializing a ZRTP channel
.. sip_new() // channels/chan_sip.c ... //ZRTP channel initialization -> g_zrtp_enable->ast_zrtp_init_zrtp_channel()
6) Deinitializing a ZRTP channel
We deinitialize a ZRTP channel when the sip channel is destroyed to release allocated resources.
.. sip_destroy() // channels/chan_sip.c __sip_destroy() // channels/chan_sip.c ... //Deinitialize ZRTP session -> g_zrtp_enable->ast_zrtp_done_zrtp_channel(); ...
7) Start ZRTP channel
If a ZRTP channel hasn't been started yet we try to start a ZRTP channel with every RTP packet.
.. ast_read() // main/channel.c __ast_read() // main/channel.c sip_read() // channels/chan_sip.c sip_rtp_read() // channels/chan_sip.c ... //try to start ZRTP channel every RTP packet -> g_zrtp_enable->ast_zrtp_start_zrtp_channel(); ...
8) Enter Secure state
When the ZRTP engine enters the Secure state it calls the zrtp_event_callback() function with the ZRTP_EVENT_IS_SECURE event value.
zrtp_event_callback() // res/res_zrtp.c //process ZRTP secure state -> case ZRTP_EVENT_IS_SECURE: ...
9) Process RTP packets
We have to decrypt RTP data before Asterisk will process it.
.. sip_rtp_read() // channels/chan_sip.c ast_rtp_read() // main/rtp.c ... //process RTP data by ZRTP engine -> g_zrtp_enable->ast_zrtp_read() ...
We have to encrypt RTP data before a packet will be sent.
.. sip_write() // channels/chan_sip.c ast_rtp_write() // main/rtp.c ast_rtp_raw_write() // main/rtp.c ... //peocess RTP data by ZRTP engine -> g_zrtp_enable->ast_zrtp_write() ...
10) Process RTCP packets
We have to decrypt RTCP data before Asterisk will process it.
.. sip_rtp_read() // channels/chan_sip.c ast_rtcp_read() // main/rtp.c ... //process RTCP data by ZRTP engine -> g_zrtp_enable->ast_zrtp_read_rtcp() ...
We have to encrypt RTCP data before a packet will be sent.
Process RTCP sender's report.
.. ast_rtcp_write() // main/rtp.c ast_rtcp_write_sr() // main/rtp.c ... //process RTCP data by ZRTP engine -> g_zrtp_enable->ast_zrtp_write_rtcp() ...
Send RTCP recepient's report.
.. ast_rtcp_write() // main/rtp.c ast_rtcp_write_rr() // main/rtp.c ... //process RTCP data by ZRTP engine -> g_zrtp_enable->ast_zrtp_write_rtcp() ...
11) Insert ZRTP signaling hash value into SDP of outgoing SIP packet
..
add_sdp()
...
-> //insert ZRTP signaling hash
...
12) Update ZRTP signaling hash value received from incoming SIP packet
.. sipsock_read() // channels/chan_sip.c handle_request() // channels/chan_sip.c process_sdp() // channels/chan_sip.c ... -> //update ZRTP signaling hash based on incoming packet ...
13) Process DTMF signals
We use DTMF signals to get commands from a ZRTP peer, such as to replay SAS values, mark a peer as verified, etc. All DTMF signals caught by a ZRTP engine will be passed to Asterisk transparently.
.. ast_bridge_call() // res/res_features.c ast_channel_bridge() // main/channel.c ast_generic_bridge() // main/channel.c ast_read() // main/channel.c ... -> //process DTMF signals by ZRTP engine ...
a) Process ZRTP packets
.. p2p_rtp_callback() // main/rtp.c bridge_p2p_rtp_write() // main/rtp.c ... -> //process ZRTP packets in Pass Through mode
Cache implementation
The current implementation of ZRTP for Asterisk uses the default cache implementation from libzrtp. The cache is stored in memory and is flushed to a binary file periodically and when the zrtp library is released. The advantage of using the default cache implementation is simplicity. On the other hand, the default cache implementation isn't intended to service multiple zrtp peers. It is recommended that an alternative cache implementation is used for better perfomance and larger load capacity, e.g. B-trees or databases.
Timeout handling
The ZRTP module has its own timer implementation. This timer is used by the ZRTP library for resending packets in the zrtp stream after a specified time. There is no separate thread for timeout handling. To handle timeouts, the ast_zrtp_process_timeout() function is called. This function is called for every incoming or outgoing packet.
ZRTP Protocol packet sending
To send a ZRTP protocol packet, the zrtp library calls the zrtp_send_rtp() function. ZRTP for Asterisk has the zrtp_send_rtp() implementation. It calls ast_zrtp_sendto() which sends the data over the connection in a UDP sendto system call.
System-dependent functions
The ZRTP library has the default implementation of system-dependent functions (such as memcpy, memset, etc). The default implementation of system-dependent functions is also used in the ZRTP Asterisk module.
..
ast_channel_setoption(chan, AST_OPTION_ENROLL_ZRTP, NULL, 0, 0); // apps/app_enroll_zrtp.c
..
Then it enters a loop to wait for control frames.
When a ZRTP stream enters the Secure state (zrtp_event_callback() with ZRTP_EVENT_IS_SECURE event value) it checks whether the ZRTP peer has already been enrolled. If the ZRTP peer has already been enrolled, an AST_CONTROL_ZRTP_ALREADY_ENROLLED control frame will be sent.
If the ZRTP peer hasn't yet been enrolled, an AST_CONTROL_ZRTP_ENROLLMENT_SECURE control frame will be sent.
If a non-ZRTP point tries to call a enrollment extension, it will be detected and a zrtp_event_callback() with the event value ZRTP_EVENT_NO_ZRTP_QUICK will be called. On this event the ZRTP Asterisk module sends an AST_CONTROL_ZRTP_NO_ZRTP_QUICK control frame.
Behaviour of the Enroll_zrtp application on arrival of different control frames: