/*
  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/>.
*/

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <string>
#include <sstream>

#include <libzrtpcpp/ZrtpSdesStream.h>
#include <libzrtpcpp/ZrtpTextData.h>
#include <libzrtpcpp/ZrtpConfigure.h>
#include <libzrtpcpp/zrtpB64Decode.h>
#include <libzrtpcpp/zrtpB64Encode.h>
#include <srtp/SrtpHandler.h>
#include <srtp/CryptoContext.h>
#include <srtp/CryptoContextCtrl.h>
#include <cryptcommon/ZrtpRandom.h>
#include <crypto/hmac384.h>


#if defined(_WIN32) || defined(_WIN64)
# define snprintf _snprintf
#endif

// SRTP authentication tag length is 80 bits = 10 bytes
#define ZRTP_TUNNEL_AUTH_LEN  10
#define ZRTP_TUNNEL_LABEL     10
/*
 * The ABNF grammar for the crypto attribute is defined below (from RFC 4568):
 *
 *  "a=crypto:" tag 1*WSP crypto-suite 1*WSP key-params *(1*WSP session-param)
 *
 *  tag              = 1*9DIGIT
 */

/*
 * Buffer size for names and other strings inside the crypto string. The parse
 * format below restricts parsing to 99 char to provide space for the @c nul byte.
 */
#define MAX_INNER_LEN 100

/*
 * This format scans a received SDES crypto attribute string according to the
 * grammer shown above but without a "a=crypto:" prefix.
 *
 * The format string parses:
 * - %d - the tag as decimal value
 * - %s - the crypto suite name, limited to 99 chars (see MAX_INNER_LEN)
 * - %s - the key parameters, limited to 99 chars
 * - %n - the number of parsed characters to far. The pointer to the session
 *   parameters is: cryptoString + numParsedChars.
 */
static const char parseCrypto[] = "%d %99s %99s %n";

static const int64_t maxTagValue = 999999999;

static const int minElementsCrypto = 3;

/*
 * The ABNF grammar for the key-param (from RFC 4568):
 *
 *  key-param        = key-method ":" key-info
 *
 * The SRTP specific definitions:
 *
 *  key-method          = srtp-key-method
 *  key-info            = srtp-key-info
 *
 *  srtp-key-method     = "inline"
 *  srtp-key-info       = key-salt ["|" lifetime] ["|" mki]
 *
 */

/*
 * This format parses the key parameter string which is never longer than
 * 99 chars (see parse string above):
 * - the fixed string "inline:"
 * - %[A-Za-z0-9+/=] - base 64 characters of master key||master salt
 * - the fixed separator character '|'
 * - %[0-9^] - the lifetime infomration as string that contains digits and ^
 * - the fixed separator character '|'
 * - %[0-9]:%d - parses and strore MKI value and MKI length, separated by ':'
 *
 * If the key parameter string does not contain the operional fields lifetime
 * and MKI information the respective parameters are not filled.
 */
static const char parseKeyParam[] = " inline:%[A-Za-z0-9+/=]|%[0-9^]|%[0-9]:%d";

static const int minElementsKeyParam = 1;

typedef struct _cryptoMix {
    const char* name;
    int32_t hashLength;
    ZrtpSdesStream::sdesHmacTypeMix hashType;
} cryptoMix;

static const size_t MIX_HMAC_STRING_MIN_LEN = sizeof("HMAC-SHA-384");

static cryptoMix knownMixAlgos[] = {
    {"HMAC-SHA-384", 384, ZrtpSdesStream::MIX_HMAC_SHA},
    {NULL, 0, ZrtpSdesStream::MIX_NONE}
};

