/* $Id$ */
/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * 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 2 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 
 */
#ifndef __PJMEDIA_TRANSPORT_SRTP_H__
#define __PJMEDIA_TRANSPORT_SRTP_H__

/**
 * @file transport_srtp.h
 * @brief Secure RTP (SRTP) transport.
 */

#include <pjmedia/transport.h>


/**
 * @defgroup PJMEDIA_TRANSPORT_SRTP Secure RTP (SRTP) Media Transport
 * @ingroup PJMEDIA_TRANSPORT
 * @brief Media transport adapter to add SRTP feature to existing transports
 * @{
 *
 * This module implements SRTP as described by RFC 3711, using RFC 4568 as
 * key exchange method. It implements \ref PJMEDIA_TRANSPORT to integrate
 * with the rest of PJMEDIA framework.
 *
 * As we know, media transport is separated from the stream object (which 
 * does the encoding/decoding of PCM frames, (de)packetization of RTP/RTCP 
 * packets, and de-jitter buffering). The connection between stream and media
 * transport is established when the stream is created (we need to specify 
 * media transport during stream creation), and the interconnection can be 
 * depicted from the diagram below:
 *
   \image html media-transport.PNG

 * I think the diagram above is self-explanatory.
 *
 * SRTP functionality is implemented as some kind of "adapter", which is 
 * plugged between the stream and the actual media transport that does 
 * sending/receiving RTP/RTCP packets. When SRTP is used, the interconnection
 * between stream and transport is like the diagram below:
 *
    \image html media-srtp-transport.PNG

 * So to stream, the SRTP transport behaves as if it is a media transport 
 * (because it is a media transport), and to the media transport it behaves
 * as if it is a stream. The SRTP object then forwards RTP packets back and
 * forth between stream and the actual transport, encrypting/decrypting 
 * the RTP/RTCP packets as necessary.
 * 
 * The neat thing about this design is the SRTP "adapter" then can be used 
 * to encrypt any kind of media transports. We currently have UDP and ICE 
 * media transports that can benefit SRTP, and we could add SRTP to any 
 * media transports that will be added in the future. 
 */

PJ_BEGIN_DECL


/**
 * Crypto option.
 */
typedef enum pjmedia_srtp_crypto_option
{
    /** When this flag is specified, encryption will be disabled. */
    PJMEDIA_SRTP_NO_ENCRYPTION  = 1,

    /** When this flag is specified, authentication will be disabled. */
    PJMEDIA_SRTP_NO_AUTHENTICATION  = 2

} pjmedia_srtp_crypto_option;


/**
 * This structure describes an individual crypto setting.
 */
typedef struct pjmedia_srtp_crypto
{
    /** Optional key. If empty, a random key will be autogenerated. */
    pj_str_t	key;

    /** Crypto name.   */
    pj_str_t	name;

    /** Flags, bitmask from #pjmedia_srtp_crypto_option */
    unsigned	flags;

} pjmedia_srtp_crypto;


/**
 * This enumeration specifies the behavior of the SRTP transport regarding
 * media security offer and answer.
 */
typedef enum pjmedia_srtp_use
{
    /**
     * When this flag is specified, SRTP will be disabled, and the transport
     * will reject RTP/SAVP offer.
     */
    PJMEDIA_SRTP_DISABLED,

    /**
     * When this flag is specified, SRTP setting is unknown. This is to set
     * the initial remote's SRTP usage. It will be set later after remote's
     * policy in the SDP is received.
     */
    PJMEDIA_SRTP_UNKNOWN = PJMEDIA_SRTP_DISABLED,

    /**
     * When this flag is specified, SRTP will be advertised as optional and
     * incoming SRTP offer will be accepted.
     */
    PJMEDIA_SRTP_OPTIONAL,

    /**
     * When this flag is specified, the transport will require that RTP/SAVP
     * media shall be used.
     */
    PJMEDIA_SRTP_MANDATORY

} pjmedia_srtp_use;


/**
 * This enumeration specifies SRTP keying methods.
 */
