diff -ruN pjproject-2.10/pjmedia/include/pjmedia/transport_zrtp.h pjsip/pjmedia/include/pjmedia/transport_zrtp.h
--- pjproject-2.10/pjmedia/include/pjmedia/transport_zrtp.h	1970-01-01 01:00:00.000000000 +0100
+++ pjsip/pjmedia/include/pjmedia/transport_zrtp.h	2021-02-06 16:42:58.084103561 +0100
@@ -0,0 +1,647 @@
+/* $Id$ */
+/*
+  Copyright (C) 2010 Werner Dittmann
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __PJMEDIA_TRANSPORT_ZRTP_H__
+#define __PJMEDIA_TRANSPORT_ZRTP_H__
+
+/**
+ * @file transport_zrtp.h
+ * @brief ZRTP Media Transport Adapter
+ */
+
+/* transport.h includes types.h -> config.h -> config_auto.h */
+#include <pjmedia/transport.h>
+
+#include "../../third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpCWrapper.h"
+
+/**
+ * @defgroup PJMEDIA_TRANSPORT_ZRTP ZRTP Transport Adapter
+ * @brief This the ZRTP transport adapter.
+ * @{
+ *
+ * PJMEDIA extension to support GNU ZRTP.
+ *
+ * ZRTP was developed by Phil Zimmermann and provides functions to
+ * negotiate keys and other necessary data (crypto data) to set-up
+ * the Secure RTP (SRTP) crypto context. Refer to Phil's ZRTP
+ * specification at his <a href="http://zfoneproject.com/">Zfone
+ * project</a> site to get more detailed information about the
+ * capabilities of ZRTP.
+ *
+ * <b>Short overview of the ZRTP implementation</b>
+ *
+ * ZRTP is a specific protocol to negotiate encryption algorithms
+ * and the required key material. ZRTP uses a RTP session to
+ * exchange its protocol messages. Thus ZRTP is independent of any
+ * signaling protocol like SIP, XMPP and alike.
+ *
+ * A complete GNU ZRTP implementation consists of two parts, the
+ * GNU ZRTP core and some specific code that binds the GNU ZRTP core to
+ * the underlying RTP/SRTP stack and the operating system:
+ * <ul>
+ * <li>
+ *      The GNU ZRTP core is independent of a specific RTP/SRTP
+ *      stack and the operationg system and consists of the ZRTP
+ *      protocol state engine, the ZRTP protocol messages, and the
+ *      GNU ZRTP engine. The GNU ZRTP engine provides methods to
+ *      setup ZRTP message and to analyze received ZRTP messages,
+ *      to compute the crypto data required for SRTP, and to
+ *      maintain the required hashes and HMAC.
+ * </li>
+ * <li>
+ *      The second part of an implementation is specific
+ *      <em>glue</em> code the binds the GNU ZRTP core to the
+ *      actual RTP/SRTP implementation and other operating system
+ *      specific services such as timers, mutexes.
+ * </li>
+ * </ul>
+ *
+ * The GNU ZRTP core uses callback methods (refer to
+ * zrtp_Callback) to access RTP/SRTP or operating specific methods,
+ * for example to send data via the RTP stack, to access
+ * timers, provide mutex handling, and to report events to the
+ * application.
+ *
+ * <b>The PJMEDIA ZRTP transport</b>
+ *
+ * ZRTP transport implements code that is specific to the pjmedia
+ * implementation. ZRTP transport also implements the specific code to
+ * provide the mutex and timeout handling to the GNU ZRTP
+ * core. Both, the mutex and the timeout handling, use the pjlib
+ * library to stay independent of the operating
+ * seystem.
+ *
+ * To perform its tasks ZRTP transport
+ * <ul>
+ * <li> implements the pjmedia transport functions and callbacks.
+ * </li>
+ * <li> implements the zrtp_Callbacks methods to provide
+ *      access and other specific services (timer, mutex) to GNU
+ *      ZRTP
+ * </li>
+ * <li> provides ZRTP specific methods that applications may use
+ *      to control and setup GNU ZRTP
+ * </li>
+ * <li> can register and use an application specific callback
+ *      class (refer to zrtp_UserCallbacks)
+ * </li>
+ * </ul>
+ *
+ * After instantiating a GNU ZRTP session (see below for a short
+ * example) applications may use the methods of
+ * ZRTP transport and the ZRTP engine to control and setup GNU ZRTP,
+ * for example enable or disable ZRTP processing or getting ZRTP status
+ * information.
+ *
+ * GNU ZRTP defines zrtp_UserCallback methods structure that an application
+ * may use and register with ZRTP transport. GNU ZRTP and ZRTP transport
+ * use the zrtp_UserCallback 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
+ * ZRTP transport, pjmedia RTP implementation, the GNU ZRTP core,
+ * SRTP and an application that provides zrtp_UserCallback methods.
+ *
+ @verbatim
+                            +-----------+
+                            |           |
+                            | SRTP-ZRTP |
+                            |           |
+                            +-----------+
+                            |C Wrapper  |
+                            +-----+-----+
+                                  |
+                                  | uses
+                                  |
+  +-----------------+      +-------+--------+      +-+-----------------+
+  |  App (pjsua)    |      |                |      |C|                 |
+  |  creates a      | uses | transport_zrtp | uses | |    GNU ZRTP     |
+  | ZRTP transport  +------+   implements   +------+W|      core       |
+  | and implements  |      |  zrtp_Callback |      |r| implementation  |
+  |zrtp_UserCallback|      |                |      |a|  (ZRtp et al)   |
+  +-----------------+      +----------------+      |p|                 |
+                                                   +-+-----------------+
+
+@endverbatim
+ *
+ * The following short code snippet shows how to use ZRTP transport
+ *
+ * @code
+ *
+ * #include <pjmedia/transport_zrtp.h>
+ * ...
+ * // Create media transport
+ * status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
+ *                                       0, &transport);
+ * if (status != PJ_SUCCESS)
+ *     return status;
+ *
+ * status = pjmedia_transport_zrtp_create(med_endpt, NULL, transport,
+ *                                        &zrtp_tp);
+ * app_perror(THIS_FILE, "Error creating zrtp", status);
+ * transport = zrtp_tp;
+ * if (dir == PJMEDIA_DIR_ENCODING)
+ *      pjmedia_transport_zrtp_initialize(transport, "testenc.zid", 1, NULL);
+ * else
+ *      pjmedia_transport_zrtp_initialize(transport, "testdec.zid", 1, NULL);
+ * ...
+ * @endcode
+ *
+ */
+
+#define PJMEDIA_TRANSPORT_TYPE_ZRTP PJMEDIA_TRANSPORT_TYPE_USER+2
+
+PJ_BEGIN_DECL
+
+/**
+ * ZRTP option.
+ */
+typedef enum pjmedia_zrtp_use
+{
+    /** When this flag is specified, ZRTP will be disabled. */
+    PJMEDIA_NO_ZRTP  = 1,
+
+    /** When this flag is specified, PJSUA-LIB creates a ZRTP transport
+     * call calls back the applicaion for further process if callback is
+     * set.
+     */
+    PJMEDIA_CREATE_ZRTP  = 2
+
+} pjmedia_zrtp_use;
+
+/**
+ * This structure specifies ZRTP transport specific info. This will fit
+ * into \a buffer field of pjmedia_transport_specific_info.
+ */
+typedef struct pjmedia_zrtp_info
+{
+    /**
+     * Specify whether the ZRTP transport is active for this session.
+     */
+    pj_bool_t           active;
+
+    /**
+     * Specify the cipher being used.
+     */
+    char cipher[128];
+
+} pjmedia_zrtp_info;
+
+/**
+ * Application callback methods.
+ *
+ * The RTP stack specific part of GNU ZRTP uses these callback methods
+ * to report ZRTP events to the application. Thus the application that
+ * instantiates the RTP stack shall implement these methods and show these
+ * inforemation to the user.
+ *
+ * <b>CAVEAT</b><br/>
+ * All user callback methods 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.
+ *
+ * @author Werner Dittmann <Werner.Dittmann@t-online.de>
+ */
+typedef struct pjmedia_zrtp_cb
+{
+    /**
+     * Inform user interface that security is active now.
+     *
+     * ZRTP calls this method if the sender and the receiver are
+     * in secure mode now.
+     *
+     * @param cipher
+     *    Name and mode of cipher used to encrypt the SRTP stream
+     */
+    void (*secure_on)(pjmedia_transport *tp, char* cipher);
+
+    /**
+     * Inform user interface that security is not active any more.
+     *
+     * ZRTP calls this method if either the sender or the receiver
+     * left secure mode.
+     *
+     */
+    void (*secure_off)(pjmedia_transport *tp);
+
+    /**
+     * Show the Short Authentication String (SAS) on user interface.
+     *
+     * ZRTP calls this method to display the SAS and inform about the SAS
+     * verification status. The user interface shall enable a SAS verfication
+     * button (or similar UI element). The user shall click on this UI
+     * element after he/she confirmed the SAS code with the partner.
+     *
+     * @param sas
+     *     The string containing the SAS.
+     * @param verified
+     *    If <code>verified</code> is true then SAS was verified by both
+     *    parties during a previous call, otherwise it is set to false.
+     */
+    void (*show_sas)(pjmedia_transport *tp, char* sas, int32_t verified);
+
+    /**
+     * Inform the user that ZRTP received "go clear" message from its peer.
+     *
+     * On receipt of a go clear message the user is requested to confirm
+     * a switch to unsecure (clear) modus. Until the user confirms ZRTP
+     * (and the underlying RTP) does not send any data.
+     * 
+     */
+    void (*confirm_go_clear)(pjmedia_transport *tp);
+
+    /**
+     * Show some information to user.
+     *
+     * ZRTP calls this method to display some information to the user.
+     * Along with the message ZRTP provides a severity indicator that
+     * defines: Info, Warning, Error, and Alert. Refer to the <code>
+     * MessageSeverity</code> enum in <code>ZrtpCodes.h</code>. The
+     * UI may use this indicator to highlight messages or alike.
+     *
+     * @param sev
+     *     Severity of the message.
+     * @param subCode
+     *     The subcode identifying the reason.
+     */
+    void (*show_message)(pjmedia_transport *tp, int32_t sev, int32_t subCode);
+
+    /**
+     * ZRTP transport calls this if the negotiation failed.
+     *
+     * ZRTPQueue calls this method in case ZRTP negotiation failed. The
+     * parameters show the severity as well as some explanatory text.
+     * Refer to the <code>MessageSeverity</code> enum above.
+     *
+     * @param severity
+     *     This defines the message's severity
+     * @param subCode
+     *     The subcode identifying the reason.
+     */
+    void (*negotiation_failed)(pjmedia_transport *tp, int32_t severity, int32_t subCode);
+
+    /**
+     * ZRTP transport calls this method if the other side does not support ZRTP.
+     *
+     * If the other side does not answer the ZRTP <em>Hello</em> packets then
+     * ZRTP calls this method.
+     *
+     */
+    void (*not_supported_by_other)(pjmedia_transport *tp);
+
+    /**
+     * ZRTP transport calls this method to inform about a PBX enrollment request.
+     *
+     * Please refer to chapter 8.3 ff to get more details about PBX enrollment
+     * and SAS relay.
+     *
+     * @param info
+     *    Give some information to the user about the PBX requesting an
+     *    enrollment.
+     */
+    void (*ask_enrollment)(pjmedia_transport *tp, int32_t info);
+
+    /**
+     * ZRTP transport calls this method to inform about PBX enrollment result.
+     *
+     * Informs the use about the acceptance or denial of an PBX enrollment
+     * request
+     *
+     * @param info
+     *    Give some information to the user about the result of an
+     *    enrollment.
+     */
+    void (*inform_enrollment)(pjmedia_transport *tp, int32_t info);
+
+    /**
+     * ZRTP transport calls this method to request a SAS signature.
+     *
+     * After ZRTP core was able to compute the Short Authentication String
+     * (SAS) it calls this method. The client may now use an approriate
+     * method to sign the SAS. The client may use
+     * setSignatureData() of ZrtpQueue to store the signature
+     * data an enable signature transmission to the other peer. Refer
+     * to chapter 8.2 of ZRTP specification.
+     *
+     * @param sas
+     *    The SAS string to sign.
+     * @see ZrtpQueue#setSignatureData
+     *
+     */
+    void (*sign_sas)(pjmedia_transport *tp, uint8_t* sas);
+
+    /**
+     * ZRTP transport calls this method to request a SAS signature check.
+     *
+     * After ZRTP received a SAS signature in one of the Confirm packets it
+     * call this method. The client may use <code>getSignatureLength()</code>
+     * and <code>getSignatureData()</code>of ZrtpQueue to get the signature
+     * data and perform the signature check. Refer to chapter 8.2 of ZRTP
+     * specification.
+     *
+     * If the signature check fails the client may return false to ZRTP. In
+     * this case ZRTP signals an error to the other peer and terminates
+     * the ZRTP handshake.
+     *
+     * @param sas
+     *    The SAS string that was signed by the other peer.
+     * @return
+     *    true if the signature was ok, false otherwise.
+     *
+     */
+    int32_t (*check_sas_signature)(pjmedia_transport *tp, uint8_t* sas);
+} pjmedia_zrtp_cb;
+
+
+/**
+ * Create the transport adapter, specifying the underlying transport to be
+ * used to send and receive RTP/RTCP packets.
+ *
+ * @param endpt     The media endpoint.
+ * @param timer_heap  The heap where timers will be scheduled.
+ * @param transport The underlying media transport to send and receive
+ *          RTP/RTCP packets.
+ * @param p_tp      Pointer to receive the media transport instance.
+ *
+ * @param close_slave
+ *          Close the slave transport on transport_destroy. PJSUA-LIB
+ *          sets this to PJ_FALSE because it takes care of this.
+ *
+ * @return      PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_transport_zrtp_create( pjmedia_endpt *endpt,
+                                                    pj_timer_heap_t *timer_heap,
+                                                    pjmedia_transport *transport,
+                                                    pjmedia_transport **p_tp,
+                                                    pj_bool_t close_slave);
+
+/*
+ * Implement the specific ZRTP transport functions
+ */
+
+/**
+ * Initialize the ZRTP transport.
+ *
+ * Before an application can use ZRTP it has to initialize the
+ * ZRTP implementation. This method opens a file that contains ZRTP specific
+ * information such as the applications ZID (ZRTP id) and its
+ * retained shared secrets.
+ *
+ * Before an application initializes the ZRTP it may use ZRTP functions
+ * to set specific configuration data. See the relevant documentation
+ * in @c ZrtpCWrapper.h . The application can peform this after
+ * it created transport_zrtp.
+ *
+ * If one application requires several ZRTP sessions all
+ * sessions use the same timeout thread and use the same ZID
+ * file. Therefore an application does not need to do any
+ * synchronisation regading ZID files or timeouts. This is
+ * managed by the ZRTP implementation.
+ *
+ * The current implementation of ZRTP transport does not support
+ * different ZID files for one application instance. This
+ * restriction may be removed in later versions.
+ *
+ * The application may specify its own ZID file name. If no
+ * ZID file name is specified it defaults to
+ * <code>$HOME/.GNUccRTP.zid</code> if the <code>HOME</code>
+ * environment variable is set. If it is not set the current
+ * directory is used.
+ *
+ * If the method could set up the timeout thread and open the ZID
+ * file then it enables ZRTP processing and returns.
+ *
+ * @param tp
+ *      Pointer to the ZRTP transport data as returned by
+ *      @c pjmedia_transport_zrtp_create.
+ *
+ * @param zidFilename
+ *     The name of the ZID file, can be a relative or absolut
+ *     filename.
+ *
+ * @param autoEnable
+ *     if set to true the method automatically sets enableZrtp to
+ *     true. This enables the ZRTP auto-sense mode.
+ *
+ * @param zrtp_cb
+ *     Pointer the application's ZRTP callbacks structure. Setting
+ *     a NULL switches off the user callbacks
+ * @return
+ *     PJ_SUCCESS on success, ZRTP processing enabled, other codes
+ *     leave ZRTP processing disabled.
+ *
+ */
+PJ_DECL(pj_status_t) pjmedia_transport_zrtp_initialize(pjmedia_transport *tp,
+                                                       const char *zidFilename,
+                                                       pj_bool_t autoEnable,
+                                                       pjmedia_zrtp_cb *zrtp_cb);
+/**
+ * Enable or disable ZRTP processing.
+ *
+ * Call this method to enable or disable ZRTP processing after
+ * calling <code>pjmedia_transport_zrtp_initialize</code> with the
+ * parameter @c autoEnable set to false. This can be done before
+ * using a RTP session or at any time during a RTP session.
+ *
+ * Existing SRTP sessions or currently active ZRTP processing will
+ * not be stopped or disconnected.
+ *
+ * If the application enables ZRTP then:
+ * <ul>
+ * <li>ZRTP transport starts to send ZRTP Hello packets after at least
+ * one RTP packet was sent and received on the associated RTP
+ * session. Thus if an application enables ZRTP and ZRTP transport
+ * detects traffic on the RTP session then ZRTP transport automatically
+ * starts the ZRTP protocol. This automatic start is convenient
+ * for applications that negotiate RTP parameters and set up RTP
+ * sessions but the actual RTP traffic starts some time later.
+ * </li>
+ * <li>ZRTP transport analyses incoming packets to detect ZRTP
+ * messages. If ZRTP was started, either via automatic start (see
+ * above) or explicitly via @c zrtp_startZrtp, then ZrtpQueue
+ * forwards ZRTP packets to the GNU ZRTP core.
+ * </ul>
+ *
+ * @param tp
+ *      Pointer to the ZRTP transport data as returned by
+ *      @c pjmedia_transport_zrtp_create.
+ *
+ * @param onOff
+ *     @c 1 to enable ZRTP, @c 0 to disable ZRTP
+ */
+PJ_DECL(void) pjmedia_transport_zrtp_setEnableZrtp(pjmedia_transport *tp, pj_bool_t onOff);
+
+/**
+ * Return the state of ZRTP enable state.
+ *
+ * @param tp
+ *      Pointer to the ZRTP transport data as returned by
+ *      @c pjmedia_transport_zrtp_create.
+ *
+ * @return @c true if ZRTP processing is enabled, @c false
+ * otherwise.
+ */
+PJ_DECL(pj_bool_t) pjmedia_transport_zrtp_isEnableZrtp(pjmedia_transport *tp);
+
+/**
+ * Starts the ZRTP protocol engine.
+ *
+ * Applications may call this method to immediatly start the ZRTP protocol
+ * engine any time after initializing ZRTP and setting optinal parameters,
+ * for example client id or multi-stream parameters.
+ *
+ * If the application does not call this method but sucessfully initialized
+ * the ZRTP engine using @c pjmedia_transport_zrtp_initialize then ZRTP may
+ * also start, depending on the autoEnable parameter.
+ *
+ * @param tp
+ *      Pointer to the ZRTP transport data as returned by
+ *      @c pjmedia_transport_zrtp_create.
+ *
+ * @see pjmedia_transport_zrtp_initialize
+ */
+PJ_DECL(void) pjmedia_transport_zrtp_startZrtp(pjmedia_transport *tp);
+
+/**
+ * Stops the ZRTP protocol engine.
+ *
+ * Applications call this method to stop the ZRTP protocol
+ * engine. The ZRTP transport can not start or process any ZRTP
+ * negotiations.
+ *
+ * This call does not deactivate SRTP processing of ZRTP transport, thus
+ * the ZRTP transport still encrypts/decrypts data via SRTP.
+ *
+ * @param tp
+ *      Pointer to the ZRTP transport data as returned by
+ *      @c pjmedia_transport_zrtp_create.
+ *
+ */
+PJ_DECL(void) pjmedia_transport_zrtp_stopZrtp(pjmedia_transport *tp);
+
+/**
+ * Set the local SSRC in case of receive-only sessions.
+ *
+ * Receiver-only RTP sessions never send RTP packets, thus ZRTP cannot learn
+ * the local (sender) SSRC. ZRTP requires the SSRC to bind the RTP session
+ * to the SRTP and its handshake. In this case the application shall generate
+ * a SSRC value and set it.
+ *
+ * Usually an application knows if a specific RTP session is receive-only, for
+ * example by inspecting and parsing the SDP data.
+ *
+ * If the application later decides to switch this RTP session to full-duplex
+ * mode (send and receive) it shall use the generated SSRC to intialize the
+ * RTP session. Then the outgoing packets are encrypted by SRTP.
+ *
+ * @param tp
+ *      Pointer to the ZRTP transport data as returned by
+ *      @c pjmedia_transport_zrtp_create.
+ *
+ * @param ssrc
+ *      The local ssrc value in host order.
+ */
+PJ_DECL(void) pjmedia_transport_zrtp_setLocalSSRC(pjmedia_transport *tp, uint32_t ssrc);
+
+/**
+ * Check the state of the MitM mode flag.
+ *
+ * If true then this ZRTP session acts as MitM, usually enabled by a PBX
+ * client (user agent)
+ *
+ * @return state of mitmMode
+ */
+PJ_DECL(pj_bool_t) pjmedia_transport_zrtp_isMitmMode(pjmedia_transport *tp);
+
+/**
+ * Set the state of the MitM mode flag.
+ *
+ * If MitM mode is set to true this ZRTP session acts as MitM, usually
+ * enabled by a PBX client (user agent).
+ *
+ * @param mitmMode defines the new state of the mitmMode flag
+ */
+PJ_DECL(void) pjmedia_transport_zrtp_setMitmMode(pjmedia_transport *tp, pj_bool_t mitmMode);
+
+/**
+ * Set / reset the SAS verification flag.
+ *
+ */
+PJ_DECL(void) pjmedia_transport_zrtp_setSASVerified(pjmedia_transport *tp, pj_bool_t verified);
+
+/**
+ * Get the peer's ZID.
+ *
+ */
+PJ_DECL(int) pjmedia_transport_zrtp_getPeerZid(pjmedia_transport *tp, unsigned char* data);
+
+/**
+ * Get the peer's name.
+ *
+ */
+PJ_DECL(char*) pjmedia_transport_zrtp_getPeerName(pjmedia_transport *tp);
+
+/**
+ * Set the peer's name.
+ *
+ */
+PJ_DECL(void) pjmedia_transport_zrtp_putPeerName(pjmedia_transport *tp, const char *name);
+
+
+PJ_DECL(char*) pjmedia_transport_zrtp_getMultiStreamParameters(pjmedia_transport *tp, pj_int32_t *length);
+
+PJ_DECL(void) pjmedia_transport_zrtp_setMultiStreamParameters(pjmedia_transport *tp, const char *parameters, pj_int32_t length, pjmedia_transport *master_tp);
+
+/**
+ * Get the ZRTP context pointer.
+ *
+ * Appplications need the ZRTP context pointer if they call ZRTP specific
+ * methods. The ZRTP specific include file @c ZrtpCWrapper contains the
+ * descriptions of the ZRTP methods.
+ *
+ * @return Pointer to ZRTP context
+ *
+ * @see zrtp_setAuxSecret()
+ * @see zrtp_setPbxSecret()
+ * @see zrtp_inState()
+ * @see zrtp_SASVerified()
+ * @see zrtp_resetSASVerified()
+ * @see zrtp_getHelloHash()
+ * @see zrtp_getMultiStrParams()
+ * @see zrtp_setMultiStrParams()
+ * @see zrtp_isMultiStream()
+ * @see zrtp_isMultiStreamAvailable()
+ * @see zrtp_acceptEnrollment()
+ * @see zrtp_setSignatureData()
+ * @see zrtp_getSignatureData()
+ * @see zrtp_getSignatureLength()
+ * @see zrtp_getZid();
+ */
+PJ_DECL(ZrtpContext*) pjmedia_transport_zrtp_getZrtpContext(pjmedia_transport *tp);
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif  /* __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__ */
+
+
diff -ruN pjproject-2.10/pjmedia/src/pjmedia/transport_zrtp.c pjsip/pjmedia/src/pjmedia/transport_zrtp.c
--- pjproject-2.10/pjmedia/src/pjmedia/transport_zrtp.c	1970-01-01 01:00:00.000000000 +0100
+++ pjsip/pjmedia/src/pjmedia/transport_zrtp.c	2021-02-06 16:42:04.322625052 +0100
@@ -0,0 +1,1261 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010 Werner Dittmann
+ * This is the pjmedia ZRTP transport module.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <pjmedia/transport_zrtp.h>
+#include <pjmedia/endpoint.h>
+#include <pjlib.h>
+#include <pjlib-util.h>
+
+#include "../../third_party/zsrtp/include/ZsrtpCWrapper.h"
+
+#define THIS_FILE "transport_zrtp.c"
+
+#define MAX_RTP_BUFFER_LEN	    PJMEDIA_MAX_MTU
+#define MAX_RTCP_BUFFER_LEN	    PJMEDIA_MAX_MTU
+
+
+/* Transport functions prototypes */
+static pj_status_t transport_get_info(pjmedia_transport *tp,
+                                      pjmedia_transport_info *info);
+static pj_status_t transport_attach(pjmedia_transport *tp,
+                                    void *user_data,
+                                    const pj_sockaddr_t *rem_addr,
+                                    const pj_sockaddr_t *rem_rtcp,
+                                    unsigned addr_len,
+                                    void (*rtp_cb)(void*,
+                                                   void*,
+                                                   pj_ssize_t),
+                                    void (*rtcp_cb)(void*,
+                                                    void*,
+                                                    pj_ssize_t));
+static void    transport_detach(pjmedia_transport *tp,
+                                void *strm);
+static pj_status_t transport_send_rtp(pjmedia_transport *tp,
+                                      const void *pkt,
+                                      pj_size_t size);
+static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
+                                       const void *pkt,
+                                       pj_size_t size);
+static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
+                                        const pj_sockaddr_t *addr,
+                                        unsigned addr_len,
+                                        const void *pkt,
+                                        pj_size_t size);
+static pj_status_t transport_media_create(pjmedia_transport *tp,
+        pj_pool_t *sdp_pool,
+        unsigned options,
+        const pjmedia_sdp_session *rem_sdp,
+        unsigned media_index);
+static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
+                                        pj_pool_t *sdp_pool,
+                                        pjmedia_sdp_session *local_sdp,
+                                        const pjmedia_sdp_session *rem_sdp,
+                                        unsigned media_index);
+static pj_status_t transport_media_start(pjmedia_transport *tp,
+        pj_pool_t *pool,
+        const pjmedia_sdp_session *local_sdp,
+        const pjmedia_sdp_session *rem_sdp,
+        unsigned media_index);
+static pj_status_t transport_media_stop(pjmedia_transport *tp);
+static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
+        pjmedia_dir dir,
+        unsigned pct_lost);
+static pj_status_t transport_destroy(pjmedia_transport *tp);
+
+
+/* The transport operations */
+static struct pjmedia_transport_op tp_zrtp_op =
+{
+    &transport_get_info,
+    &transport_attach,
+    &transport_detach,
+    &transport_send_rtp,
+    &transport_send_rtcp,
+    &transport_send_rtcp2,
+    &transport_media_create,
+    &transport_encode_sdp,
+    &transport_media_start,
+    &transport_media_stop,
+    &transport_simulate_lost,
+    &transport_destroy
+};
+
+/* The transport zrtp instance */
+struct tp_zrtp
+{
+    pjmedia_transport base;
+    pj_pool_t       *pool;
+
+    /* Stream information. */
+    void        *stream_user_data;
+    void (*stream_rtp_cb)(void *user_data,
+                          void *pkt,
+                          pj_ssize_t);
+    void (*stream_rtcp_cb)(void *user_data,
+                           void *pkt,
+                           pj_ssize_t);
+
+    /* Add your own member here.. */
+    uint64_t protect;
+    uint64_t unprotect;
+    int32_t  unprotect_err;
+    int32_t refcount;
+    pj_timer_heap_t* timer_heap;
+    pj_timer_entry timeoutEntry;
+    pj_mutex_t* zrtpMutex;
+    ZsrtpContext* srtpReceive;
+    ZsrtpContext* srtpSend;
+    ZsrtpContextCtrl* srtcpReceive;
+    ZsrtpContextCtrl* srtcpSend;
+    void* sendBuffer;
+    void* sendBufferCtrl;
+    pj_uint8_t* zrtpBuffer;
+//    pj_int32_t sendBufferLen;
+    pj_uint32_t peerSSRC;       /* stored in host order */
+    pj_uint32_t localSSRC;      /* stored in host order */
+    char* clientIdString;
+    pjmedia_transport   *slave_tp;
+    pjmedia_zrtp_cb cb;
+    ZrtpContext* zrtpCtx;
+    pj_uint16_t zrtpSeq;
+    pj_bool_t enableZrtp;
+    pj_bool_t started;
+    pj_bool_t close_slave;
+    pj_bool_t mitmMode;
+    char cipher[128];
+};
+
+/* Forward declaration of thethe ZRTP specific callback functions that this
+  adapter must implement */
+static int32_t zrtp_sendDataZRTP(ZrtpContext* ctx, const uint8_t* data, int32_t length) ;
+static int32_t zrtp_activateTimer(ZrtpContext* ctx, int32_t time) ;
+static int32_t zrtp_cancelTimer(ZrtpContext* ctx) ;
+static void zrtp_sendInfo(ZrtpContext* ctx, int32_t severity, int32_t subCode) ;
+static int32_t zrtp_srtpSecretsReady(ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part) ;
+static void zrtp_srtpSecretsOff(ZrtpContext* ctx, int32_t part) ;
+static void zrtp_srtpSecretsOn(ZrtpContext* ctx, char* c, char* s, int32_t verified) ;
+static void zrtp_handleGoClear(ZrtpContext* ctx) ;
+static void zrtp_zrtpNegotiationFailed(ZrtpContext* ctx, int32_t severity, int32_t subCode) ;
+static void zrtp_zrtpNotSuppOther(ZrtpContext* ctx) ;
+static void zrtp_synchEnter(ZrtpContext* ctx) ;
+static void zrtp_synchLeave(ZrtpContext* ctx) ;
+static void zrtp_zrtpAskEnrollment(ZrtpContext* ctx, int32_t info) ;
+static void zrtp_zrtpInformEnrollment(ZrtpContext* ctx, int32_t info) ;
+static void zrtp_signSAS(ZrtpContext* ctx, uint8_t* sasHash) ;
+static int32_t zrtp_checkSASSignature(ZrtpContext* ctx, uint8_t* sasHash) ;
+
+/* The callback function structure for ZRTP */
+static zrtp_Callbacks c_callbacks =
+{
+    &zrtp_sendDataZRTP,
+    &zrtp_activateTimer,
+    &zrtp_cancelTimer,
+    &zrtp_sendInfo,
+    &zrtp_srtpSecretsReady,
+    &zrtp_srtpSecretsOff,
+    &zrtp_srtpSecretsOn,
+    &zrtp_handleGoClear,
+    &zrtp_zrtpNegotiationFailed,
+    &zrtp_zrtpNotSuppOther,
+    &zrtp_synchEnter,
+    &zrtp_synchLeave,
+    &zrtp_zrtpAskEnrollment,
+    &zrtp_zrtpInformEnrollment,
+    &zrtp_signSAS,
+    &zrtp_checkSASSignature
+};
+
+static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e);
+
+static char clientId[] =    "SIP SIMPLE Client SDK";
+
+/*
+ * Create the ZRTP transport.
+ */
+PJ_DEF(pj_status_t) pjmedia_transport_zrtp_create(pjmedia_endpt *endpt,
+                                                  pj_timer_heap_t *timer_heap,
+                                                  pjmedia_transport *tp,
+                                                  pjmedia_transport **p_tp,
+                                                  pj_bool_t close_slave)
+{
+    pj_pool_t *pool;
+    struct tp_zrtp *zrtp;
+
+    PJ_ASSERT_RETURN(endpt && tp && p_tp, PJ_EINVAL);
+
+    /* Create the pool and initialize the adapter structure */
+    pool = pjmedia_endpt_create_pool(endpt, "zrtp%p", 5*1024, 512);
+    zrtp = PJ_POOL_ZALLOC_T(pool, struct tp_zrtp);
+    zrtp->pool = pool;
+
+    /* Initialize base pjmedia_transport */
+    pj_memcpy(zrtp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME);
+    zrtp->base.type = tp->type;
+    zrtp->base.op = &tp_zrtp_op;
+
+    /* Set the timer heap to be used for timers */
+    zrtp->timer_heap = timer_heap;
+
+    /* Create the empty wrapper */
+    zrtp->zrtpCtx = zrtp_CreateWrapper();
+
+    /* Initialize standard values */
+    zrtp->clientIdString = clientId;    /* Set standard name */
+    zrtp->zrtpSeq = 1;                  /* TODO: randomize */
+    pj_mutex_create_simple(zrtp->pool, "zrtp", &zrtp->zrtpMutex);
+    zrtp->zrtpBuffer = pj_pool_zalloc(pool, MAX_ZRTP_SIZE);
+    zrtp->sendBuffer = pj_pool_zalloc(pool, MAX_RTP_BUFFER_LEN);
+    zrtp->sendBufferCtrl = pj_pool_zalloc(pool, MAX_RTCP_BUFFER_LEN);
+
+    zrtp->slave_tp = tp;
+    zrtp->close_slave = close_slave;
+    zrtp->mitmMode = PJ_FALSE;
+
+    /* Done */
+    zrtp->refcount++;
+    *p_tp = &zrtp->base;
+    return PJ_SUCCESS;
+}
+
+PJ_DECL(pj_status_t) pjmedia_transport_zrtp_initialize(pjmedia_transport *tp,
+                                                       const char *zidFilename,
+                                                       pj_bool_t autoEnable,
+                                                       pjmedia_zrtp_cb *cb)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    zrtp_initializeZrtpEngine(zrtp->zrtpCtx, &c_callbacks, zrtp->clientIdString,
+                              zidFilename, zrtp, zrtp->mitmMode);
+    zrtp->enableZrtp = autoEnable;
+    if (cb)
+	pj_memcpy(&zrtp->cb, cb, sizeof(pjmedia_zrtp_cb));
+    return PJ_SUCCESS;
+}
+
+static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)e->user_data;
+
+    zrtp_processTimeout(zrtp->zrtpCtx);
+    PJ_UNUSED_ARG(ht);
+}
+
+/*
+ * Here start with callback functions that support the ZRTP core
+ */
+static int32_t zrtp_sendDataZRTP(ZrtpContext* ctx, const uint8_t* data, int32_t length)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+    pj_uint16_t totalLen = length + 12;     /* Fixed number of bytes of ZRTP header */
+    pj_uint32_t crc;
+    pj_uint8_t* buffer = zrtp->zrtpBuffer;
+    pj_uint16_t* pus;
+    pj_uint32_t* pui;
+
+    if ((totalLen) > MAX_ZRTP_SIZE)
+        return 0;
+
+    /* Get some handy pointers */
+    pus = (pj_uint16_t*)buffer;
+    pui = (pj_uint32_t*)buffer;
+
+    /* set up fixed ZRTP header */
+    *buffer = 0x10;     /* invalid RTP version - refer to ZRTP spec chap 5 */
+    *(buffer + 1) = 0;
+    pus[1] = pj_htons(zrtp->zrtpSeq++);
+    pui[1] = pj_htonl(ZRTP_MAGIC);
+    pui[2] = pj_htonl(zrtp->localSSRC);   /* stored in host order */
+
+    /* Copy ZRTP message data behind the header data */
+    pj_memcpy(buffer+12, data, length);
+
+    /* Setup and compute ZRTP CRC */
+    crc = zrtp_GenerateCksum(buffer, totalLen-CRC_SIZE);
+
+    /* convert and store CRC in ZRTP packet.*/
+    crc = zrtp_EndCksum(crc);
+    *(uint32_t*)(buffer+totalLen-CRC_SIZE) = pj_htonl(crc);
+
+    /* Send the ZRTP packet using the slave transport */
+    return (pjmedia_transport_send_rtp(zrtp->slave_tp, buffer, totalLen) == PJ_SUCCESS) ? 1 : 0;
+}
+
+static int32_t zrtp_activateTimer(ZrtpContext* ctx, int32_t time)
+{
+    pj_time_val timeout;
+    pj_status_t status;
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    timeout.sec = time / 1000;
+    timeout.msec = time % 1000;
+
+    pj_timer_entry_init(&zrtp->timeoutEntry, 0, zrtp, &timer_callback);
+    status = pj_timer_heap_schedule(zrtp->timer_heap, &zrtp->timeoutEntry, &timeout);
+    if (status == PJ_SUCCESS)
+        return 1;
+    else
+        return 0;
+}
+
+static int32_t zrtp_cancelTimer(ZrtpContext* ctx)
+{
+    pj_status_t status;
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    status = pj_timer_heap_cancel(zrtp->timer_heap, &zrtp->timeoutEntry);
+    if (status == PJ_SUCCESS)
+        return 1;
+    else
+        return 0;
+}
+
+static void zrtp_sendInfo(ZrtpContext* ctx, int32_t severity, int32_t subCode)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (zrtp->cb.show_message)
+        zrtp->cb.show_message(&zrtp->base, severity, subCode);
+
+}
+
+static int32_t zrtp_srtpSecretsReady(ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+    
+    ZsrtpContext* recvCrypto;
+    ZsrtpContext* senderCrypto;
+    ZsrtpContextCtrl* recvCryptoCtrl;
+    ZsrtpContextCtrl* senderCryptoCtrl;
+    int cipher;
+    int authn;
+    int authKeyLen;
+    //    int srtcpAuthTagLen;
+    
+    if (secrets->authAlgorithm == zrtp_Sha1) {
+        authn = SrtpAuthenticationSha1Hmac;
+        authKeyLen = 20;
+        //        srtcpAuthTagLen = 80;   // Always 80 bit for SRTCP / SHA1
+    }
+    
+    if (secrets->authAlgorithm == zrtp_Skein) {
+        authn = SrtpAuthenticationSkeinHmac;
+        authKeyLen = 32;
+        //        srtcpAuthTagLen = 64;   // Always 64 bit for SRTCP / Skein
+    }
+    
+    if (secrets->symEncAlgorithm == zrtp_Aes)
+        cipher = SrtpEncryptionAESCM;
+    
+    if (secrets->symEncAlgorithm == zrtp_TwoFish)
+        cipher = SrtpEncryptionTWOCM;
+    
+    if (part == ForSender) {
+        // To encrypt packets: intiator uses initiator keys,
+        // responder uses responder keys
+        // Create a "half baked" crypto context first and store it. This is
+        // the main crypto context for the sending part of the connection.
+        if (secrets->role == Initiator) {
+            senderCrypto = zsrtp_CreateWrapper(zrtp->localSSRC,
+                                               0,
+                                               0L,                                      // keyderivation << 48,
+                                               cipher,                                  // encryption algo
+                                               authn,                                   // authtentication algo
+                                               (unsigned char*)secrets->keyInitiator,   // Master Key
+                                               secrets->initKeyLen / 8,                 // Master Key length
+                                               (unsigned char*)secrets->saltInitiator,  // Master Salt
+                                               secrets->initSaltLen / 8,                // Master Salt length
+                                               secrets->initKeyLen / 8,                 // encryption keyl
+                                               authKeyLen,                              // authentication key len
+                                               secrets->initSaltLen / 8,                // session salt len
+                                               secrets->srtpAuthTagLen / 8);            // authentication tag lenA
+            
+            senderCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->localSSRC,
+                                                       cipher,                                    // encryption algo
+                                                       authn,                                     // authtication algo
+                                                       (unsigned char*)secrets->keyInitiator,     // Master Key
+                                                       secrets->initKeyLen / 8,                   // Master Key length
+                                                       (unsigned char*)secrets->saltInitiator,    // Master Salt
+                                                       secrets->initSaltLen / 8,                  // Master Salt length
+                                                       secrets->initKeyLen / 8,                   // encryption keyl
+                                                       authKeyLen,                                // authentication key len
+                                                       secrets->initSaltLen / 8,                  // session salt len
+                                                       secrets->srtpAuthTagLen / 8);              // authentication tag len
+            //                                                              srtcpAuthTagLen / 8);                      // authentication tag len
+        }
+        else {
+            senderCrypto = zsrtp_CreateWrapper(zrtp->localSSRC,
+                                               0,
+                                               0L,                                      // keyderivation << 48,
+                                               cipher,                                  // encryption algo
+                                               authn,                                   // authtentication algo
+                                               (unsigned char*)secrets->keyResponder,   // Master Key
+                                               secrets->respKeyLen / 8,                 // Master Key length
+                                               (unsigned char*)secrets->saltResponder,  // Master Salt
+                                               secrets->respSaltLen / 8,                // Master Salt length
+                                               secrets->respKeyLen / 8,                 // encryption keyl
+                                               authKeyLen,                              // authentication key len
+                                               secrets->respSaltLen / 8,                // session salt len
+                                               secrets->srtpAuthTagLen / 8);            // authentication tag len
+            
+            senderCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->localSSRC,
+                                                       cipher,                                    // encryption algo
+                                                       authn,                                     // authtication algo
+                                                       (unsigned char*)secrets->keyResponder,     // Master Key
+                                                       secrets->respKeyLen / 8,                   // Master Key length
+                                                       (unsigned char*)secrets->saltResponder,    // Master Salt
+                                                       secrets->respSaltLen / 8,                  // Master Salt length
+                                                       secrets->respKeyLen / 8,                   // encryption keyl
+                                                       authKeyLen,                                // authentication key len
+                                                       secrets->respSaltLen / 8,                  // session salt len
+                                                       secrets->srtpAuthTagLen / 8);              // authentication tag len
+            //                                                              srtcpAuthTagLen / 8);                      // authentication tag len
+        }
+        if (senderCrypto == NULL) {
+            return 0;
+        }
+        // Create a SRTP crypto context for real SSRC sender stream.
+        // Note: key derivation can be done at this time only if the
+        // key derivation rate is 0 (disabled). For ZRTP this is the
+        // case: the key derivation is defined as 2^48
+        // which is effectively 0.
+        zsrtp_deriveSrtpKeys(senderCrypto, 0L);
+        zrtp->srtpSend = senderCrypto;
+        
+        zsrtp_deriveSrtpKeysCtrl(senderCryptoCtrl);
+        zrtp->srtcpSend = senderCryptoCtrl;
+    }
+    if (part == ForReceiver) {
+        // To decrypt packets: intiator uses responder keys,
+        // responder initiator keys
+        // See comment above.
+        if (secrets->role == Initiator) {
+            recvCrypto = zsrtp_CreateWrapper(zrtp->peerSSRC,
+                                             0,
+                                             0L,                                      // keyderivation << 48,
+                                             cipher,                                  // encryption algo
+                                             authn,                                   // authtentication algo
+                                             (unsigned char*)secrets->keyResponder,   // Master Key
+                                             secrets->respKeyLen / 8,                 // Master Key length
+                                             (unsigned char*)secrets->saltResponder,  // Master Salt
+                                             secrets->respSaltLen / 8,                // Master Salt length
+                                             secrets->respKeyLen / 8,                 // encryption keyl
+                                             authKeyLen,                              // authentication key len
+                                             secrets->respSaltLen / 8,                // session salt len
+                                             secrets->srtpAuthTagLen / 8);            // authentication tag len
+            
+            recvCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->peerSSRC,
+                                                     cipher,                                    // encryption algo
+                                                     authn,                                     // authtication algo
+                                                     (unsigned char*)secrets->keyResponder,     // Master Key
+                                                     secrets->respKeyLen / 8,                   // Master Key length
+                                                     (unsigned char*)secrets->saltResponder,    // Master Salt
+                                                     secrets->respSaltLen / 8,                  // Master Salt length
+                                                     secrets->respKeyLen / 8,                   // encryption keyl
+                                                     authKeyLen,                                // authentication key len
+                                                     secrets->respSaltLen / 8,                  // session salt len
+                                                     secrets->srtpAuthTagLen / 8);              // authentication tag len
+            //                                                            srtcpAuthTagLen / 8);                      // authentication tag len
+        }
+        else {
+            recvCrypto = zsrtp_CreateWrapper(zrtp->peerSSRC,
+                                             0,
+                                             0L,                                      // keyderivation << 48,
+                                             cipher,                                  // encryption algo
+                                             authn,                                   // authtentication algo
+                                             (unsigned char*)secrets->keyInitiator,   // Master Key
+                                             secrets->initKeyLen / 8,                 // Master Key length
+                                             (unsigned char*)secrets->saltInitiator,  // Master Salt
+                                             secrets->initSaltLen / 8,                // Master Salt length
+                                             secrets->initKeyLen / 8,                 // encryption keyl
+                                             authKeyLen,                              // authentication key len
+                                             secrets->initSaltLen / 8,                // session salt len
+                                             secrets->srtpAuthTagLen / 8);            // authentication tag len
+            
+            recvCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->peerSSRC,
+                                                     cipher,                                    // encryption algo
+                                                     authn,                                     // authtication algo
+                                                     (unsigned char*)secrets->keyInitiator,     // Master Key
+                                                     secrets->initKeyLen / 8,                   // Master Key length
+                                                     (unsigned char*)secrets->saltInitiator,    // Master Salt
+                                                     secrets->initSaltLen / 8,                  // Master Salt length
+                                                     secrets->initKeyLen / 8,                   // encryption keyl
+                                                     authKeyLen,                                // authentication key len
+                                                     secrets->initSaltLen / 8,                  // session salt len
+                                                     secrets->srtpAuthTagLen / 8);              // authentication tag len
+            //                                                            srtcpAuthTagLen / 8);                      // authentication tag len
+        }
+        if (recvCrypto == NULL) {
+            return 0;
+        }
+        // Create a SRTP crypto context for real SSRC input stream.
+        // If the sender didn't provide a SSRC just insert the template
+        // into the queue. After we received the first packet the real
+        // crypto context will be created.
+        //
+        // Note: key derivation can be done at this time only if the
+        // key derivation rate is 0 (disabled). For ZRTP this is the
+        // case: the key derivation is defined as 2^48
+        // which is effectively 0.
+        zsrtp_deriveSrtpKeys(recvCrypto, 0L);
+        zrtp->srtpReceive = recvCrypto;
+        
+        zsrtp_deriveSrtpKeysCtrl(recvCryptoCtrl);
+        zrtp->srtcpReceive = recvCryptoCtrl;
+    }
+    return 1;
+}
+
+static void zrtp_srtpSecretsOff(ZrtpContext* ctx, int32_t part)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (part == ForSender)
+    {
+        zsrtp_DestroyWrapper(zrtp->srtpSend);
+        zsrtp_DestroyWrapperCtrl(zrtp->srtcpSend);
+        zrtp->srtpSend = NULL;
+        zrtp->srtcpSend = NULL;
+    }
+    if (part == ForReceiver)
+    {
+        zsrtp_DestroyWrapper(zrtp->srtpReceive);
+        zsrtp_DestroyWrapperCtrl(zrtp->srtcpReceive);
+        zrtp->srtpReceive = NULL;
+        zrtp->srtcpReceive = NULL;
+    }
+
+    if (zrtp->cb.secure_off)
+        zrtp->cb.secure_off(&zrtp->base);
+}
+
+static void zrtp_srtpSecretsOn(ZrtpContext* ctx, char* c, char* s, int32_t verified)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+    int len;
+
+    len = strlen(c);
+    if (len > sizeof(zrtp->cipher) - 1)
+        len = sizeof(zrtp->cipher) - 1;
+    memcpy(zrtp->cipher, c, len);
+    zrtp->cipher[len] = '\0';
+
+    if (zrtp->cb.secure_on)
+        zrtp->cb.secure_on(&zrtp->base, c);
+
+    if (s && strlen(s) > 0 && zrtp->cb.show_sas)
+        zrtp->cb.show_sas(&zrtp->base, s, verified);
+}
+
+static void zrtp_handleGoClear(ZrtpContext* ctx)
+{
+    /* TODO: implement */
+}
+
+static void zrtp_zrtpNegotiationFailed(ZrtpContext* ctx, int32_t severity, int32_t subCode)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (zrtp->cb.negotiation_failed)
+        zrtp->cb.negotiation_failed(&zrtp->base, severity, subCode);
+}
+
+static void zrtp_zrtpNotSuppOther(ZrtpContext* ctx)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (zrtp->cb.not_supported_by_other)
+        zrtp->cb.not_supported_by_other(&zrtp->base);
+}
+
+static void zrtp_synchEnter(ZrtpContext* ctx)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+    pj_mutex_lock(zrtp->zrtpMutex);
+}
+
+static void zrtp_synchLeave(ZrtpContext* ctx)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+    pj_mutex_unlock(zrtp->zrtpMutex);
+}
+
+static void zrtp_zrtpAskEnrollment(ZrtpContext* ctx, int32_t info)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (zrtp->cb.ask_enrollment)
+        zrtp->cb.ask_enrollment(&zrtp->base, info);
+}
+
+static void zrtp_zrtpInformEnrollment(ZrtpContext* ctx, int32_t info)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (zrtp->cb.inform_enrollment)
+        zrtp->cb.inform_enrollment(&zrtp->base, info);
+}
+
+static void zrtp_signSAS(ZrtpContext* ctx, uint8_t* sasHash)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (zrtp->cb.sign_sas)
+        zrtp->cb.sign_sas(&zrtp->base, sasHash);
+}
+
+static int32_t zrtp_checkSASSignature(ZrtpContext* ctx, uint8_t* sasHash)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData;
+
+    if (zrtp->cb.check_sas_signature)
+        return zrtp->cb.check_sas_signature(&zrtp->base, sasHash);
+    return 0;
+}
+
+/*
+ * Implement the specific ZRTP transport functions
+ */
+PJ_DEF(void) pjmedia_transport_zrtp_setEnableZrtp(pjmedia_transport *tp, pj_bool_t onOff)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    zrtp->enableZrtp = onOff;
+}
+
+PJ_DEF(pj_bool_t) pjmedia_transport_zrtp_isEnableZrtp(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    PJ_ASSERT_RETURN(tp, PJ_FALSE);
+
+    return zrtp->enableZrtp;
+
+}
+
+PJ_DEF(void) pjmedia_transport_zrtp_startZrtp(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp && zrtp->zrtpCtx);
+
+    zrtp_startZrtpEngine(zrtp->zrtpCtx);
+    zrtp->started = 1;
+}
+
+PJ_DEF(void) pjmedia_transport_zrtp_stopZrtp(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp && zrtp->zrtpCtx);
+
+    zrtp_stopZrtpEngine(zrtp->zrtpCtx);
+    zrtp->started = 0;
+}
+
+PJ_DEF(void) pjmedia_transport_zrtp_setLocalSSRC(pjmedia_transport *tp, uint32_t ssrc)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    zrtp->localSSRC = ssrc;
+}
+
+PJ_DEF(pj_bool_t) pjmedia_transport_zrtp_isMitmMode(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    return zrtp->mitmMode;
+}
+
+PJ_DEF(void) pjmedia_transport_zrtp_setMitmMode(pjmedia_transport *tp, pj_bool_t mitmMode)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    zrtp->mitmMode = mitmMode;
+}
+
+PJ_DEF(ZrtpContext*) pjmedia_transport_zrtp_getZrtpContext(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    PJ_ASSERT_RETURN(tp, NULL);
+
+    return zrtp->zrtpCtx;
+}
+
+PJ_DEF(void) pjmedia_transport_zrtp_setSASVerified(pjmedia_transport *tp, pj_bool_t verified)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    if (verified)
+        zrtp_SASVerified(zrtp->zrtpCtx);
+    else
+        zrtp_resetSASVerified(zrtp->zrtpCtx);
+}
+
+PJ_DEF(int) pjmedia_transport_zrtp_getPeerZid(pjmedia_transport *tp, unsigned char* data)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    return zrtp_getPeerZid(zrtp->zrtpCtx, data);
+}
+
+PJ_DEF(char*) pjmedia_transport_zrtp_getPeerName(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    return zrtp_getPeerName(zrtp->zrtpCtx);
+}
+
+PJ_DEF(void) pjmedia_transport_zrtp_putPeerName(pjmedia_transport *tp, const char *name)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    zrtp_putPeerName(zrtp->zrtpCtx, name);
+}
+
+PJ_DEF(char*) pjmedia_transport_zrtp_getMultiStreamParameters(pjmedia_transport *tp, pj_int32_t *length)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_assert(tp);
+
+    return zrtp_getMultiStrParams(zrtp->zrtpCtx, length);
+}
+
+PJ_DEF(void) pjmedia_transport_zrtp_setMultiStreamParameters(pjmedia_transport *tp, const char *parameters, pj_int32_t length, pjmedia_transport *master_tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    struct tp_zrtp *master_zrtp = (struct tp_zrtp*)master_tp;
+    pj_assert(tp);
+    pj_assert(master_tp);
+
+    zrtp_setMultiStrParams(zrtp->zrtpCtx, (char*) parameters, length, master_zrtp->zrtpCtx);
+}
+
+/*
+ * get_info() is called to get the transport addresses to be put
+ * in SDP c= line and a=rtcp line.
+ */
+static pj_status_t transport_get_info(pjmedia_transport *tp,
+                                      pjmedia_transport_info *info)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pjmedia_zrtp_info zrtp_info;
+    int spc_info_idx;
+
+    PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info->specific_info_cnt <
+                     PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT, PJ_ETOOMANY);
+
+    zrtp_info.active = zrtp_inState(zrtp->zrtpCtx, SecureState) ? PJ_TRUE : PJ_FALSE;
+    if (zrtp_info.active)
+        memcpy(zrtp_info.cipher, zrtp->cipher, sizeof(zrtp->cipher));
+    else
+        zrtp_info.cipher[0] = '\0';
+
+    spc_info_idx = info->specific_info_cnt++;
+    info->spc_info[spc_info_idx].type = PJMEDIA_TRANSPORT_TYPE_ZRTP;
+
+    pj_memcpy(&info->spc_info[spc_info_idx].buffer, &zrtp_info,
+              sizeof(zrtp_info));
+
+    return pjmedia_transport_get_info(zrtp->slave_tp, info);
+}
+
+/* This is our RTP callback, that is called by the slave transport when it
+ * receives RTP packet.
+ */
+static void transport_rtp_cb(void *user_data, void *pkt, pj_ssize_t size)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)user_data;
+
+    pj_uint8_t* buffer = (pj_uint8_t*)pkt;
+    int32_t newLen = 0;
+    pj_status_t rc = PJ_SUCCESS;
+
+    pj_assert(zrtp && zrtp->stream_rtcp_cb && pkt);
+
+    // check if this could be a real RTP/SRTP packet.
+    if ((*buffer & 0xf0) != 0x10)
+    {
+        //  Could be real RTP, check if we are in secure mode
+        if (zrtp->srtpReceive == NULL || size < 0)
+        {
+            zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt, size);
+        }
+        else
+        {
+            rc = zsrtp_unprotect(zrtp->srtpReceive, pkt, size, &newLen);
+            if (rc == 1)
+            {
+                zrtp->unprotect++;
+                zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt,
+                                    newLen);
+                zrtp->unprotect_err = 0;
+            }
+            else
+            {
+                if (zrtp->cb.show_message)
+                {
+                    if (rc == -1)
+                        zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningSRTPauthError);
+                    else
+                        zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningSRTPreplayError);
+                }
+                zrtp->unprotect_err = rc;
+                /* We failed to decrypt the packet, but forward it regardless to the slave
+                 * transport, it might not have been encrypted after all */
+                zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt, size);
+            }
+        }
+        if (!zrtp->started && zrtp->enableZrtp)
+            pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);
+
+        return;
+    }
+
+    // We assume all other packets are ZRTP packets here. Process
+    // if ZRTP processing is enabled. Because valid RTP packets are
+    // already handled we delete any packets here after processing.
+    if (zrtp->enableZrtp && zrtp->zrtpCtx != NULL)
+    {
+        // Get CRC value into crc (see above how to compute the offset)
+        pj_uint16_t temp = size - CRC_SIZE;
+        pj_uint32_t crc = *(uint32_t*)(buffer + temp);
+        crc = pj_ntohl(crc);
+
+        if (!zrtp_CheckCksum(buffer, temp, crc))
+        {
+            if (zrtp->cb.show_message)
+                zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningCRCmismatch);
+            return;
+        }
+
+        pj_uint32_t magic = *(pj_uint32_t*)(buffer + 4);
+        magic = pj_ntohl(magic);
+
+        // Check if it is really a ZRTP packet, return, no further processing
+        if (magic != ZRTP_MAGIC)
+            return;
+
+        // cover the case if the other party sends _only_ ZRTP packets at the
+        // beginning of a session. Start ZRTP in this case as well.
+        if (!zrtp->started)
+        {
+            pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);
+        }
+        // this now points beyond the undefined and length field.
+        // We need them, thus adjust
+        unsigned char* zrtpMsg = (buffer + 12);
+
+        // store peer's SSRC in host order, used when creating the CryptoContext
+        zrtp->peerSSRC = *(pj_uint32_t*)(buffer + 8);
+        zrtp->peerSSRC = pj_ntohl(zrtp->peerSSRC);
+        zrtp_processZrtpMessage(zrtp->zrtpCtx, zrtpMsg, zrtp->peerSSRC, size);
+    }
+}
+
+
+/* This is our RTCP callback, that is called by the slave transport when it
+ * receives RTCP packet.
+ */
+static void transport_rtcp_cb(void *user_data, void *pkt, pj_ssize_t size)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)user_data;
+    int32_t newLen = 0;
+    pj_status_t rc = PJ_SUCCESS;
+    
+    pj_assert(zrtp && zrtp->stream_rtcp_cb);
+    
+    if (zrtp->srtcpReceive == NULL || size < 0)
+    {
+        zrtp->stream_rtcp_cb(zrtp->stream_user_data, pkt, size);
+    }
+    else
+    {
+        rc = zsrtp_unprotectCtrl(zrtp->srtcpReceive, pkt, size, &newLen);
+
+        if (rc == 1)
+        {
+            /* Call stream's callback */
+            zrtp->stream_rtcp_cb(zrtp->stream_user_data, pkt, newLen);
+        }
+        else
+        {
+            // Testing: print some error output
+        }
+    }
+}
+
+
+/*
+ * attach() is called by stream to register callbacks that we should
+ * call on receipt of RTP and RTCP packets.
+ */
+static pj_status_t transport_attach(pjmedia_transport *tp,
+                                    void *user_data,
+                                    const pj_sockaddr_t *rem_addr,
+                                    const pj_sockaddr_t *rem_rtcp,
+                                    unsigned addr_len,
+                                    void (*rtp_cb)(void*,
+                                                   void*,
+                                                   pj_ssize_t),
+                                    void (*rtcp_cb)(void*,
+                                                    void*,
+                                                    pj_ssize_t))
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
+
+    /* In this example, we will save the stream information and callbacks
+     * to our structure, and we will register different RTP/RTCP callbacks
+     * instead.
+     */
+    pj_assert(zrtp->stream_user_data == NULL);
+    zrtp->stream_user_data = user_data;
+    zrtp->stream_rtp_cb = rtp_cb;
+    zrtp->stream_rtcp_cb = rtcp_cb;
+
+    status = pjmedia_transport_attach(zrtp->slave_tp, zrtp, rem_addr,
+                                      rem_rtcp, addr_len, &transport_rtp_cb,
+                                      &transport_rtcp_cb);
+    if (status != PJ_SUCCESS)
+    {
+        zrtp->stream_user_data = NULL;
+        zrtp->stream_rtp_cb = NULL;
+        zrtp->stream_rtcp_cb = NULL;
+        return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * detach() is called when the media is terminated, and the stream is
+ * to be disconnected from us.
+ */
+static void transport_detach(pjmedia_transport *tp, void *strm)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+
+    PJ_UNUSED_ARG(strm);
+    PJ_ASSERT_ON_FAIL(tp, return);
+
+    if (zrtp->stream_user_data != NULL)
+    {
+        pjmedia_transport_detach(zrtp->slave_tp, zrtp);
+        zrtp->stream_user_data = NULL;
+        zrtp->stream_rtp_cb = NULL;
+        zrtp->stream_rtcp_cb = NULL;
+    }
+}
+
+
+/*
+ * send_rtp() is called to send RTP packet. The "pkt" and "size" argument
+ * contain both the RTP header and the payload.
+ */
+static pj_status_t transport_send_rtp(pjmedia_transport *tp,
+                                      const void *pkt,
+                                      pj_size_t size)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_uint32_t* pui = (pj_uint32_t*)pkt;
+    int32_t newLen = 0;
+    pj_status_t rc = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(tp && pkt, PJ_EINVAL);
+
+
+    if (!zrtp->started && zrtp->enableZrtp)
+    {
+        if (zrtp->localSSRC == 0)
+            zrtp->localSSRC = pj_ntohl(pui[2]);   /* Learn own SSRC before starting ZRTP */
+
+        pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);
+    }
+
+    if (zrtp->srtpSend == NULL)
+    {
+        return pjmedia_transport_send_rtp(zrtp->slave_tp, pkt, size);
+    }
+    else
+    {
+        if (size+80 > MAX_RTP_BUFFER_LEN)
+            return PJ_ETOOBIG;
+
+        pj_memcpy(zrtp->sendBuffer, pkt, size);
+        rc = zsrtp_protect(zrtp->srtpSend, zrtp->sendBuffer, size, &newLen);
+        zrtp->protect++;
+
+        if (rc == 1)
+            return pjmedia_transport_send_rtp(zrtp->slave_tp, zrtp->sendBuffer, newLen);
+        else
+            return PJ_EIGNORED;
+    }
+}
+
+
+/*
+ * send_rtcp() is called to send RTCP packet. The "pkt" and "size" argument
+ * contain the RTCP packet.
+ */
+static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
+                                       const void *pkt,
+                                       pj_size_t size)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_status_t rc = PJ_SUCCESS;
+    int32_t newLen = 0;
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    /* You may do some processing to the RTCP packet here if you want. */
+    if (zrtp->srtcpSend == NULL)
+    {
+        return pjmedia_transport_send_rtcp(zrtp->slave_tp, pkt, size);
+    }
+    else
+    {
+        if (size+80 > MAX_RTCP_BUFFER_LEN)
+            return PJ_ETOOBIG;
+
+        pj_memcpy(zrtp->sendBufferCtrl, pkt, size);
+        rc = zsrtp_protectCtrl(zrtp->srtcpSend, zrtp->sendBufferCtrl, size, &newLen);
+
+        if (rc == 1)
+            return pjmedia_transport_send_rtcp(zrtp->slave_tp, zrtp->sendBufferCtrl, newLen);
+        else
+            return PJ_EIGNORED;
+    }
+
+    /* Send the packet using the slave transport */
+//    return pjmedia_transport_send_rtcp(zrtp->slave_tp, pkt, size);
+}
+
+
+/*
+ * This is another variant of send_rtcp(), with the alternate destination
+ * address in the argument.
+ */
+static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
+                                        const pj_sockaddr_t *addr,
+                                        unsigned addr_len,
+                                        const void *pkt,
+                                        pj_size_t size)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    return pjmedia_transport_send_rtcp2(zrtp->slave_tp, addr, addr_len,
+                                        pkt, size);
+}
+
+/*
+ * The media_create() is called when the transport is about to be used for
+ * a new call.
+ */
+static pj_status_t transport_media_create(pjmedia_transport *tp,
+        pj_pool_t *sdp_pool,
+        unsigned options,
+        const pjmedia_sdp_session *rem_sdp,
+        unsigned media_index)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    /* if "rem_sdp" is not NULL, it means we are UAS. You may do some
+     * inspections on the incoming SDP to verify that the SDP is acceptable
+     * for us. If the SDP is not acceptable, we can reject the SDP by
+     * returning non-PJ_SUCCESS.
+     */
+    if (rem_sdp)
+    {
+        /* Do your stuff.. */
+    }
+
+    /* Once we're done with our initialization, pass the call to the
+     * slave transports to let it do it's own initialization too.
+     */
+    return pjmedia_transport_media_create(zrtp->slave_tp, sdp_pool, options,
+                                          rem_sdp, media_index);
+}
+
+/*
+ * The encode_sdp() is called when we're about to send SDP to remote party,
+ * either as SDP offer or as SDP answer.
+ */
+static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
+                                        pj_pool_t *sdp_pool,
+                                        pjmedia_sdp_session *local_sdp,
+                                        const pjmedia_sdp_session *rem_sdp,
+                                        unsigned media_index)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    int32_t numVersions, i;
+    
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+    
+    /* If "rem_sdp" is not NULL, it means we're encoding SDP answer. You may
+     * do some more checking on the SDP's once again to make sure that
+     * everything is okay before we send SDP.
+     */
+    if (rem_sdp)
+    {
+        /* Do checking stuffs here.. */
+    }
+
+    /* Add zrtp-hash attributes to both INVITE and 200 OK. */
+    numVersions = zrtp_getNumberSupportedVersions(zrtp->zrtpCtx);
+    for (i = 0; i < numVersions; i++) {
+        char *zrtp_hello_hash = zrtp_getHelloHash(zrtp->zrtpCtx, i);
+        if (zrtp_hello_hash && *zrtp_hello_hash) {
+            int zrtp_hello_hash_len = strlen(zrtp_hello_hash);
+            pj_str_t *zrtp_hash_str = PJ_POOL_ALLOC_T(sdp_pool, pj_str_t);
+            pjmedia_sdp_attr *zrtp_hash = NULL;
+
+            zrtp_hash_str->ptr = zrtp_hello_hash;
+            zrtp_hash_str->slen = zrtp_hello_hash_len;
+
+            zrtp_hash = pjmedia_sdp_attr_create(sdp_pool, "zrtp-hash", zrtp_hash_str);
+            if (zrtp_hash && 
+                pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count, local_sdp->media[media_index]->attr, zrtp_hash) == PJ_SUCCESS) {
+                PJ_LOG(4, (THIS_FILE, "attribute added: a=zrtp-hash:%s", zrtp_hello_hash));
+            }
+            else {
+                PJ_LOG(4, (THIS_FILE, "error adding attribute: a=zrtp-hash:%s", zrtp_hello_hash));
+            }
+        }
+    }
+
+    /* You may do anything to the local_sdp, e.g. adding new attributes, or
+     * even modifying the SDP if you want.
+     */
+    if (0)
+    {
+        /* Say we add a proprietary attribute here.. */
+        pjmedia_sdp_attr *my_attr;
+
+        my_attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr);
+        pj_strdup2(sdp_pool, &my_attr->name, "X-zrtp");
+        pj_strdup2(sdp_pool, &my_attr->value, "some value");
+
+        pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count,
+                             local_sdp->media[media_index]->attr,
+                             my_attr);
+    }
+
+    /* And then pass the call to slave transport to let it encode its
+     * information in the SDP. You may choose to call encode_sdp() to slave
+     * first before adding your custom attributes if you want.
+     */
+    return pjmedia_transport_encode_sdp(zrtp->slave_tp, sdp_pool, local_sdp, rem_sdp, media_index);
+}
+
+/*
+ * The media_start() is called once both local and remote SDP have been
+ * negotiated successfully, and the media is ready to start. Here we can start
+ * committing our processing.
+ */
+static pj_status_t transport_media_start(pjmedia_transport *tp,
+        pj_pool_t *pool,
+        const pjmedia_sdp_session *local_sdp,
+        const pjmedia_sdp_session *rem_sdp,
+        unsigned media_index)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    /* Do something.. */
+
+    /* And pass the call to the slave transport */
+    return pjmedia_transport_media_start(zrtp->slave_tp, pool, local_sdp,
+                                         rem_sdp, media_index);
+}
+
+/*
+ * The media_stop() is called when media has been stopped.
+ */
+static pj_status_t transport_media_stop(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    /* Do something.. */
+    PJ_LOG(4, (THIS_FILE, "Media stop - encrypted packets: %ld, decrypted packets: %ld",
+               zrtp->protect, zrtp->unprotect));
+
+    /* And pass the call to the slave transport */
+    return pjmedia_transport_media_stop(zrtp->slave_tp);
+}
+
+/*
+ * simulate_lost() is called to simulate packet lost
+ */
+static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
+        pjmedia_dir dir,
+        unsigned pct_lost)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    return pjmedia_transport_simulate_lost(zrtp->slave_tp, dir, pct_lost);
+}
+
+/*
+ * destroy() is called when the transport is no longer needed.
+ */
+static pj_status_t transport_destroy(pjmedia_transport *tp)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+
+    PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+    PJ_LOG(4, (THIS_FILE, "Destroy - encrypted packets: %ld, decrypted packets: %ld",
+               zrtp->protect, zrtp->unprotect));
+
+    /* close the slave transport in case */
+    if (zrtp->close_slave && zrtp->slave_tp)
+        pjmedia_transport_close(zrtp->slave_tp);
+
+    /* Self destruct.. */
+    zrtp_stopZrtpEngine(zrtp->zrtpCtx);
+    zrtp_DestroyWrapper(zrtp->zrtpCtx);
+    zrtp->zrtpCtx = NULL;
+
+    /* In case mutex is being acquired by other thread */
+    pj_mutex_lock(zrtp->zrtpMutex);
+    pj_mutex_unlock(zrtp->zrtpMutex);
+    pj_mutex_destroy(zrtp->zrtpMutex);
+
+    pj_pool_release(zrtp->pool);
+
+    return PJ_SUCCESS;
+}
+
+
+
+
--- pjsip/pjmedia/src/pjmedia/transport_zrtp.c	2021-02-05 12:38:15.913793992 +0100
+++ pjsip/pjmedia/src/pjmedia/transport_zrtp.c	2021-03-11 09:20:35.134754145 +0100
@@ -78,6 +78,8 @@
         pjmedia_dir dir,
         unsigned pct_lost);
 static pj_status_t transport_destroy(pjmedia_transport *tp);