typedef struct _suite {
    ZrtpSdesStream::sdesSuites suite;
    const char *name;
    int32_t    keyLength;             // key length in bits
    int32_t    saltLength;            // salt lenght in bits
    int32_t    authKeyLength;         // authentication key length in bits
    const char *tagLength;            // tag type hs80 or hs32
    const char *cipher;               // aes1 or aes3
    uint32_t   b64length;             // length of b64 encoded key/saltstring
    uint64_t   defaultSrtpLifetime;   // key lifetimes in number of packets
    uint64_t   defaultSrtcpLifetime;
} suiteParam;

/* NOTE: the b64len of a 128 bit suite is 40, a 256bit suite uses 64 characters */
static suiteParam knownSuites[] = {
    {ZrtpSdesStream::AES_CM_128_HMAC_SHA1_32, "AES_CM_128_HMAC_SHA1_32", 128, 112, 160,
     hs32, "AES-128", 40, (uint64_t)1<<48, (uint64_t)1<<31
    },
    {ZrtpSdesStream::AES_CM_128_HMAC_SHA1_80, "AES_CM_128_HMAC_SHA1_80", 128, 112, 160,
     hs80, "AES-128", 40, (uint64_t)1<<48, (uint64_t)1<<31
    },
    {(ZrtpSdesStream::sdesSuites)0, NULL, 0, 0, 0, 0, 0, 0, 0, 0}
};

ZrtpSdesStream::ZrtpSdesStream(const sdesSuites s) :
    state(STREAM_INITALIZED), suite(s), recvSrtp(NULL), recvSrtcp(NULL), sendSrtp(NULL),
    sendSrtcp(NULL), srtcpIndex(0), recvZrtpTunnel(0), sendZrtpTunnel(0), cryptoMixHashLength(0), 
    cryptoMixHashType(MIX_NONE)  {
}

ZrtpSdesStream::~ZrtpSdesStream() {
    close();
}

void ZrtpSdesStream::close() {
    delete sendSrtp;
    sendSrtp = NULL;

    delete recvSrtp;
    recvSrtp = NULL;

    delete sendSrtcp;
    sendSrtp = NULL;

    delete recvSrtcp;
    recvSrtp = NULL;

    delete recvZrtpTunnel;
    recvZrtpTunnel = NULL;

    delete sendZrtpTunnel;
    sendZrtpTunnel = NULL;
}

bool ZrtpSdesStream::createSdes(char *cryptoString, size_t *maxLen, bool sipInvite) {

    if (sipInvite) {
        if (state != STREAM_INITALIZED)
            return false;
        tag = 1;
    }
    else {
        if (state != IN_PROFILE_READY)
            return false;
    }

    bool s = createSdesProfile(cryptoString, maxLen);
    if (!s)
        return s;

    if (sipInvite) {
        state = OUT_PROFILE_READY;
    }
    else {
        createSrtpContexts(sipInvite);
        state = SDES_SRTP_ACTIVE;
    }
    return s;
}

bool ZrtpSdesStream::parseSdes(const char *cryptoString, size_t length, bool sipInvite) {

    if (sipInvite) {
        if (state != OUT_PROFILE_READY)
            return false;
    }
    else {
        if (state != STREAM_INITALIZED)
            return false;
    }
    sdesSuites tmpSuite;
    int32_t tmpTag;

    bool s = parseCreateSdesProfile(cryptoString, length, &tmpSuite, &tmpTag);
    if (!s)
        return s;

    if (sipInvite) {
        // Check if answerer used same tag and suite as the offerer
        if (tmpTag != tag || suite != tmpSuite)
            return false;
        createSrtpContexts(sipInvite);
        state = SDES_SRTP_ACTIVE;
    }
    else {
        // Answerer stores tag and suite and uses it in createSdesProfile
        suite = tmpSuite;
        tag = tmpTag;
        state = IN_PROFILE_READY;
    }
    return s;
}

bool ZrtpSdesStream::outgoingRtp(uint8_t *packet, size_t length, size_t *newLength) {

    if (state != SDES_SRTP_ACTIVE || sendSrtp == NULL) {
        *newLength = length;
        return true;
    }
    bool rc = SrtpHandler::protect(sendSrtp, packet, length, newLength);
    if (rc)
        ;//protect++;
    return rc;
}

