/*
  Copyright (C) 2006 - 2012 Werner Dittmann

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

#ifndef CRYPTOCONTEXT_H
#define CRYPTOCONTEXT_H

/**
 * @file CryptoContext.h
 * @brief The C++ SRTP implementation
 * @ingroup Z_SRTP
 * @{
 */

#define REPLAY_WINDOW_SIZE 64

const int SrtpAuthenticationNull      = 0;
const int SrtpAuthenticationSha1Hmac  = 1;
const int SrtpAuthenticationSkeinHmac = 2;

const int SrtpEncryptionNull  = 0;
const int SrtpEncryptionAESCM = 1;
const int SrtpEncryptionAESF8 = 2;
const int SrtpEncryptionTWOCM = 3;
const int SrtpEncryptionTWOF8 = 4;

// Check if included via CryptoContextCtrl.cpp - avoid double definitions
#ifndef CRYPTOCONTEXTCTRL_H

#include <stdint.h>

class SrtpSymCrypto;

/**
 * @brief Implementation for a SRTP cryptographic context.
 *
 * This class holds data and provides functions that implement a
 * cryptographic context for SRTP. Refer to RFC 3711, chapter 3.2 for some
 * more detailed information about the SRTP cryptographic context.
 *
 * Each SRTP cryptographic context uses a RTP source identified by
 * its SSRC. Thus you can independently protect each source inside a RTP
 * session.
 *
 * Key management mechanisms negotiate the parameters for the SRTP
 * cryptographic context, such as master key, key length, authentication
 * length and so on. The key management mechanisms are not part of
 * SRTP. Refer to MIKEY (RFC 3880) or to Phil Zimmermann's ZRTP protocol
 * (RFC6189). After key management negotiated the data the application can
 * setup the SRTP cryptographic context and enable SRTP processing.
 *
 * This SRTP context implementation supports RTP only.
 *
 * A short eample how to setup a SRTP CryptoContext:
 @verbatim

 // First some key and salt data - this data is just for demo purposes
 uint8 masterKey[] = {   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

 uint8 masterSalt[] = {  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
                        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d };

 ...

 CryptoContext* cryptoCtxSend =
     new CryptoContext(0xfeedbacc,
     0,                           // roc,
     0L,                          // keyderivation rate << 48,
     SrtpEncryptionAESCM,         // encryption algo
     SrtpAuthenticationSha1Hmac,  // authtication algo
     masterKey,                   // Master Key data
     128 / 8,                     // Master Key length in bytes
     masterSalt,                  // Master Salt data
     112 / 8,                     // Master Salt length in bytes
     128 / 8,                     // encryption keylength in bytes
     160 / 8,                     // authentication key length in bytes (SHA1)
     112 / 8,                     // session salt length in bytes
      80 / 8);                     // authentication tag length in bytes

 cryptoCtxSend->deriveSrtpKeys(0);

 ....

 // To protect a RTP packet
 // buffer: pointer to the RTP packet, length of the RTP data, newLength is a
 // pointer to a size_t that gets the updated length.
 bool rc = SrtpHandler::protect(cryptoCtxSend, buffer, length, newLength);

 // To unprotect a SRTP packet:
 // buffer: pointer to the RTP packet, length of the SRTP data, newLength is a
 // pointer to a size_t that gets the updated length.
 int32_t rc = SrtpHandler::unprotect(cryptoCtxRecv, buffer, length, newLength);

 @endverbatim
 *
 * @note You need two CryptoContext instances - one for the sending channel the
 * other one for the receiving channel. 
 *
 * Before an appliction can use a CryptoContext it must call the key derivation
 * function deriveSrtpKeys() first. Only then this SRTP cryptographic context is ready
 * to protect or unprotect a RTP SSRC stream.
 *
 * Together with the newCryptoContextForSSRC() function an application can prepare a
 * CryptoContext and save it as template. Once it needs a new CryptoContext, say
 * for a new SSRC, it calls newCryptoContextForSSRC() on the saved context to get an
 * initialized copy and then call deriveSrtpKeys() to compute and process the keys.
 *
 * @note A saved, pre-initialized template contains the non-processed keys. Only
 * the method deriveSrtpKeys() processes the keys and cleares them. Thus don't store
 * CryptoContext templates if the application cannot protect the templates against
 * reading from other possibly rogue applications.
 *
 * @sa SrtpHandler
 * 
 * @author Werner Dittmann <Werner.Dittmann@t-online.de>
 */