+static pj_status_t transport_attach2(pjmedia_transport *tp,
+                           pjmedia_transport_attach_param *att_param);
 
 
 /* The transport operations */
@@ -94,7 +96,8 @@
     &transport_media_start,
     &transport_media_stop,
     &transport_simulate_lost,
-    &transport_destroy
+    &transport_destroy,
+    &transport_attach2
 };
 
 /* The transport zrtp instance */
@@ -108,6 +111,7 @@
     void (*stream_rtp_cb)(void *user_data,
                           void *pkt,
                           pj_ssize_t);
+    void (*stream_rtp_cb2)(pjmedia_tp_cb_param *param);
     void (*stream_rtcp_cb)(void *user_data,
                            void *pkt,
                            pj_ssize_t);
@@ -794,7 +798,7 @@
     int32_t newLen = 0;
     pj_status_t rc = PJ_SUCCESS;
 
-    pj_assert(zrtp && zrtp->stream_rtcp_cb && pkt);
+    pj_assert(zrtp && zrtp->stream_rtp_cb && pkt);
 
     // check if this could be a real RTP/SRTP packet.
     if ((*buffer & 0xf0) != 0x10)
@@ -876,6 +880,106 @@
     }
 }
 
+/* This is our RTP callback, that is called by the slave transport when it
+ * receives RTP packet.
+ */
+static void transport_rtp_cb2(pjmedia_tp_cb_param *param)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)param->user_data;
+    pj_uint8_t* buffer = (pj_uint8_t*)param->pkt;
+    int32_t newLen = 0;
+    pj_status_t rc = PJ_SUCCESS;
+    pjmedia_tp_cb_param cbparam;
+ 
+    pj_assert(zrtp && zrtp->stream_rtp_cb2 && param->pkt);
+ 
+    // check if this could be a real RTP/SRTP packet.
+    if ((*buffer & 0xf0) != 0x10)
+    {
+        //  Could be real RTP, check if we are in secure mode
+        if (zrtp->srtpReceive == NULL || param->size < 0)
+        {
+            pj_memcpy(&cbparam, param, sizeof(cbparam));
+            cbparam.user_data = zrtp->stream_user_data;
+            zrtp->stream_rtp_cb2(&cbparam);
+        }
+        else
+        {
+            rc = zsrtp_unprotect(zrtp->srtpReceive, param->pkt, param->size, &newLen);
+            if (rc == 1)
+            {
+                zrtp->unprotect++;
+               param->size = newLen;
+                pj_memcpy(&cbparam, param, sizeof(cbparam));
+                cbparam.user_data = zrtp->stream_user_data;
+                zrtp->stream_rtp_cb2(&cbparam);
+                //zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt,
+                //                    newLen);
+                zrtp->unprotect_err = 0;
+            }
+            else
+            {
+                if (zrtp->cb.show_message)
+                {
+                    if (rc == -1)
+                        zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningSRTPauthError);
+                    else
+                        zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningSRTPreplayError);
+                }
+                zrtp->unprotect_err = rc;
+                /* We failed to decrypt the packet, but forward it regardless to the slave
+                 * transport, it might not have been encrypted after all */
+                pj_memcpy(&cbparam, param, sizeof(cbparam));
+                cbparam.user_data = zrtp->stream_user_data;
+                zrtp->stream_rtp_cb2(&cbparam);
+            }
+        }
+        if (!zrtp->started && zrtp->enableZrtp)
+            pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);
+
+        return;
+    }
+
+    // We assume all other packets are ZRTP packets here. Process
+    // if ZRTP processing is enabled. Because valid RTP packets are
+    // already handled we delete any packets here after processing.
+    if (zrtp->enableZrtp && zrtp->zrtpCtx != NULL)
+    {
+        // Get CRC value into crc (see above how to compute the offset)
+        pj_uint16_t temp = param->size - CRC_SIZE;
+        pj_uint32_t crc = *(uint32_t*)(buffer + temp);
+        crc = pj_ntohl(crc);
+
+        if (!zrtp_CheckCksum(buffer, temp, crc))
+        {
+            if (zrtp->cb.show_message)
+                zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningCRCmismatch);
+            return;
+        }
+
+        pj_uint32_t magic = *(pj_uint32_t*)(buffer + 4);
+        magic = pj_ntohl(magic);
+
+        // Check if it is really a ZRTP packet, return, no further processing
+        if (magic != ZRTP_MAGIC)
+            return;
+
+        // cover the case if the other party sends _only_ ZRTP packets at the
+        // beginning of a session. Start ZRTP in this case as well.
+        if (!zrtp->started)
+        {
+            pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp);
+        }
+        // this now points beyond the undefined and length field.
+        // We need them, thus adjust
+        unsigned char* zrtpMsg = (buffer + 12);
+
+        // store peer's SSRC in host order, used when creating the CryptoContext
+        zrtp->peerSSRC = *(pj_uint32_t*)(buffer + 8);
+        zrtp->peerSSRC = pj_ntohl(zrtp->peerSSRC);
+        zrtp_processZrtpMessage(zrtp->zrtpCtx, zrtpMsg, zrtp->peerSSRC, param->size);
+    }
+}
 
 /* This is our RTCP callback, that is called by the slave transport when it
  * receives RTCP packet.
@@ -1256,6 +1359,45 @@
     return PJ_SUCCESS;
 }
 
+/*
+ * attach2() is called by stream to register callbacks that we should
+ * call on receipt of RTP and RTCP packets.
+ */
+static pj_status_t transport_attach2(pjmedia_transport *tp,
+                                     pjmedia_transport_attach_param *att_param)
+{
+    struct tp_zrtp *zrtp = (struct tp_zrtp*)tp;
+    pj_status_t status;
+
+    /* In this example, we will save the stream information and callbacks
+     * to our structure, and we will register different RTP/RTCP callbacks
+     * instead.
+     */
+    pj_assert(zrtp->stream_user_data == NULL);
+    zrtp->stream_user_data = att_param->user_data;
+    if (att_param->rtp_cb2) {
+        zrtp->stream_rtp_cb2 = att_param->rtp_cb2;
+    } else {
+        zrtp->stream_rtp_cb = att_param->rtp_cb;
+    }
+    zrtp->stream_rtcp_cb = att_param->rtcp_cb;
+
+    att_param->rtp_cb2 = &transport_rtp_cb2;
+    att_param->rtp_cb = NULL;    
+    att_param->rtcp_cb = &transport_rtcp_cb;
+    att_param->user_data = zrtp;
+        
+    status = pjmedia_transport_attach2(zrtp->slave_tp, att_param);
+    if (status != PJ_SUCCESS) {
+	zrtp->stream_user_data = NULL;
+	zrtp->stream_rtp_cb = NULL;
+	zrtp->stream_rtp_cb2 = NULL;
+	zrtp->stream_rtcp_cb = NULL;
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
 

 