int ZrtpSdesStream::incomingRtp(uint8_t *packet, size_t length, size_t *newLength) {
    if (state != SDES_SRTP_ACTIVE || recvSrtp == NULL) {    // SRTP inactive, just return with newLength set
        *newLength = length;
        return 1;
    }
    int32_t rc = SrtpHandler::unprotect(recvSrtp, packet, length, newLength);
    if (rc == 1) {
//            unprotect++
    }
    else {
//            unprotectFailed++;
    }
    return rc;
}


bool ZrtpSdesStream::outgoingZrtpTunnel(uint8_t *packet, size_t length, size_t *newLength) {

    if (state != SDES_SRTP_ACTIVE || sendZrtpTunnel == NULL) {
        *newLength = length;
        return true;
    }
    bool rc = SrtpHandler::protect(sendZrtpTunnel, packet, length, newLength);
    if (rc)
        ;//protect++;
    return rc;
}

int ZrtpSdesStream::incomingZrtpTunnel(uint8_t *packet, size_t length, size_t *newLength) {
    if (state != SDES_SRTP_ACTIVE || recvZrtpTunnel == NULL) {    // SRTP inactive, just return with newLength set
        *newLength = length;
        return 1;
    }
    int32_t rc = SrtpHandler::unprotect(recvZrtpTunnel, packet, length, newLength);
    if (rc == 1) {
//            unprotect++
    }
    else {
//            unprotectFailed++;
    }
    return rc;
}



bool ZrtpSdesStream::outgoingRtcp(uint8_t *packet, size_t length, size_t *newLength) {
#if 0
SrtpHandler::protectCtrl(CryptoContextCtrl* pcc, uint8_t* buffer, size_t length, size_t* newLength, uint32_t *srtcpIndex)
#endif
    return false;
}

int ZrtpSdesStream::incomingSrtcp(uint8_t *packet, size_t length, size_t *newLength) {
#if 0
int32_t SrtpHandler::unprotectCtrl(CryptoContextCtrl* pcc, uint8_t* buffer, size_t length, size_t* newLength)
#endif
    return 0;
}

const char* ZrtpSdesStream::getCipher() {
    return knownSuites[suite].cipher;
}

const char* ZrtpSdesStream::getAuthAlgo() {
    if (strcmp(knownSuites[suite].tagLength, hs80) == 0)
        return "HMAC-SHA1 80 bit";
    else
        return "HMAC-SHA1 32 bit";
}

int ZrtpSdesStream::getCryptoMixAttribute(char *algoNames, size_t length) {

    if (length < MIX_HMAC_STRING_MIN_LEN)
        return 0;

    // In case we support more than one MIX profile select the correct one if the
    // application called setCryptoMixAttribute(...) and we already selected the one to use.
    if (cryptoMixHashType != MIX_NONE) {
        for (cryptoMix* cp = knownMixAlgos; cp->name != NULL; cp++) {
            if (cp->hashLength == cryptoMixHashLength && cp->hashType == cryptoMixHashType) {
                strcpy(algoNames, cp->name);
                return strlen(cp->name);
            }
        }
    }
    // TODO: enhance here to support multiple algorithms (concatenate strings into the buffer until buffer full)
    else {
        strcpy(algoNames, knownMixAlgos[0].name);
        return strlen(algoNames);
    }
    return 0;
}

bool ZrtpSdesStream::setCryptoMixAttribute(const char *algoNames) {

    int len = strlen(algoNames);
    if (len <= 0)
        return false;

    std::string algoIn(algoNames);
    algoIn += ' ';

    // split input name string and lookup if we support one of the offered algorithms
    // We take the first match.
    std::string delimiters = " ";
    size_t current;
    size_t next = -1;

    do {
        current = next + 1;
        next = algoIn.find_first_of(delimiters, current);
        if (next == std::string::npos)
            break;

        std::string tmps = algoIn.substr(current, next - current );
        const char* nm = tmps.c_str();

        for (cryptoMix* cp = knownMixAlgos; cp->name != NULL; cp++) {
            if (strncmp(cp->name, nm, strlen(cp->name)) == 0) {
                cryptoMixHashLength = cp->hashLength;
                cryptoMixHashType = cp->hashType;
                return true;
            }
        }
    } while (true);

    return false;
}