class CryptoContext {
public:
    /**
     * @brief Constructor for an active SRTP cryptographic context.
     *
     * This constructor creates an pre-initialized SRTP cryptographic context were
     * algorithms are allocated, keys are stored and so on. An application can
     * call newCryptoContextForSSRC() to get a full copy of this pre-initialized
     * CryptoContext.
     *
     *
     * @param ssrc
     *    The RTP SSRC that this SRTP cryptographic context belongs to.
     *
     * @param roc
     *    The initial Roll-Over-Counter according to RFC 3711. These are the
     *    upper 32 bit of the overall 48 bit SRTP packet index. Usually set to zero.
     *    Refer to chapter 3.2.1 of the RFC.
     *
     * @param keyDerivRate
     *    The key derivation rate defines when to recompute the SRTP session
     *    keys. Refer to chapter 4.3.1 in the RFC.
     *
     * @param ealg
     *    The encryption algorithm to use. Possible values are <code>
     *    SrtpEncryptionNull, SrtpEncryptionAESCM, SrtpEncryptionAESF8,
     *    SrtpEncryptionTWOCM, SrtpEncryptionTWOF8</code>. See chapter 4.1.1
     *    for AESCM (Counter mode) and 4.1.2 for AES F8 mode.
     *
     * @param aalg
     *    The authentication algorithm to use. Possible values are <code>
     *    SrtpEncryptionNull, SrtpAuthenticationSha1Hmac, SrtpAuthenticationSkeinHmac
     *    </code>.
     *
     * @param masterKey
     *    Pointer to the master key for this SRTP cryptographic context.
     *    Must point to <code>masterKeyLength</code> bytes. Refer to chapter
     *    3.2.1 of the RFC about the role of the master key.
     *
     * @param masterKeyLength
     *    The length in bytes of the master key in bytes. The length must
     *    match the selected encryption algorithm. Because SRTP uses AES
     *    based  encryption only, then master key length may be 16 or 32
     *    bytes (128 or 256 bit master key)
     *
     * @param masterSalt
     *    SRTP uses the master salt to generate the initialization vector
     *    that in turn is input to compute the session key, session
     *    authentication key and the session salt.
     *
     * @param masterSaltLength
     *    The length in bytes of the master salt data in bytes. According to
     *    RFC3711 the standard value for the master salt length should
     *    be 14 bytes (112 bit).
     *
     * @param ekeyl
     *    The length in bytes of the session encryption key that SRTP shall
     *    generate and use. Usually the same length as for the master key
     *    length, however you may use a different length as well.
     *
     * @param akeyl
     *    The length in bytes of the session authentication key. SRTP
     *    computes this key and uses it as input to the authentication
     *    algorithm.
     *    This is usually 160 bits (20 bytes) for @c SrtpAuthenticationSha1Hmac
     *    and 256 bits (32 bytes) for @c SrtpAuthenticationSkeinHmac.
     *
     * @param skeyl
     *    The length in bytes of the session salt. SRTP computes this salt
     *    key and uses it as input during encryption. The length usually
     *    is the same as the master salt length.
     *
     * @param tagLength
     *    The length is bytes of the authentication tag that SRTP appends
     *    to the RTP packet. The @c CryptoContext supports @c SrtpAuthenticationSha1Hmac
     *    with 4 and 10 byte (32 and 80 bits) and @c SrtpAuthenticationSkeinHmac
     *    with 4 and 8 bytes (32 and 64 bits) tag length. Refer to chapter 4.2. in RFC 3711.
     */
    CryptoContext(uint32_t ssrc, int32_t roc,
                   int64_t  keyDerivRate,
                   const  int32_t ealg,
                   const  int32_t aalg,
                   uint8_t* masterKey,
                   int32_t  masterKeyLength,
                   uint8_t* masterSalt,
                   int32_t  masterSaltLength,
                   int32_t  ekeyl,
                   int32_t  akeyl,
                   int32_t  skeyl,
                   int32_t  tagLength);

    /**
     * @brief Destructor.
     *
     * Cleans the SRTP cryptographic context.
     */
    ~CryptoContext();

    /**
     * @brief Set the Roll-Over-Counter.
     *
     * Ths method sets the upper 32 bit of the 48 bit SRTP packet index
     * (the roll-over-part)
     *
     * @param r
     *   The roll-over-counter
     */
    inline void setRoc(uint32_t r) { roc = r; }

    /**
     * @brief Get the Roll-Over-Counter.
     *
     * Ths method get the upper 32 bit of the 48 bit SRTP packet index
     * (the roll-over-part)
     *
     * @return The roll-over-counter
     */
    inline uint32_t getRoc() const { return roc; }

    /**
     * @brief Perform SRTP encryption.
     *
     * This method encrypts <em>and</em> decrypts SRTP payload data. Plain
     * data gets encrypted, encrypted data get decrypted.
     *
     * @param pkt
     *    Pointer to RTP packet buffer, used for F8.
     *
     * @param payload
     *    The data to encrypt.
     *
     * @param paylen
     *    Length of payload.
     *
     * @param index
     *    The 48 bit SRTP packet index. See the <code>guessIndex</code>
     *    method.
     *
     * @param ssrc
     *    The RTP SSRC data in <em>host</em> order.
     */
    void srtpEncrypt(uint8_t* pkt, uint8_t* payload, uint32_t paylen, uint64_t index, uint32_t ssrc);

    /**
     * @brief Compute the authentication tag.
     *
     * Compute the authentication tag according the the paramters in the
     * SRTP Cryptograhic context.
     *
     * @param pkt
     *    Pointer to RTP packet buffer that contains the data to authenticate.
     *
     * @param pktlen
     *    Length of the RTP packet buffer
     *
     * @param roc
     *    The 32 bit SRTP roll-over-counter.
     *
     * @param tag
     *    Points to a buffer that hold the computed tag. This buffer must
     *    be able to hold <code>tagLength</code> bytes.
     */
    void srtpAuthenticate(uint8_t* pkt, uint32_t pktlen, uint32_t roc, uint8_t* tag);