typedef enum pjmedia_srtp_keying_method
{
    /**
     * Session Description (SDES).
     */
    PJMEDIA_SRTP_KEYING_SDES,

    /**
     * DTLS-SRTP.
     */
    PJMEDIA_SRTP_KEYING_DTLS_SRTP,

    /**
     * Number of keying method.
     */
    PJMEDIA_SRTP_KEYINGS_COUNT

} pjmedia_srtp_keying_method;


/**
 * Structure containing callbacks to receive SRTP notifications.
 */
typedef struct pjmedia_srtp_cb
{
    /**
     * This callback will be called when SRTP negotiation completes. This
     * callback will be invoked when the negotiation is done outside of
     * the SDP signalling, such as in DTLS-SRTP.
     *
     * @param tp	PJMEDIA SRTP transport.
     * @param status	Operation status.
     */
    void    (*on_srtp_nego_complete)(pjmedia_transport *tp,
				     pj_status_t status);

} pjmedia_srtp_cb;


/**
 * RTP sequence rollover counter settings.
 */
typedef struct pjmedia_srtp_roc
{
    /**
     * The synchronization source.
     */
    pj_uint32_t	    ssrc;

    /**
     * The rollover counter.
     */
    pj_uint32_t	    roc;

} pjmedia_srtp_roc;

/**
 * Settings to be given when creating SRTP transport. Application should call
 * #pjmedia_srtp_setting_default() to initialize this structure with its 
 * default values.
 */
typedef struct pjmedia_srtp_setting
{
    /**
     * Specify the usage policy. Default is PJMEDIA_SRTP_OPTIONAL.
     */
    pjmedia_srtp_use		 use;

    /**
     * Specify whether the SRTP transport should close the member transport 
     * when it is destroyed. Default: PJ_TRUE.
     */
    pj_bool_t			 close_member_tp;

    /**
     * Specify the number of crypto suite settings. If set to zero, all
     * available cryptos will be enabled. Default: zero.
     */
    unsigned			 crypto_count;

    /**
     * Specify individual crypto suite setting and its priority order.
     *
     * Notes for DTLS-SRTP keying:
     *  - Currently only supports these cryptos: AES_CM_128_HMAC_SHA1_80,
     *    AES_CM_128_HMAC_SHA1_32, AEAD_AES_256_GCM, and AEAD_AES_128_GCM.
     *  - SRTP key is not configurable.
     */
    pjmedia_srtp_crypto		 crypto[PJMEDIA_SRTP_MAX_CRYPTOS];

    /**
     * Specify the number of enabled keying methods. If set to zero, all
     * keyings will be enabled. Maximum value is PJMEDIA_SRTP_MAX_KEYINGS.
     *
     * Default is zero (all keyings are enabled with priority order:
     * SDES, DTLS-SRTP).
     */
    unsigned			 keying_count;

    /**
     * Specify enabled keying methods and its priority order. Keying method
     * with higher priority will be given earlier chance to process the SDP,
     * for example as currently only one keying is supported in the SDP offer,
     * keying with first priority will be likely used in the SDP offer.
     */
    pjmedia_srtp_keying_method	 keying[PJMEDIA_SRTP_KEYINGS_COUNT];

    /**
     * RTP sequence rollover counter initialization value for incoming
     * direction. This is useful to maintain ROC after media transport
     * recreation such as in IP change scenario.
     */
    pjmedia_srtp_roc		 rx_roc;

    /**
     * The previous value of RTP sequence rollover counter. This is
     * useful in situations when we expect the remote to reset/maintain
     * ROC but for some reason, they don't. Thus, when we encounter
     * SRTP packet unprotect failure during probation, we will retry to
     * unprotect with this ROC value as well.
     * Set prev_rx_roc.ssrc to 0 to disable this feature.
     */
    pjmedia_srtp_roc		 prev_rx_roc;

    /**
     * RTP sequence rollover counter initialization value for outgoing
     * direction. This is useful to maintain ROC after media transport
     * recreation such as in IP change scenario.
     */
    pjmedia_srtp_roc		 tx_roc;

    /**
     * Specify SRTP callback.
     */
    pjmedia_srtp_cb		 cb;

    /**
     * Specify SRTP transport user data.
     */
    void			*user_data;

} pjmedia_srtp_setting;