#ifdef WEAKRANDOM
/*
 * A standard random number generator that uses the portable random() system function.
 *
 * This should be enhanced to use a better random generator
 */
static int _random(unsigned char *output, size_t len)
{
    size_t i;

    for(i = 0; i < len; ++i )
        output[i] = random();

    return( 0 );
}
#else
#include <cryptcommon/ZrtpRandom.h>
static int _random(unsigned char *output, size_t len)
{
    return ZrtpRandom::getRandomData(output, len);
}
#endif

static int b64Encode(const uint8_t *binData, int32_t binLength, char *b64Data, int32_t b64Length)
{
    base64_encodestate _state;
    int codelength;

    base64_init_encodestate(&_state, 0);
    codelength = base64_encode_block(binData, binLength, b64Data, &_state);
    codelength += base64_encode_blockend(b64Data+codelength, &_state);

    return codelength;
}

static int b64Decode(const char *b64Data, int32_t b64length, uint8_t *binData, int32_t binLength)
{
    base64_decodestate _state;
    int codelength;

    base64_init_decodestate(&_state);
    codelength = base64_decode_block(b64Data, b64length, binData, &_state);
    return codelength;
}

void* createSha384HmacContext(uint8_t* key, int32_t keyLength);
void freeSha384HmacContext(void* ctx);
void hmacSha384Ctx(void* ctx, const uint8_t* data[], uint32_t dataLength[], uint8_t* mac, int32_t* macLength );

static int expand(uint8_t* prk, uint32_t prkLen, uint8_t* info, int32_t infoLen, int32_t L, uint32_t hashLen, uint8_t* outbuffer)
{
    int32_t n;
    uint8_t *T;
    void* hmacCtx;

    const uint8_t* data[4];      // 3 data pointers for HMAC data plus terminating NULL
    uint32_t dataLen[4];
    int32_t dataIdx = 0;

    uint8_t counter;
    int32_t macLength;

    if (prkLen < hashLen)
        return -1;

    n = (L + (hashLen-1)) / hashLen;

    // T points to buffer that holds concatenated T(1) || T(2) || ... T(N))
    T = reinterpret_cast<uint8_t*>(malloc(n * hashLen));

    if (hashLen == 384/8)
        hmacCtx = createSha384HmacContext(prk, prkLen);
    else
        return -1;

    // Prepare first HMAC. T(0) has zero length, thus we ignore it in first run.
    // After first run use its output (T(1)) as first data in next HMAC run.
    for (int i = 1; i <= n; i++) {
        if (infoLen > 0 && info != NULL) {
            data[dataIdx] = info;
            dataLen[dataIdx++] = infoLen;
        }
        counter = i & 0xff;
        data[dataIdx] = &counter;
        dataLen[dataIdx++] = 1;

        data[dataIdx] = NULL;
        dataLen[dataIdx++] = 0;

        if (hashLen == 384/8)
            hmacSha384Ctx(hmacCtx, data, dataLen, T + ((i-1) * hashLen), &macLength);

        // Use output of previous hash run as first input of next hash run
        dataIdx = 0;
        data[dataIdx] = T + ((i-1) * hashLen);
        dataLen[dataIdx++] = hashLen;
    }
    freeSha384HmacContext(hmacCtx);
    memcpy(outbuffer, T, L);
    free(T);
    return 0;
}