    /**
     * @brief Perform key derivation according to SRTP specification
     *
     * This method computes the session key, session authentication key and the
     * session salt key. This method must be called at least once after the
     * SRTP Cryptograhic context was set up.
     *
     * This method clears the key data once it was processed by the encryptions'
     * set key functions.
     *
     * @param index
     *    The 48 bit SRTP packet index. See the <code>guessIndex</code>
     *    method. Usually 0.
     */
    void deriveSrtpKeys(uint64_t index);

    /**
     * @brief Compute (guess) the new SRTP index based on the sequence number of
     * a received RTP packet.
     *
     * The method uses the algorithm show in RFC3711, Appendix A, to compute
     * the new index.
     *
     * @param newSeqNumber
     *    The sequence number of the received RTP packet in host order.
     *
     * @return The new SRTP packet index
     */
    uint64_t guessIndex(uint16_t newSeqNumber);

    /**
     * @brief Check for packet replay.
     *
     * The method check if a received packet is either to old or was already
     * received.
     *
     * The method supports a 64 packet history relative the the given
     * sequence number.
     *
     * @param newSeqNumber
     *    The sequence number of the received RTP packet in host order.
     *
     * @return <code>true</code> if no replay, <code>false</code> if packet
     *    is too old ar was already received.
     */
    bool checkReplay(uint16_t newSeqNumber);

    /**
     * @brief Update the SRTP packet index.
     *
     * Call this method after all checks were successful. See chapter
     * 3.3.1 in the RFC when to update the ROC and ROC processing.
     *
     * @param newSeqNumber
     *    The sequence number of the received RTP packet in host order.
     */
    void update(uint16_t newSeqNumber);

    /**
     * @brief Get the length of the SRTP authentication tag in bytes.
     *
     * @return the length of the authentication tag.
     */
    int32_t getTagLength() const { return tagLength; }

    /**
     * @brief Get the length of the MKI in bytes.
     *
     * @return the length of the MKI.
     */
    int32_t getMkiLength() const { return mkiLength; }

    /**
     * @brief Get the SSRC of this SRTP Cryptograhic context.
     *
     * @return the SSRC.
     */
    uint32_t getSsrc() const { return ssrcCtx; }

    /**
     * @brief Set the start (base) number to compute the PRF labels.
     *
     * Refer to RFC3711, chapters 4.3.1 and 4.3.2 about values for labels.
     * CryptoContext computes the labes as follows:
     *
     * - labelBase + 0 -> encryption label
     * - labelBase + 1 -> authentication label
     * - labelBase + 2 -> salting key label
     *
     * The CryptoContext constructor initializes CryptoContext::labelBase
     * with 0 to comply with RFC 3711 label values.
     *
     * Applications may set the #labelBase to other values to use the CryptoContext
     * for other purposes.
     */
    void setLabelbase(uint8_t base) { labelBase = base; }

    /**
     * @brief Derive a new Crypto Context for use with a new SSRC
     *
     * This method returns a new Crypto Context initialized with the data
     * of this crypto context. Replacing the SSRC, Roll-over-Counter, and
     * the key derivation rate the application can use this Crypto Context
     * to encrypt / decrypt a new stream (Synchronization source) inside
     * one RTP session.
     *
     * Before the application can use this crypto context it must call deriveSrtpKeys().
     *
     * @param ssrc
     *     The SSRC for this context
     * @param roc
     *     The Roll-Over-Counter for this context, usually 0
     * @param keyDerivRate
     *     The key derivation rate for this context, usally 0
     * @return
     *     a new CryptoContext with all relevant data set.
     */
    CryptoContext* newCryptoContextForSSRC(uint32_t ssrc, int roc, int64_t keyDerivRate);

private:

    uint32_t ssrcCtx;
    bool   using_mki;
    uint32_t mkiLength;
    uint8_t* mki;

    uint32_t roc;
    uint32_t guessed_roc;
    uint16_t s_l;
    int64_t  key_deriv_rate;

    /* bitmask for replay check */
    uint64_t replay_window;

    uint8_t* master_key;
    uint32_t master_key_length;
    uint32_t master_key_srtp_use_nb;
    uint32_t master_key_srtcp_use_nb;
    uint8_t* master_salt;
    uint32_t master_salt_length;

    /* Session Encryption, Authentication keys, Salt */
    int32_t  n_e;
    uint8_t* k_e;
    int32_t  n_a;
    uint8_t* k_a;
    int32_t  n_s;
    uint8_t* k_s;

    int32_t ealg;
    int32_t aalg;
    int32_t ekeyl;
    int32_t akeyl;
    int32_t skeyl;
    int32_t tagLength;
    uint8_t labelBase;
    bool  seqNumSet;

    void*   macCtx;

    SrtpSymCrypto* cipher;
    SrtpSymCrypto* f8Cipher;
};

#endif

/**
 * @}
 */
#endif