/**
 * This structure specifies SRTP transport specific info. This will fit
 * into \a buffer field of pjmedia_transport_specific_info.
 */
typedef struct pjmedia_srtp_info
{
    /**
     * Specify whether the SRTP transport is active for SRTP session.
     */
    pj_bool_t			active;

    /**
     * Specify the policy used by the SRTP session for receive direction.
     */
    pjmedia_srtp_crypto		rx_policy;

    /**
     * Specify the policy used by the SRTP session for transmit direction.
     */
    pjmedia_srtp_crypto		tx_policy;

    /**
     * Specify the usage policy.
     */
    pjmedia_srtp_use		use;

    /**
     * Specify the peer's usage policy.
     */
    pjmedia_srtp_use		peer_use;

    /**
     * RTP sequence rollover counter info for incoming direction.
     */
    pjmedia_srtp_roc		rx_roc;

    /**
     * RTP sequence rollover counter info for outgoing direction.
     */
    pjmedia_srtp_roc		tx_roc;

} pjmedia_srtp_info;


/**
 * This structure specifies DTLS-SRTP negotiation parameters.
 */
typedef struct pjmedia_srtp_dtls_nego_param
{
    /**
     * Fingerprint of remote certificate, should be formatted as
     * "SHA-256/1 XX:XX:XX...". If this is not set, fingerprint verification
     * will not be performed.
     */
    pj_str_t		 rem_fingerprint;

    /**
     * Remote address and port.
     */
    pj_sockaddr		 rem_addr;

    /**
     * Remote RTCP address and port.
     */
    pj_sockaddr		 rem_rtcp;

    /**
     * Set to PJ_TRUE if our role is active. Active role will initiates
     * the DTLS negotiation. Passive role will wait for incoming DTLS
     * negotiation packet.
     */
    pj_bool_t		 is_role_active;

} pjmedia_srtp_dtls_nego_param;



/**
 * Initialize SRTP library. This function should be called before
 * any SRTP functions, however calling #pjmedia_transport_srtp_create() 
 * will also invoke this function. This function will also register SRTP
 * library deinitialization to #pj_atexit(), so the deinitialization
 * of SRTP library will be performed automatically by PJLIB destructor.
 *
 * @param endpt	    The media endpoint instance.
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt);


/**
 * Initialize SRTP setting with its default values.
 *
 * @param opt	SRTP setting to be initialized.
 */
PJ_DECL(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt);