void ZrtpSdesStream::computeMixedKeys(bool sipInvite) {
    uint8_t salt[MAX_SALT_LEN*2];
    uint8_t ikm[MAX_KEY_LEN*2];

    // Concatenate the existing salt and key data. Depending on our role we have to change
    // the order of the data.
    if (sipInvite) {             // We are offerer, use local created data as mso and mko, so they go first
        memcpy(salt, &localKeySalt[localKeyLenBytes], localSaltLenBytes);
        memcpy(&salt[localSaltLenBytes], &remoteKeySalt[remoteKeyLenBytes], remoteSaltLenBytes);

        memcpy(ikm, localKeySalt, localKeyLenBytes);
        memcpy(&ikm[localKeyLenBytes], remoteKeySalt, remoteKeyLenBytes);
    }
    else {
        memcpy(salt, &remoteKeySalt[remoteKeyLenBytes], remoteSaltLenBytes);
        memcpy(&salt[remoteSaltLenBytes], &localKeySalt[localKeyLenBytes], localSaltLenBytes);

        memcpy(ikm, remoteKeySalt, remoteKeyLenBytes);
        memcpy(&ikm[remoteKeyLenBytes], localKeySalt, localKeyLenBytes);
    }
    uint32_t saltLen = localSaltLenBytes + remoteSaltLenBytes;
    uint32_t keyLen = localKeyLenBytes + remoteKeyLenBytes;
    uint32_t L = saltLen + keyLen;

    uint8_t prk[MAX_DIGEST_LENGTH];
    uint32_t prkLen;

    switch(cryptoMixHashType) {
        case MIX_HMAC_SHA:
            if (cryptoMixHashLength == 384)
                hmac_sha384(salt, saltLen, ikm, keyLen, prk, &prkLen);
            else
                return;
            break;

        case MIX_MAC_SKEIN:
            return;

        default:
            return;
    }

    uint8_t T[(MAX_SALT_LEN + MAX_KEY_LEN)*2] = {0};
    expand(prk, prkLen, NULL, 0, L, cryptoMixHashLength/8, T);

    // We have a new set of SRTP key data now, replace the old with the new.
    int32_t offset = 0;
    if (sipInvite) {    // We are offerer, replace local created data with mso and mko, remote with msa, mka
        memcpy(&localKeySalt[localKeyLenBytes], T, localSaltLenBytes);
        offset += localSaltLenBytes;
        memcpy(&remoteKeySalt[remoteKeyLenBytes], &T[offset], remoteSaltLenBytes);
        offset += remoteSaltLenBytes;

        memcpy(localKeySalt, &T[offset], localKeyLenBytes);
        offset += localKeyLenBytes;
        memcpy(remoteKeySalt, &T[offset], remoteKeyLenBytes);
    }
    else {            // We are answerer, replace remote data with mso and mko, local data with msa, mka
        memcpy(&remoteKeySalt[remoteKeyLenBytes], T, remoteSaltLenBytes);
        offset += remoteSaltLenBytes;
        memcpy(&localKeySalt[localKeyLenBytes], &T[offset], localSaltLenBytes);
        offset += localSaltLenBytes;

        memcpy(remoteKeySalt, &T[offset], remoteKeyLenBytes);
        offset += remoteKeyLenBytes;
        memcpy(localKeySalt, &T[offset], localKeyLenBytes);
    }
}

