/* 
 * 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:
 *
   \img{pjmedia/docs/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:
 *
   \img{pjmedia/docs/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__ */