/**
 * Enumerate available SRTP crypto name.
 *
 * @param count	    On input, specifies the maximum length of crypto
 *		    array. On output, the number of available crypto
 *		    initialized by this function.
 * @param crypto    The SRTP crypto array output.
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_srtp_enum_crypto(unsigned *count,
					      pjmedia_srtp_crypto crypto[]);


/**
 * Enumerate available SRTP keying methods.
 *
 * @param count	    On input, specifies the maximum length of keying method
 *		    array. On output, the number of available keying method
 *		    initialized by this function.
 * @param keying    The SRTP keying method array output.
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_srtp_enum_keying(unsigned *count,
				      pjmedia_srtp_keying_method keying[]);


/**
 * Create an SRTP media transport.
 *
 * @param endpt	    The media endpoint instance.
 * @param tp	    The actual media transport to send and receive 
 *		    RTP/RTCP packets. This media transport will be
 *		    kept as member transport of this SRTP instance.
 * @param opt	    Optional settings. If NULL is given, default
 *		    settings will be used.
 * @param p_tp	    Pointer to receive the transport SRTP instance.
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_create(
				       pjmedia_endpt *endpt,
				       pjmedia_transport *tp,
				       const pjmedia_srtp_setting *opt,
				       pjmedia_transport **p_tp);

/**
 * Get current SRTP media transport setting.
 *
 * @param srtp	    The SRTP transport.
 * @param opt	    Structure to receive the SRTP setting
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_get_setting(
				       pjmedia_transport *srtp,
				       pjmedia_srtp_setting *opt);


/**
 * Modify SRTP media transport setting.
 *
 * @param srtp	    The SRTP transport.
 * @param opt	    New setting
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_modify_setting(
				       pjmedia_transport *srtp,
				       const pjmedia_srtp_setting *opt);

/**
 * Get fingerprint of local DTLS-SRTP certificate.
 *
 * @param srtp	    The SRTP transport.
 * @param hash	    Fingerprint hash algorithm, currently valid values are
 *		    "SHA-256" and "SHA-1".
 * @param buf	    Buffer for fingerprint output. The output will be
 *		    formatted as "SHA-256/1 XX:XX:XX..." and null terminated.
 * @param len	    On input, the size of the buffer.
 *		    On output, the length of the fingerprint.
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_dtls_get_fingerprint(
				pjmedia_transport *srtp,
				const char *hash,
				char *buf, pj_size_t *len);


/**
 * Manually start DTLS-SRTP negotiation with the given parameters. Application
 * only needs to call this function when the SRTP transport is used without
 * SDP offer/answer. When SDP offer/answer framework is used, the DTLS-SRTP
 * negotiation will be handled by pjmedia_transport_media_create(),
 * pjmedia_transport_media_start(), pjmedia_transport_media_encode_sdp(), and
 * pjmedia_transport_media_stop().
 *
 * When the negotiation completes, application will be notified via SRTP
 * callback on_srtp_nego_complete(), if set. If the negotiation is successful,
 * SRTP will be automatically started.
 *
 * Note that if the SRTP member transport is an ICE transport, application
 * should only call this function after ICE negotiation is completed
 * successfully.
 *
 * @param srtp	    The SRTP transport.
 * @param param	    DTLS-SRTP nego parameter.
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_dtls_start_nego(
				pjmedia_transport *srtp,
				const pjmedia_srtp_dtls_nego_param *param);


/**
 * Manually start SRTP session with the given parameters. Application only
 * needs to call this function when the SRTP transport is used without SDP
 * offer/answer. When SDP offer/answer framework is used, the SRTP transport
 * will be started/stopped by #pjmedia_transport_media_start() and 
 * #pjmedia_transport_media_stop() respectively.
 *
 * Please note that even if an RTP stream is only one direction, application
 * will still need to provide both crypto suites, because it is needed by 
 * RTCP.

 * If application specifies the crypto keys, the keys for transmit and receive
 * direction MUST be different.
 *
 * @param srtp	    The SRTP transport.
 * @param tx	    Crypto suite setting for transmit direction.
 * @param rx	    Crypto suite setting for receive direction.
 *
 * @return	    PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_start(
					    pjmedia_transport *srtp,
					    const pjmedia_srtp_crypto *tx,
					    const pjmedia_srtp_crypto *rx);

/**
 * Stop SRTP session.
 *
 * @param srtp	    The SRTP media transport.
 *
 * @return	    PJ_SUCCESS on success.
 *
 * @see #pjmedia_transport_srtp_start() 
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp);


/**
 * This is a utility function to decrypt SRTP packet using SRTP transport.
 * This function is not part of SRTP transport's API, but it can be used
 * to decrypt SRTP packets from non-network (for example, from a saved file)
 * without having to use the transport framework. See pcaputil.c in the
 * samples collection on how to use this function.
 *
 * @param tp		The SRTP transport.
 * @param is_rtp	Set to non-zero if the packet is SRTP, otherwise set
 *			to zero if the packet is SRTCP.
 * @param pkt		On input, it contains SRTP or SRTCP packet. On
 *			output, it contains the decrypted RTP/RTCP packet.
 * @param pkt_len	On input, specify the length of the buffer. On
 *			output, it will be filled with the actual length
 *			of decrypted packet.
 *
 * @return		PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp,
							pj_bool_t is_rtp,
							void *pkt,
							int *pkt_len);


/**
 * Query member transport of SRTP.
 *
 * @param srtp		    The SRTP media transport.
 *
 * @return		    member media transport.
 */
PJ_DECL(pjmedia_transport*) pjmedia_transport_srtp_get_member(
						    pjmedia_transport *srtp);


PJ_END_DECL

/**
 * @}
 */

#endif /* __PJMEDIA_TRANSPORT_SRTP_H__ */