void ZrtpSdesStream::createSrtpContexts(bool sipInvite) {

    if (cryptoMixHashType != MIX_NONE) {
        computeMixedKeys(sipInvite);
    }

    sendSrtp = new CryptoContext(0,                     // SSRC (used for lookup)
                                 0,                     // Roll-Over-Counter (ROC)
                                 0L,                    // keyderivation << 48,
                                 localCipher,                // encryption algo
                                 localAuthn,                 // authtentication algo
                                 localKeySalt,               // Master Key
                                 localKeyLenBytes,           // Master Key length
                                 &localKeySalt[localKeyLenBytes], // Master Salt
                                 localSaltLenBytes,          // Master Salt length
                                 localKeyLenBytes,           // encryption keylen
                                 localAuthKeyLen,            // authentication key len (HMAC key lenght)
                                 localSaltLenBytes,          // session salt len
                                 localTagLength);            // authentication tag len
    sendSrtp->deriveSrtpKeys(0L);

    sendZrtpTunnel = new CryptoContext(0,                     // SSRC (used for lookup)
                                 0,                     // Roll-Over-Counter (ROC)
                                 0L,                    // keyderivation << 48,
                                 localCipher,                // encryption algo
                                 localAuthn,                 // authtentication algo
                                 localKeySalt,               // Master Key
                                 localKeyLenBytes,           // Master Key length
                                 &localKeySalt[localKeyLenBytes], // Master Salt
                                 localSaltLenBytes,          // Master Salt length
                                 localKeyLenBytes,           // encryption keylen
                                 localAuthKeyLen,            // authentication key len (HMAC key lenght)
                                 localSaltLenBytes,          // session salt len
                                 ZRTP_TUNNEL_AUTH_LEN);      // authentication tag len

    sendZrtpTunnel->setLabelbase(ZRTP_TUNNEL_LABEL);
    sendZrtpTunnel->deriveSrtpKeys(0L);
    memset(localKeySalt, 0, sizeof(localKeySalt));

    recvSrtp = new CryptoContext(0,                     // SSRC (used for lookup)
                                 0,                     // Roll-Over-Counter (ROC)
                                 0L,                    // keyderivation << 48,
                                 remoteCipher,                // encryption algo
                                 remoteAuthn,                 // authtentication algo
                                 remoteKeySalt,               // Master Key
                                 remoteKeyLenBytes,           // Master Key length
                                 &remoteKeySalt[remoteKeyLenBytes], // Master Salt
                                 remoteSaltLenBytes,          // Master Salt length
                                 remoteKeyLenBytes,           // encryption keylen
                                 remoteAuthKeyLen,            // authentication key len (HMAC key lenght)
                                 remoteSaltLenBytes,          // session salt len
                                 remoteTagLength);            // authentication tag len
    recvSrtp->deriveSrtpKeys(0L);

    recvZrtpTunnel = new CryptoContext(0,                     // SSRC (used for lookup)
                                 0,                     // Roll-Over-Counter (ROC)
                                 0L,                    // keyderivation << 48,
                                 remoteCipher,                // encryption algo
                                 remoteAuthn,                 // authtentication algo
                                 remoteKeySalt,               // Master Key
                                 remoteKeyLenBytes,           // Master Key length
                                 &remoteKeySalt[remoteKeyLenBytes], // Master Salt
                                 remoteSaltLenBytes,          // Master Salt length
                                 remoteKeyLenBytes,           // encryption keylen
                                 remoteAuthKeyLen,            // authentication key len (HMAC key lenght)
                                 remoteSaltLenBytes,          // session salt len
                                 ZRTP_TUNNEL_AUTH_LEN);       // authentication tag len

    recvZrtpTunnel->setLabelbase(ZRTP_TUNNEL_LABEL);
    recvZrtpTunnel->deriveSrtpKeys(0L);
    memset(remoteKeySalt, 0, sizeof(remoteKeySalt));
}

