blob: d7d2265d330b8fb3d50050b75fa22321aee793d4 [file] [log] [blame]
/*
Copyright (C) 2012-2013 Werner Dittmann
This program 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 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 _ZRTPSDESSTREAM_H_
#define _ZRTPSDESSTREAM_H_
/**
* @file ZrtpSdesStream.h
* @brief The ZRTP main engine
* @defgroup GNU_ZRTP The GNU ZRTP C++ implementation
* @{
*
* This class implements SDES and provides a simple to use API for applications.
*
* This SDES implementation currently supports only two SDES algorithms and it does
* not support optional parameters such as lifetime or MKI parameters. Also session
* parameters are not supported. Most applications that use SDES don't use these
* optional parameters.
*
* It is not necessary to explicitly start the SDES stream. The class initiates
* the SRTP after it created and parsed all necessary SDES crypto strings.
*
* Because SDES works together with the signaling protocol, for example SIP, it is
* important to adhere to a defined flow. The following pseudo code snippet depicts
* such a flow. Applications shall follow this flow.
*
*<pre>
*
* Inviter Answerer
* (Offerer)
*
* ZrtpSdesStream inv; ZrtpSdesStream answ;
*
* // create/get own SDES data
* inv.createSdes(...);
* inv.getCryptoMixAttribute(...)
*
* // prepare SIP/SDP offer, send
* // it to answerer
* // receive SIP/SDP, get
* // SDES data, parse/set it
* answ.setCryptoMixAttribute(...)
* answ.parseSdes(...)
*
* // create/get own SDES data
* answ.getCryptoMixAttribute(...)
* answ.createSdes(...)
*
* // prepare SIP/SDP answer,
* // send to offerer
* // receive SIP/SDP answer, get
* // SDES data, parse, set mix algo
* // if availabe
* inv.setCryptoMixAttribute(...)
* inv.parseSdes(...)
*
* ... ...
*
* inv.outgoingRtp(...)
* answ.incomingRtp(...)
*
* answ.outgoingRtp(...)
* inv.incomingRtp(...)
*</pre>
*
* To use SDES without the new crypto mix feature just do not use the crypto mix functions.
* An application may always send crypto mix attributes. If the answerer does not support this
* feature it does not send back a selected algorithm and the offerer cannot set an algorithm.
* Thus the crypto mix feature is not used.
*
* @author Werner Dittmann <Werner.Dittmann@t-online.de>
*/
#include <common/osSpecifics.h>
class CryptoContext;
class CryptoContextCtrl;
/*
* These functions support 256 bit encryption algorithms.
*/
#define MAX_KEY_LEN 32
#define MAX_SALT_LEN 14
#define MAX_DIGEST_LENGTH 64
/**
* Maximum length of a raw crypto string.
*/
#define MAX_CRYPT_STRING_LEN 200
class __EXPORT ZrtpSdesStream {
public:
/**
* Supported SDES crypto suites.
*/
typedef enum {
AES_CM_128_HMAC_SHA1_32 = 0,
AES_CM_128_HMAC_SHA1_80
} sdesSuites;
/**
* SDES stream state
*/
typedef enum {
STREAM_INITALIZED = 1,
OUT_PROFILE_READY,
IN_PROFILE_READY,
SDES_SRTP_ACTIVE
} sdesZrtpStates;
typedef enum {
MIX_NONE = 0,
MIX_HMAC_SHA,
MIX_MAC_SKEIN
} sdesHmacTypeMix;
/**
* @brief Create and SDES/ZRTP stream.
*
* This method creates an SDES stream with capabilities to handle RTP,
* RTCP, SRTP, and SRTCP packets.
*
* @param suite defines which crypto suite to use for this stream. The values are
* @c AES_CM_128_HMAC_SHA1_80 or @c AES_CM_128_HMAC_SHA1_32.
*/
ZrtpSdesStream(const sdesSuites suite =AES_CM_128_HMAC_SHA1_32);
~ZrtpSdesStream();
/**
* @brief Close an SDES/ZRTP stream.
*
* Close the stream and return allocated memory to the pool.
*/
void close();
/**
* @brief Creates an SDES crypto string for the SDES/ZRTP stream.
*
* Creates the crypto string that the application can use in the SDP fields of
* SIP INVITE or SIP 200 OK.
*
* An INVITE-ing application shall call this function at the same point when
* it calls the functions to get the @c zrtp-hash string and shall insert the
* created crypto string into the SDP.
*
* An answering application shall call this function directly @b after it called
* @c sdesZrtpStreamParseSdes. This usually at the same point when it gets the
* @c zrtp-hash from the SDP parameters and forwards it to @c libzrtp. The
* answering application's SRTP environment is now ready.
*
* @param cryptoString output buffer that receives the crypto string in raw
* format, without the any signaling prefix, for example
* @c a=crypto:. The function terminates the crypto string
* with a @c nul byte
*
* @param maxLen length of the crypto string buffer. On return it contains the
* actual length of the crypto string.
*
* @param sipInvite the inviter (offerer) must set this to @c true, the answerer must
* set it to @c false.
*
* @return @c true if data could be created, @c false otherwise.
*/
bool createSdes(char *cryptoString, size_t *maxLen, bool sipInvite);
/**
* @brief Parses an SDES crypto string for the SDES/ZRTP stream.
*
* Parses a SDES crypto string that the application received in a SIP INVITE
* or SIP 200 OK.
*
* An INVITE-ing (offerer) application shall call this function right after it received
* the 200 OK from the answering application and must call this function with the
* @c sipInvite parameter set to @c true. The offerer's SRTP is now ready for use.
*
* The answering application calls this function after it received the INVITE and
* extracted the crypto string from the SDP and must call this function with the
* @c sipInvite parameter set to @c false.
*
* @param cryptoString the received crypto sting in raw format,
* without any signaling prefix, for example @c a=crypto:
*
* @param length length of the crypto string to parse. If the length is
* @c zero then the function uses @c strlen to compute
* the length.
*
* @param sipInvite the inviter (offerer) must set this to @c true, the answerer must
* set it to @c false.
*
* @return @c true if data could be created, @c false otherwise.
*/
bool parseSdes(const char *cryptoString, size_t length, bool sipInvite);
/**
* @brief Get Crypto Mix attribute string
*
* The offerer calls this method to get a string of @b all supported crypto mix algorithms
* and shall send this list to the answerer.
*
* The answerer calls this function only @b after it received the crypto mix string and @b after
* calling @c setCryptoMixAttribute(...). The method returns only one (the selected)
* crypto mix algorithm and the answerer must send this to the offerer, for example in 200 OK.
*
* @param algoNames buffer to store the nul terminated crypto mix algorithm names.
* The buffer must be long enough to hold at least the name of the mandatory
* algorithm HMAC-SHA-384.
*
* @param length length of buffer
*
* @return Length of algorithm names (excluding nul byte) or zero if crypto mix not supported or
* enabled.
*/
int getCryptoMixAttribute(char *algoNames, size_t length);
/**
* @brief Set Crypto Mix attribute string
*
* The method checks if it the string contains an supported algorithm and selects one algorithm.
*
* The offerer calls this method @b after it received the selected algorithm in the answer.
*
* The answerer must call this method @b before it calls the @c getCryptoMixAttribute() method.
*
* @param algoNames buffer that contains the received crypto mix algorithm names.
* The buffer must be nul terminated.
*
* @return @c false if none of the offered algorithms is supported.
*/
bool setCryptoMixAttribute(const char *algoNames);
/*
* ******** Outgoing RTP/RTCP packet handling
*/
/**
* @brief Process an outgoing RTP packet
*
* This function processes an outgoing RTP packet. Depending on the state
* the packet is either:
* - not encrypted if neither SDES nor ZRTP are active or supported by the
* other client. This is the standard case if the stream was just initialized.
* - encrypted with SDES provided key data. This is the case if the application
* called both @c sdesZrtpStreamCreateSdes and @c sdesZrtpStreamParseSdes
* functions to properly setup the SDES key data.
*
* @param packet the buffer that contains the RTP packet. After processing, the
* encrypted packet is stored in the same buffer. The buffer must
* big enough to hold the additional SRTP data, depending on the
* SRTP profile these are usually 4 - 20 bytes.
*
* @param length length of the RTP packet
*
* @param newLength to an integer that get the new length of the packet including SRTP data.
*
* @return
* - @c true if encryption is successful, app shall send packet to the recipient.
* - @c false if there was an error during encryption, don't send the packet.
*/
bool outgoingRtp(uint8_t *packet, size_t length, size_t *newLength);
/**
* @brief Process an outgoing RTCP packet
*
* This function works in the same way as @c outgoingRtp.
*
* @param packet the buffer that contains the RTCP packet. After processing, the
* encrypted packet is stored in the same buffer. The buffer must
* big enough to hold the additional SRTP data, depending on the
* SRTP profile these are usually 8 - 20 bytes.
*
* @param length length of the RTP packet
*
* @param newLength to an integer that get the new length of the packet including SRTP data.
*
* @return
* - @c true if encryption is successful, app shall send packet to the recipient.
* - @c false if there was an error during encryption, don't send the packet.
*/
bool outgoingRtcp(uint8_t *packet, size_t length, size_t *newLength);
/*
* ******** Incoming SRTP/SRTCP packet handling
*/
/**
* @brief Process an incoming RTP or SRTP packet
*
* This function processes an incoming RTP/SRTP packet. Depending on the state
* the packet is either:
* - not decrypted if SDES is not active or supported by the
* other client. This is the standard case if the stream was just initialized.
* - decrypted with SDES provided key data. This is the case if the application
* called both @c sdesZrtpStreamCreateSdes and @c sdesZrtpStreamParseSdes
* functions to properly setup the SDES key data.
*
* @param packet the buffer that contains the RTP/SRTP packet. After processing,
* the decrypted packet is stored in the same buffer.
*
* @param length length of the RTP packet
*
* @param newLength to an integer that get the new length of the packet excluding SRTCP data.
*
* @return
* - 1: success,
* - -1: SRTP authentication failed,
* - -2: SRTP replay check failed
*/
int incomingRtp(uint8_t *packet, size_t length, size_t *newLength);
/**
* @brief Process an incoming RTCP or SRTCP packet
*
* This function works in the same way as @c incomingRtp.
*
* @param packet the buffer that contains the RTCP/SRTCP packet. After processing,
* the decrypted packet is stored in the same buffer.
*
* @param length length of the RTCP packet
*
* @param newLength to an integer that get the new length of the packet excluding SRTCP data.
*
* @return
* - 1: success,
* - -1: SRTCP authentication failed,
* - -2: SRTCP replay check failed
*/
int incomingSrtcp(uint8_t *packet, size_t length, size_t *newLength);
/**
* @brief Process an outgoing ZRTP packet.
*
* Works like @c outgoingRtp, refer to that documentation.
*
* @param packet the buffer that contains the ZRTP packet.
*
* @param length length of the ZRTP packet
*
* @param newLength to an integer that get the new length of the packet including SRTP data.
*
* @return
* - @c true if encryption is successful, app shall send packet to the recipient.
* - @c false if there was an error during encryption, don't send the packet.
*/
bool outgoingZrtpTunnel(uint8_t *packet, size_t length, size_t *newLength);
/**
* @brief Process an incoming ZRTP packet
*
* Works like @c incomingRtp, refer to that documentation.
*
* @param packet the buffer that contains the ZRTP/SRTP packet. After processing,
* the decrypted packet is stored in the same buffer.
*
* @param length length of the RTP packet
*
* @param newLength to an integer that get the new length of the packet excluding SRTCP data.
*
* @return
* - 1: success,
* - -1: SRTP authentication failed,
* - -2: SRTP replay check failed
*/
int incomingZrtpTunnel(uint8_t *packet, size_t length, size_t *newLength);
/**
* @brief Return state of SDES stream.
*
* @return state of stream.
*/
sdesZrtpStates getState() {return state;}
/**
* @brief Return SDES crypto mixer HMAC type.
*
* @return HMAC type
*/
sdesHmacTypeMix getHmacTypeMix() {return cryptoMixHashType;}
/**
* @brief Return name of active cipher algorithm.
*
* @return point to name of cipher algorithm.
*/
const char* getCipher();
/**
* @brief Return name of active SRTP authentication algorithm.
*
* @return point to name of authentication algorithm.
*/
const char* getAuthAlgo();
/*
* ******** Lower layer functions
*/
private:
/**
* @brief Create an SRTP crypto context and the according SDES crypto string.
*
* This lower layer method creates an SDES crypto string. It selects a valid
* crypto suite, generates the key and salt data, converts these into base 64
* and returns the crypto string in raw format without any signaling prefixes.
*
* The output string has the following format:
* @verbatim
* 1 AES_CM_128_HMAC_SHA1_32 inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj
* @endverbatim
*
* Applications usually don't use this method directly. Applications shall
* use the SDES stream functions.
*
* Depending on the crypto suite the overall length of the crypto string
* is variable. For a normal AES_128_CM suite the minumum lenth is 73
* characters, a AES_256_CM suite results in 97 characters (not counting
* any signaling prefixes).
*
* @param cryptoString points to a char output buffer that receives the
* crypto string in the raw format, without the any
* signaling prefix, for example @c a=crypto: in case
* of SDP signaling. The function terminates the
* crypto string with a @c nul byte
*
* @param maxLen points to an integer. On input this integer specifies the
* length of the output buffer. If @c maxLen is smaller than
* the resulting crypto string the function returns an error
* conde. On return the functions sets @c maxLen to the
* actual length of the resultig crypto string.
*
* @param tag the value of the @c tag field in the crypto string. The
* answerer must use this input to make sure that the tag value
* in the answer matches the value in the offer. See RFC 4568,
* section 5.1.2.
* If the tag value is @c -1 the function sets the tag to @c 1.
*
* @return @c true if data could be created, @c false
* otherwise.
*/
bool createSdesProfile(char *cryptoString, size_t *maxLen);
/**
* @brief Parse and check an offered SDES crypto string and create SRTP crypto context.
*
* The method parses an offered SDES crypto string and checks if it is
* valid. Next it checks if the string contains a supported crypto suite
* and if the key and salt lengths match the selected crypto suite.
*
* Applications usually don't use this method directly. Applications shall
* use the SDES stream functions.
*
* @b NOTE: This function does not support the optional parameters lifetime,
* MKI, and session parameters. While it can parse liftime and MKI theiy are
* not evaluated and used. If these parameters are used in the input crypto
* string the function return @c false.
*
* @param cryptoString points to the crypto sting in raw format,
* without any signaling prefix, for example @c a=crypto: in case of
* SDP signaling.
*
* @param length length of the crypto string to parse. If the length is
* @c zero then the function uses @c strlen to compute the length.
*
* @param parsedSuite the function sets this to the @c sdesSuites enumerator of
* the parsed crypto suite. The answerer shall use this as input to
* @c createSdesProfile to make sure that it creates the same crypto suite.
* See RFC 4568, section 5.1.2
*
* @param tag the function sets this to the @c tag value of the parsed crypto
* string. The answerer must use this as input to @c createSdesProfile
* to make sure that it creates the correct tag in the crypto string.
* See RFC 4568, section 5.1.2
*
* @return @c true if checks were ok, @c false
* otherwise.
*/
bool parseCreateSdesProfile(const char *cryptoString, size_t length, sdesSuites *parsedSuite, int32_t *tag);
/**
* @brief Create the SRTP contexts after all SDES creation and parsing is done.
*
* @param sipInvite if this is set to @c true (not zero) then the method
* computes the key data for the inviting SIP application (offerer) and
* for the answerer otherwise.
*/
void createSrtpContexts(bool sipInvite);
/**
* @brief Compute the mixed keys if SDES mixing attribute is set.
*
* The method takes the parsed or created SDES key material and computes the mixed keys and salt.
* It replaces the existing key material with the new data.
*
* @param sipInvite if this is set to @c true (not zero) then the method
* computes the key data for the inviting SIP application (offerer) and
* for the answerer otherwise.
*/
void computeMixedKeys(bool sipInvite);
sdesZrtpStates state;
sdesSuites suite;
int32_t tag;
CryptoContext *recvSrtp; //!< The SRTP context for this stream
CryptoContextCtrl *recvSrtcp; //!< The SRTCP context for this stream
CryptoContext *sendSrtp; //!< The SRTP context for this stream
CryptoContextCtrl *sendSrtcp; //!< The SRTCP context for this stream
uint32_t srtcpIndex; //!< the local SRTCP index
CryptoContext *recvZrtpTunnel; //!< The SRTP context for sender ZRTP tunnel
CryptoContext *sendZrtpTunnel; //!< The SRTP context for receiver ZRTP tunnel
int32_t cryptoMixHashLength;
sdesHmacTypeMix cryptoMixHashType;
// Variables for crypto that this client creates and sends to the other client, filled during SDES create
uint8_t localKeySalt[((MAX_KEY_LEN + MAX_SALT_LEN + 3)/4)*4]; //!< Some buffer for key and salt, multiple of 4
int localKeyLenBytes;
int localSaltLenBytes;
int localCipher;
int localAuthn;
int localAuthKeyLen;
int localTagLength;
// Variables for crypto that this client receives from the other client, filled during SDES parse
uint8_t remoteKeySalt[((MAX_KEY_LEN + MAX_SALT_LEN + 3)/4)*4]; //!< Some buffer for key and salt, multiple of 4
int remoteKeyLenBytes;
int remoteSaltLenBytes;
int remoteCipher;
int remoteAuthn;
int remoteAuthKeyLen;
int remoteTagLength;
};
#endif