bool ZrtpSdesStream::createSdesProfile(char *cryptoString, size_t *maxLen) {

    char b64keySalt[(MAX_KEY_LEN + MAX_SALT_LEN) * 2] = {'\0'};
    uint32_t sidx;
    int32_t b64Len;

    for (sidx = 0; knownSuites[sidx].name != NULL; sidx++) {  // Lookup crypto suite parameters
        if (knownSuites[sidx].suite == suite)
            break;
    }
    if (sidx >= sizeof(knownSuites)/sizeof(struct _suite)) {
        return false;
    }
    suiteParam *pSuite = &knownSuites[sidx];
    _random(localKeySalt, sizeof(localKeySalt));

    AlgorithmEnum& auth = zrtpAuthLengths.getByName(pSuite->tagLength);
    localAuthn = SrtpAuthenticationSha1Hmac;
    localAuthKeyLen = pSuite->authKeyLength / 8;
    localTagLength = auth.getKeylen() / 8;

    // If SDES will support other encryption algos - get it here based on
    // the algorithm name in suite
    localCipher = SrtpEncryptionAESCM;

    localKeyLenBytes = pSuite->keyLength / 8;
    localSaltLenBytes = pSuite->saltLength / 8;

    if (tag == -1)
        tag = 1;

    // Get B64 code for master key and master salt and then construct the SDES crypto string
    b64Len = b64Encode(localKeySalt, localKeyLenBytes + localSaltLenBytes, b64keySalt, sizeof(b64keySalt));
    b64keySalt[b64Len] = '\0';
    memset(cryptoString, 0, *maxLen);
    *maxLen = snprintf(cryptoString, *maxLen-1, "%d %s inline:%s", tag, pSuite->name, b64keySalt);

    return true;
}

bool ZrtpSdesStream::parseCreateSdesProfile(const char *cryptoStr, size_t length, sdesSuites *parsedSuite, int32_t *outTag) {
    int elements,  i;
    int charsScanned;
    int mkiLength = 0;
    uint32_t sidx;

    char cryptoString[MAX_CRYPT_STRING_LEN+1] = {'\0'};

    /* Parsed strings */
    char suiteName[MAX_INNER_LEN]  = {'\0'};
    char keyParams[MAX_INNER_LEN]  = {'\0'};
    char keySaltB64[MAX_INNER_LEN] = {'\0'};
    char lifetime[MAX_INNER_LEN]   = {'\0'};
    char mkiVal[MAX_INNER_LEN]     = {'\0'};

    if (length == 0)
        length = strlen(cryptoStr);

    if (length > MAX_CRYPT_STRING_LEN) {
        return false;
    }
    memcpy(cryptoString, cryptoStr, length);   // make own copy, null terminated

    *outTag = -1;
    elements = sscanf(cryptoString, parseCrypto, outTag, suiteName, keyParams, &charsScanned);

    if (elements < minElementsCrypto) {        // Do we have enough elements in the string
        return false;
    }

    for (sidx = 0; knownSuites[sidx].name != NULL; sidx++) {  // Lookup crypto suite
        if (!strcmp(knownSuites[sidx].name, suiteName))
            break;
    }
    if (sidx >= sizeof(knownSuites)/sizeof(struct _suite)) {
        return false;
    }
    suiteParam *pSuite = &knownSuites[sidx];
    *parsedSuite = pSuite->suite;

    /* Now scan the key parameters */
    elements = sscanf(keyParams, parseKeyParam, keySaltB64, lifetime, mkiVal, &mkiLength);

    if (elements != minElementsKeyParam) {     // Currently we only accept key||salt B64 string, no other parameters 
        return false;
    }

    remoteKeyLenBytes = pSuite->keyLength / 8;
    remoteSaltLenBytes = pSuite->saltLength / 8;

    if (strlen(keySaltB64) != pSuite->b64length) {  // Check if key||salt B64 string hast the correct length
        return false;
    }
    i = b64Decode(keySaltB64, pSuite->b64length, remoteKeySalt, remoteKeyLenBytes + remoteSaltLenBytes);

    if (i != (remoteKeyLenBytes + remoteSaltLenBytes)) {  // Did the B64 decode delivered enough data for key||salt
        return false;
    }

    AlgorithmEnum& auth = zrtpAuthLengths.getByName(pSuite->tagLength);
    remoteAuthn = SrtpAuthenticationSha1Hmac;
    remoteAuthKeyLen = pSuite->authKeyLength / 8;
    remoteTagLength = auth.getKeylen() / 8;

    // If SDES will support other encryption algos - get it here based on
    // the algorithm name in suite
    remoteCipher = SrtpEncryptionAESCM;

    return true;
}