summaryrefslogtreecommitdiff
path: root/src/Zrtp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Zrtp.cpp')
-rw-r--r--src/Zrtp.cpp2527
1 files changed, 2527 insertions, 0 deletions
diff --git a/src/Zrtp.cpp b/src/Zrtp.cpp
new file mode 100644
index 0000000..2002462
--- /dev/null
+++ b/src/Zrtp.cpp
@@ -0,0 +1,2527 @@
+/*
+ Copyright (C) 2006-2009 Werner Dittmann
+
+ 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 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/>.
+*/
+
+/*F
+ * Authors: Werner Dittmann <Werner.Dittmann@t-online.de>
+ */
+#include <sstream>
+
+#include <libzrtpcpp/crypto/ZrtpDH.h>
+#include <libzrtpcpp/crypto/hmac256.h>
+#include <libzrtpcpp/crypto/sha256.h>
+#include <libzrtpcpp/crypto/hmac384.h>
+#include <libzrtpcpp/crypto/sha384.h>
+#include <libzrtpcpp/crypto/aesCFB.h>
+#include <libzrtpcpp/crypto/twoCFB.h>
+
+#include <libzrtpcpp/ZRtp.h>
+#include <libzrtpcpp/ZrtpStateClass.h>
+#include <libzrtpcpp/ZIDFile.h>
+#include <libzrtpcpp/ZIDRecord.h>
+#include <libzrtpcpp/Base32.h>
+
+using namespace GnuZrtpCodes;
+
+/* disabled...but used in testing and debugging, probably should have a
+ controlling #define...
+ *
+static void hexdump(const char* title, const unsigned char *s, int l) {
+ int n=0;
+
+ if (s == NULL) return;
+
+ fprintf(stderr, "%s",title);
+ for( ; n < l ; ++n)
+ {
+ if((n%16) == 0)
+ fprintf(stderr, "\n%04x",n);
+ fprintf(stderr, " %02x",s[n]);
+ }
+ fprintf(stderr, "\n");
+}
+ */
+
+/*
+ * This method simplifies detection of libzrtpcpp inside Automake, configure
+ * and friends
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+ int ZrtpAvailable()
+ {
+ return 1;
+ }
+#ifdef __cplusplus
+}
+#endif
+
+ZRtp::ZRtp(uint8_t *myZid, ZrtpCallback *cb, std::string id, ZrtpConfigure* config, bool mitmm, bool sasSignSupport):
+ callback(cb), dhContext(NULL), DHss(NULL), auxSecret(NULL), auxSecretLength(0), rs1Valid(false),
+ rs2Valid(false), msgShaContext(NULL), multiStream(false), multiStreamAvailable(false), pbxSecretTmp(NULL),
+ configureAlgos(*config) {
+
+ enableMitmEnrollment = config->isTrustedMitM();
+ paranoidMode = config->isParanoidMode();
+
+ // setup the implicit hash function pointers and length
+ hashLengthImpl = SHA256_DIGEST_LENGTH;
+ hashFunctionImpl = sha256;
+ hashListFunctionImpl = sha256;
+
+ hmacFunctionImpl = hmac_sha256;
+ hmacListFunctionImpl = hmac_sha256;
+
+ /*
+ * Generate H0 as a random number (256 bits, 32 bytes) and then
+ * the hash chain, refer to chapter 9. Use the implicit hash function.
+ */
+ randomZRTP(H0, HASH_IMAGE_SIZE);
+ sha256(H0, HASH_IMAGE_SIZE, H1); // hash H0 and generate H1
+ sha256(H1, HASH_IMAGE_SIZE, H2); // H2
+ sha256(H2, HASH_IMAGE_SIZE, H3); // H3
+
+ zrtpHello.configureHello(&configureAlgos);
+ zrtpHello.setH3(H3); // set H3 in Hello, included in helloHash
+
+ memcpy(zid, myZid, ZID_SIZE);
+ zrtpHello.setZid(zid);
+
+ if (mitmm) // this session acts for a trusted MitM (PBX)
+ zrtpHello.setMitmMode();
+
+ if (sasSignSupport) // the application supports SAS signing
+ zrtpHello.setSasSign();
+
+ setClientId(id); // set id, compute HMAC and final helloHash
+
+ stateEngine = new ZrtpStateClass(this);
+}
+
+ZRtp::~ZRtp() {
+ stopZrtp();
+ if (DHss != NULL) {
+ delete DHss;
+ DHss = NULL;
+ }
+ if (stateEngine != NULL) {
+ delete stateEngine;
+ stateEngine = NULL;
+ }
+ if (dhContext != NULL) {
+ delete dhContext;
+ dhContext = NULL;
+ }
+ if (msgShaContext != NULL) {
+ closeHashCtx(msgShaContext, NULL);
+ msgShaContext = NULL;
+ }
+ if (auxSecret != NULL) {
+ delete auxSecret;
+ auxSecret = NULL;
+ auxSecretLength = 0;
+ }
+ memset(hmacKeyI, 0, MAX_DIGEST_LENGTH);
+ memset(hmacKeyR, 0, MAX_DIGEST_LENGTH);
+
+ memset(zrtpKeyI, 0, MAX_DIGEST_LENGTH);
+ memset(zrtpKeyR, 0, MAX_DIGEST_LENGTH);
+ /*
+ * Clear the Initiator's srtp key and salt
+ */
+ memset(srtpKeyI, 0, MAX_DIGEST_LENGTH);
+ memset(srtpSaltI, 0, MAX_DIGEST_LENGTH);
+ /*
+ * Clear he Responder's srtp key and salt
+ */
+ memset(srtpKeyR, 0, MAX_DIGEST_LENGTH);
+ memset(srtpSaltR, 0, MAX_DIGEST_LENGTH);
+
+ memset(zrtpSession, 0, MAX_DIGEST_LENGTH);
+}
+
+void ZRtp::processZrtpMessage(uint8_t *message, uint32_t pSSRC) {
+ Event_t ev;
+
+ peerSSRC = pSSRC;
+ ev.type = ZrtpPacket;
+ ev.packet = message;
+
+ if (stateEngine != NULL) {
+ stateEngine->processEvent(&ev);
+ }
+}
+
+void ZRtp::processTimeout() {
+ Event_t ev;
+
+ ev.type = Timer;
+ if (stateEngine != NULL) {
+ stateEngine->processEvent(&ev);
+ }
+}
+
+#ifdef oldgoclear
+bool ZRtp::handleGoClear(uint8_t *message)
+{
+ char *msg, first, last;
+
+ msg = (char *)message + 4;
+ first = tolower(*msg);
+ last = tolower(*(msg+6));
+
+ if (first == 'g' && last == 'r') {
+ Event_t ev;
+
+ ev.type = ZrtpGoClear;
+ ev.packet = message;
+ if (stateEngine != NULL) {
+ stateEngine->processEvent(&ev);
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+#endif
+
+void ZRtp::startZrtpEngine() {
+ Event_t ev;
+
+ if (stateEngine != NULL && stateEngine->inState(Initial)) {
+ ev.type = ZrtpInitial;
+ stateEngine->processEvent(&ev);
+ }
+}
+
+void ZRtp::stopZrtp() {
+ Event_t ev;
+
+ if (stateEngine != NULL) {
+ ev.type = ZrtpClose;
+ stateEngine->processEvent(&ev);
+ }
+}
+
+bool ZRtp::inState(int32_t state)
+{
+ if (stateEngine != NULL) {
+ return stateEngine->inState(state);
+ }
+ else {
+ return false;
+ }
+}
+
+ZrtpPacketHello* ZRtp::prepareHello() {
+ return &zrtpHello;
+}
+
+ZrtpPacketHelloAck* ZRtp::prepareHelloAck() {
+ return &zrtpHelloAck;
+}
+
+/*
+ * At this point we will assume the role of Initiator. This role may change
+ * in case we have a commit-clash. Refer to chapter 5.2 in the spec how
+ * to break this tie.
+ */
+ZrtpPacketCommit* ZRtp::prepareCommit(ZrtpPacketHello *hello, uint32_t* errMsg) {
+
+ sendInfo(Info, InfoHelloReceived);
+
+ if (memcmp(hello->getVersion(), zrtpVersion, ZRTP_WORD_SIZE-1) != 0) {
+ *errMsg = UnsuppZRTPVersion;
+ return NULL;
+ }
+ // Save our peer's (presumably the Responder) ZRTP id
+ memcpy(peerZid, hello->getZid(), ZID_SIZE);
+ if (memcmp(peerZid, zid, ZID_SIZE) == 0) { // peers have same ZID????
+ *errMsg = EqualZIDHello;
+ return NULL;
+ }
+ memcpy(peerH3, hello->getH3(), HASH_IMAGE_SIZE);
+
+ /*
+ * The Following section extracts the algorithm from the peer's Hello
+ * packet. Always the preferend offered algorithms are
+ * used. If the received Hello does not contain algo specifiers
+ * or offers only unsupported optional algos then replace
+ * these with mandatory algos and put them into the Commit packet.
+ * Refer to the findBest*() functions.
+ * If this is a MultiStream ZRTP object then do not get the cipher,
+ * authentication from hello packet but use the pre-initialized values
+ * as proposed by the standard. If we switch to responder mode the
+ * commit packet may contain other algos - see function
+ * prepareConfirm2MultiStream(...).
+ */
+ sasType = findBestSASType(hello);
+
+ if (!multiStream) {
+ authLength = findBestAuthLen(hello);
+ pubKey = findBestPubkey(hello);
+ cipher = findBestCipher(hello, pubKey);
+ hash = findBestHash(hello);
+ multiStreamAvailable = checkMultiStream(hello);
+ }
+ else {
+ if (checkMultiStream(hello)) {
+ return prepareCommitMultiStream(hello);
+ }
+ else {
+ // we are in multi-stream but peer does not offer multi-stream
+ // return error code to other party - unsupported PK, must be Mult
+ *errMsg = UnsuppPKExchange;
+ return NULL;
+ }
+ }
+ setNegotiatedHash(hash);
+
+ // Modify here when introducing new DH key agreement, for example
+ // elliptic curves.
+ dhContext = new ZrtpDH(pubKey->getName());
+ dhContext->generatePublicKey();
+
+ dhContext->getPubKeyBytes(pubKeyBytes);
+ sendInfo(Info, InfoCommitDHGenerated);
+
+ // Prepare IV data that we will use during confirm packet encryption.
+ randomZRTP(randomIV, sizeof(randomIV));
+
+ /*
+ * Prepare our DHPart2 packet here. Required to compute HVI. If we stay
+ * in Initiator role then we reuse this packet later in prepareDHPart2().
+ * To create this DH packet we have to compute the retained secret ids
+ * first. Thus get our peer's retained secret data first.
+ */
+ ZIDRecord zidRec(peerZid);
+ ZIDFile *zidFile = ZIDFile::getInstance();
+ zidFile->getRecord(&zidRec);
+
+ //Compute the Initator's and Responder's retained secret ids.
+ computeSharedSecretSet(zidRec);
+
+ // Check if a PBX application set the MitM flag.
+ if (hello->isMitmMode()) {
+ mitmSeen = true;
+ }
+ // Flag to record that fact that we have a MitM key of the other peer.
+ peerIsEnrolled = zidRec.isMITMKeyAvailable();
+
+ signSasSeen = hello->isSasSign();
+ // Construct a DHPart2 message (Initiator's DH message). This packet
+ // is required to compute the HVI (Hash Value Initiator), refer to
+ // chapter 5.4.1.1.
+
+ // Fill the values in the DHPart2 packet
+ zrtpDH2.setPubKeyType(pubKey->getName());
+ zrtpDH2.setMessageType((uint8_t*)DHPart2Msg);
+ zrtpDH2.setRs1Id(rs1IDi);
+ zrtpDH2.setRs2Id(rs2IDi);
+ zrtpDH2.setAuxSecretId(auxSecretIDi);
+ zrtpDH2.setPbxSecretId(pbxSecretIDi);
+ zrtpDH2.setPv(pubKeyBytes);
+ zrtpDH2.setH1(H1);
+
+ int32_t len = zrtpDH2.getLength() * ZRTP_WORD_SIZE;
+
+ // Compute HMAC over DH2, excluding the HMAC field (HMAC_SIZE)
+ // and store in DH2. Key to HMAC is H0, use HASH_IMAGE_SIZE bytes only.
+ // Must use implicit HMAC functions.
+ uint8_t hmac[IMPL_MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+ hmacFunctionImpl(H0, HASH_IMAGE_SIZE, (uint8_t*)zrtpDH2.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen);
+ zrtpDH2.setHMAC(hmac);
+
+ // Compute the HVI, refer to chapter 5.4.1.1 of the specification
+ computeHvi(&zrtpDH2, hello);
+
+ zrtpCommit.setZid(zid);
+ zrtpCommit.setHashType((uint8_t*)hash->getName());
+ zrtpCommit.setCipherType((uint8_t*)cipher->getName());
+ zrtpCommit.setAuthLen((uint8_t*)authLength->getName());
+ zrtpCommit.setPubKeyType((uint8_t*)pubKey->getName());
+ zrtpCommit.setSasType((uint8_t*)sasType->getName());
+ zrtpCommit.setHvi(hvi);
+ zrtpCommit.setH2(H2);
+
+ len = zrtpCommit.getLength() * ZRTP_WORD_SIZE;
+
+ // Compute HMAC over Commit, excluding the HMAC field (HMAC_SIZE)
+ // and store in Hello. Key to HMAC is H1, use HASH_IMAGE_SIZE bytes only.
+ // Must use implicit HMAC functions.
+ hmacFunctionImpl(H1, HASH_IMAGE_SIZE, (uint8_t*)zrtpCommit.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen);
+ zrtpCommit.setHMAC(hmac);
+
+ // hash first messages to produce overall message hash
+ // First the Responder's Hello message, second the Commit (always Initator's).
+ // Must use negotiated hash.
+ int32_t helloLen = hello->getLength() * ZRTP_WORD_SIZE;
+ msgShaContext = createHashCtx();
+ hashCtxFunction(msgShaContext, (unsigned char*)hello->getHeaderBase(), helloLen);
+ hashCtxFunction(msgShaContext, (unsigned char*)zrtpCommit.getHeaderBase(), len);
+
+ // store Hello data temporarily until we can check HMAC after receiving Commit as
+ // Responder or DHPart1 as Initiator
+ storeMsgTemp(hello);
+
+ // calculate hash over the received Hello packet - is peer's hello hash.
+ // Use implicit hash algorithm
+ hashFunctionImpl((unsigned char*)hello->getHeaderBase(), helloLen, peerHelloHash);
+ memcpy(peerHelloVersion, hello->getVersion(), ZRTP_WORD_SIZE);
+ peerHelloVersion[ZRTP_WORD_SIZE] = 0;
+
+ return &zrtpCommit;
+}
+
+ZrtpPacketCommit* ZRtp::prepareCommitMultiStream(ZrtpPacketHello *hello) {
+
+ randomZRTP(hvi, ZRTP_WORD_SIZE*4); // This is the Multi-Stream NONCE size
+
+ zrtpCommit.setZid(zid);
+ zrtpCommit.setHashType((uint8_t*)hash->getName());
+ zrtpCommit.setCipherType((uint8_t*)cipher->getName());
+ zrtpCommit.setAuthLen((uint8_t*)authLength->getName());
+ zrtpCommit.setPubKeyType((uint8_t*)"Mult"); // this is fixed because of Multi Stream mode
+ zrtpCommit.setSasType((uint8_t*)sasType->getName());
+ zrtpCommit.setNonce(hvi);
+ zrtpCommit.setH2(H2);
+
+ int32_t len = zrtpCommit.getLength() * ZRTP_WORD_SIZE;
+
+ // Compute HMAC over Commit, excluding the HMAC field (HMAC_SIZE)
+ // and store in Hello. Key to HMAC is H1, use HASH_IMAGE_SIZE bytes only.
+ // Must use the implicit HMAC function.
+ uint8_t hmac[IMPL_MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+ hmacFunctionImpl(H1, HASH_IMAGE_SIZE, (uint8_t*)zrtpCommit.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen);
+ zrtpCommit.setHMACMulti(hmac);
+
+
+ // hash first messages to produce overall message hash
+ // First the Responder's Hello message, second the Commit
+ // (always Initator's).
+ // Must use the negotiated hash.
+ msgShaContext = createHashCtx();
+
+ int32_t helloLen = hello->getLength() * ZRTP_WORD_SIZE;
+ hashCtxFunction(msgShaContext, (unsigned char*)hello->getHeaderBase(), helloLen);
+ hashCtxFunction(msgShaContext, (unsigned char*)zrtpCommit.getHeaderBase(), len);
+
+ // store Hello data temporarily until we can check HMAC after receiving Commit as
+ // Responder or DHPart1 as Initiator
+ storeMsgTemp(hello);
+
+ // calculate hash over the received Hello packet - is peer's hello hash.
+ // Use implicit hash algorithm
+ hashFunctionImpl((unsigned char*)hello->getHeaderBase(), helloLen, peerHelloHash);
+ memcpy(peerHelloVersion, hello->getVersion(), ZRTP_WORD_SIZE);
+ peerHelloVersion[ZRTP_WORD_SIZE] = 0;
+
+ return &zrtpCommit;
+}
+
+/*
+ * At this point we will take the role of the Responder. We may have been in
+ * the role of the Initiator before and already sent a commit packet that
+ * clashed with a commit packet from our peer. If our HVI was lower than our
+ * peer's HVI then we switched to Responder and handle our peer's commit packet
+ * here. This method takes care to delete and refresh data left over from a
+ * possible Initiator preparation. This belongs to prepared DH data, message
+ * hash SHA context
+ */
+ZrtpPacketDHPart* ZRtp::prepareDHPart1(ZrtpPacketCommit *commit, uint32_t* errMsg) {
+
+ sendInfo(Info, InfoRespCommitReceived);
+
+ // The following code check the hash chain according chapter 10 to detect
+ // false ZRTP packets.
+ // Must use the implicit hash function.
+ uint8_t tmpH3[IMPL_MAX_DIGEST_LENGTH];
+ memcpy(peerH2, commit->getH2(), HASH_IMAGE_SIZE);
+ hashFunctionImpl(peerH2, HASH_IMAGE_SIZE, tmpH3);
+
+ if (memcmp(tmpH3, peerH3, HASH_IMAGE_SIZE) != 0) {
+ *errMsg = IgnorePacket;
+ return NULL;
+ }
+
+ // Check HMAC of previous Hello packet stored in temporary buffer. The
+ // HMAC key of peer's Hello packet is peer's H2 that is contained in the
+ // Commit packet. Refer to chapter 9.1.
+ if (!checkMsgHmac(peerH2)) {
+ sendInfo(Severe, SevereHelloHMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+
+ // check if we support the commited Cipher type
+ AlgorithmEnum* cp = &zrtpSymCiphers.getByName((const char*)commit->getCipherType());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppCiphertype;
+ return NULL;
+ }
+ cipher = cp;
+
+ // check if we support the commited Authentication length
+ cp = &zrtpAuthLengths.getByName((const char*)commit->getAuthLen());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppSRTPAuthTag;
+ return NULL;
+ }
+ authLength = cp;
+
+ // check if we support the commited hash type
+ cp = &zrtpHashes.getByName((const char*)commit->getHashType());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppHashType;
+ return NULL;
+ }
+ // check if the peer's commited hash is the same that we used when
+ // preparing our commit packet. If not do the necessary resets and
+ // recompute some data.
+ if (*(int32_t*)(hash->getName()) != *(int32_t*)(cp->getName())) {
+ hash = cp;
+ setNegotiatedHash(hash);
+
+ ZIDRecord zidRec(peerZid);
+ ZIDFile *zidFile = ZIDFile::getInstance();
+ zidFile->getRecord(&zidRec);
+
+ // Compute the Initator's and Responder's retained secret ids
+ // with the committed hash.
+ computeSharedSecretSet(zidRec);
+ }
+
+ // check if we support the commited pub key type
+ cp = &zrtpPubKeys.getByName((const char*)commit->getPubKeysType());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppPKExchange;
+ return NULL;
+ }
+ pubKey = cp;
+
+ // check if we support the commited SAS type
+ cp = &zrtpSasTypes.getByName((const char*)commit->getSasType());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppSASScheme;
+ return NULL;
+ }
+ sasType = cp;
+
+ // dhContext cannot be NULL - always setup during prepareCommit()
+ // check if we can use the dhContext prepared by prepareCOmmit(),
+ // if not delete old DH context and generate new one
+ // The algorithm names are 4 chars only, thus we can cast to int32_t
+ if (*(int32_t*)(dhContext->getDHtype()) != *(int32_t*)(pubKey->getName())) {
+ delete dhContext;
+ dhContext = new ZrtpDH(pubKey->getName());
+ dhContext->generatePublicKey();
+ }
+ sendInfo(Info, InfoDH1DHGenerated);
+
+ dhContext->getPubKeyBytes(pubKeyBytes);
+
+ // Setup a DHPart1 packet.
+ zrtpDH1.setPubKeyType(pubKey->getName());
+ zrtpDH1.setMessageType((uint8_t*)DHPart1Msg);
+ zrtpDH1.setRs1Id(rs1IDr);
+ zrtpDH1.setRs2Id(rs2IDr);
+ zrtpDH1.setAuxSecretId(auxSecretIDr);
+ zrtpDH1.setPbxSecretId(pbxSecretIDr);
+ zrtpDH1.setPv(pubKeyBytes);
+ zrtpDH1.setH1(H1);
+
+ int32_t len = zrtpDH1.getLength() * ZRTP_WORD_SIZE;
+
+ // Compute HMAC over DHPart1, excluding the HMAC field (HMAC_SIZE)
+ // and store in DHPart1.
+ // Use implicit Hash function
+ uint8_t hmac[IMPL_MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+ hmacFunctionImpl(H0, HASH_IMAGE_SIZE, (uint8_t*)zrtpDH1.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen);
+ zrtpDH1.setHMAC(hmac);
+
+ // We are definitly responder. Save the peer's hvi for later compare.
+ myRole = Responder;
+ memcpy(peerHvi, commit->getHvi(), HVI_SIZE);
+
+ // We are responder. Release a possibly pre-computed SHA context
+ // because this was prepared for Initiator. Then create a new one.
+ if (msgShaContext != NULL) {
+ closeHashCtx(msgShaContext, NULL);
+ }
+ msgShaContext = createHashCtx();
+
+ // Hash messages to produce overall message hash:
+ // First the Responder's (my) Hello message, second the Commit
+ // (always Initator's), then the DH1 message (which is always a
+ // Responder's message).
+ // Must use negotiated hash
+ hashCtxFunction(msgShaContext, (unsigned char*)zrtpHello.getHeaderBase(), zrtpHello.getLength() * ZRTP_WORD_SIZE);
+ hashCtxFunction(msgShaContext, (unsigned char*)commit->getHeaderBase(), commit->getLength() * ZRTP_WORD_SIZE);
+ hashCtxFunction(msgShaContext, (unsigned char*)zrtpDH1.getHeaderBase(), zrtpDH1.getLength() * ZRTP_WORD_SIZE);
+
+ // store Commit data temporarily until we can check HMAC after we got DHPart2
+ storeMsgTemp(commit);
+
+ return &zrtpDH1;
+}
+
+/*
+ * At this point we will take the role of the Initiator.
+ */
+ZrtpPacketDHPart* ZRtp::prepareDHPart2(ZrtpPacketDHPart *dhPart1, uint32_t* errMsg) {
+
+ uint8_t* pvr;
+
+ sendInfo(Info, InfoInitDH1Received);
+
+ // Because we are initiator the protocol engine didn't receive Commit
+ // thus could not store a peer's H2. A two step SHA256 is required to
+ // re-compute H3. Then compare with peer's H3 from peer's Hello packet.
+ // Must use implicit hash function.
+ uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH];
+ hashFunctionImpl(dhPart1->getH1(), HASH_IMAGE_SIZE, tmpHash); // Compute peer's H2
+ memcpy(peerH2, tmpHash, HASH_IMAGE_SIZE);
+ hashFunctionImpl(peerH2, HASH_IMAGE_SIZE, tmpHash); // Compute peer's H3 (tmpHash)
+
+ if (memcmp(tmpHash, peerH3, HASH_IMAGE_SIZE) != 0) {
+ *errMsg = IgnorePacket;
+ return NULL;
+ }
+
+ // Check HMAC of previous Hello packet stored in temporary buffer. The
+ // HMAC key of the Hello packet is peer's H2 that was computed above.
+ // Refer to chapter 9.1 and chapter 10.
+ if (!checkMsgHmac(peerH2)) {
+ sendInfo(Severe, SevereHelloHMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+
+ // get memory to store DH result TODO: make it fixed memory
+ DHss = new uint8_t[dhContext->getDhSize()];
+ if (DHss == NULL) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+
+ // get and check Responder's public value, see chap. 5.4.3 in the spec
+ pvr = dhPart1->getPv();
+ if (!dhContext->checkPubKey(pvr)) {
+ *errMsg = DHErrorWrongPV;
+ return NULL;
+ }
+ dhContext->computeSecretKey(pvr, DHss);
+
+ myRole = Initiator;
+
+ // We are Initiator: the Responder's Hello and the Initiator's (our) Commit
+ // are already hashed in the context. Now hash the Responder's DH1 and then
+ // the Initiator's (our) DH2 in that order.
+ // Use the negotiated hash function.
+ hashCtxFunction(msgShaContext, (unsigned char*)dhPart1->getHeaderBase(), dhPart1->getLength() * ZRTP_WORD_SIZE);
+ hashCtxFunction(msgShaContext, (unsigned char*)zrtpDH2.getHeaderBase(), zrtpDH2.getLength() * ZRTP_WORD_SIZE);
+
+ // Compute the message Hash
+ closeHashCtx(msgShaContext, messageHash);
+ msgShaContext = NULL;
+
+ // To compute the keys for the Initiator we need the retained secrets of our
+ // peer. Get them from the storage.
+ ZIDRecord zidRec(peerZid);
+ ZIDFile *zid = ZIDFile::getInstance();
+ zid->getRecord(&zidRec);
+
+ // Now compute the S0, all dependend keys and the new RS1. The function
+ // also performs sign SAS callback if it's active.
+ generateKeysInitiator(dhPart1, zidRec);
+ zid->saveRecord(&zidRec);
+
+ delete dhContext;
+ dhContext = NULL;
+
+ // TODO: at initiator we can call signSAS at this point, don't dealy until confirm1 reveived
+ // store DHPart1 data temporarily until we can check HMAC after receiving Confirm1
+ storeMsgTemp(dhPart1);
+ return &zrtpDH2;
+}
+
+/*
+ * At this point we are Responder.
+ */
+ZrtpPacketConfirm* ZRtp::prepareConfirm1(ZrtpPacketDHPart* dhPart2, uint32_t* errMsg) {
+
+ uint8_t* pvi;
+
+ sendInfo(Info, InfoRespDH2Received);
+
+ // Because we are responder we received a Commit and stored its H2.
+ // Now re-compute H2 from received H1 and compare with stored peer's H2.
+ // Use implicit hash function
+ uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH];
+ hashFunctionImpl(dhPart2->getH1(), HASH_IMAGE_SIZE, tmpHash);
+ if (memcmp(tmpHash, peerH2, HASH_IMAGE_SIZE) != 0) {
+ *errMsg = IgnorePacket;
+ return NULL;
+ }
+
+ // Check HMAC of Commit packet stored in temporary buffer. The
+ // HMAC key of the Commit packet is peer's H1 that is contained in
+ // DHPart2. Refer to chapter 9.1 and chapter 10.
+ if (!checkMsgHmac(dhPart2->getH1())) {
+ sendInfo(Severe, SevereCommitHMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+ // Now we have the peer's pvi. Because we are responder re-compute my hvi
+ // using my Hello packet and the Initiator's DHPart2 and compare with
+ // hvi sent in commit packet. If it doesn't macht then a MitM attack
+ // may have occured.
+ computeHvi(dhPart2, &zrtpHello);
+ if (memcmp(hvi, peerHvi, HVI_SIZE) != 0) {
+ *errMsg = DHErrorWrongHVI;
+ return NULL;
+ }
+ DHss = new uint8_t[dhContext->getDhSize()];
+ if (DHss == NULL) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+ // Get and check the Initiator's public value, see chap. 5.4.2 of the spec
+ pvi = dhPart2->getPv();
+ if (!dhContext->checkPubKey(pvi)) {
+ *errMsg = DHErrorWrongPV;
+ return NULL;
+ }
+ dhContext->computeSecretKey(pvi, DHss);
+ // Hash the Initiator's DH2 into the message Hash (other messages already
+ // prepared, see method prepareDHPart1().
+ // Use neotiated hash function
+ hashCtxFunction(msgShaContext, (unsigned char*)dhPart2->getHeaderBase(), dhPart2->getLength() * ZRTP_WORD_SIZE);
+
+ closeHashCtx(msgShaContext, messageHash);
+ msgShaContext = NULL;
+
+ // To compute the Keys for the Initiator we need the retained secrets of our
+ // peer. Get them from the storage.
+ ZIDRecord zidRec(peerZid);
+ ZIDFile *zid = ZIDFile::getInstance();
+ zid->getRecord(&zidRec);
+
+ /*
+ * The expected shared secret Ids were already computed when we built the
+ * DHPart1 packet. Generate s0, all depended keys, and the new RS1 value
+ * for the ZID record. The functions also performs sign SAS callback if it's active.
+ */
+ generateKeysResponder(dhPart2, zidRec);
+ zid->saveRecord(&zidRec);
+
+ delete dhContext;
+ dhContext = NULL;
+
+ // Fill in Confirm1 packet.
+ zrtpConfirm1.setMessageType((uint8_t*)Confirm1Msg);
+
+ // Check if user verfied the SAS in a previous call and thus verfied
+ // the retained secret. Don't set the verified flag if paranoidMode is true.
+ if (zidRec.isSasVerified() && !paranoidMode) {
+ zrtpConfirm1.setSASFlag();
+ }
+ zrtpConfirm1.setExpTime(0xFFFFFFFF);
+ zrtpConfirm1.setIv(randomIV);
+ zrtpConfirm1.setHashH0(H0);
+
+ // if this run at PBX user agent enrollment service then set flag in confirm
+ // packet and store the MitM key
+ if (enrollmentMode) {
+ computePBXSecret();
+ zrtpConfirm1.setPBXEnrollment();
+ writeEnrollmentPBX();
+ }
+ uint8_t confMac[MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+
+ // Encrypt and HMAC with Responder's key - we are Respondere here
+ int hmlen = (zrtpConfirm1.getLength() - 9) * ZRTP_WORD_SIZE;
+ cipher->getEncrypt()(zrtpKeyR, cipher->getKeylen(), randomIV, zrtpConfirm1.getHashH0(), hmlen);
+ hmacFunction(hmacKeyR, hashLength, (unsigned char*)zrtpConfirm1.getHashH0(), hmlen, confMac, &macLen);
+
+ zrtpConfirm1.setHmac(confMac);
+
+ // store DHPart2 data temporarily until we can check HMAC after receiving Confirm2
+ storeMsgTemp(dhPart2);
+ return &zrtpConfirm1;
+}
+
+/*
+ * At this point we are Responder.
+ */
+ZrtpPacketConfirm* ZRtp::prepareConfirm1MultiStream(ZrtpPacketCommit* commit, uint32_t* errMsg) {
+
+ sendInfo(Info, InfoRespCommitReceived);
+
+ // The following code checks the hash chain according chapter 10 to detect
+ // false ZRTP packets.
+ // Use implicit hash function
+ uint8_t tmpH3[IMPL_MAX_DIGEST_LENGTH];
+ memcpy(peerH2, commit->getH2(), HASH_IMAGE_SIZE);
+ hashFunctionImpl(peerH2, HASH_IMAGE_SIZE, tmpH3);
+
+ if (memcmp(tmpH3, peerH3, HASH_IMAGE_SIZE) != 0) {
+ *errMsg = IgnorePacket;
+ return NULL;
+ }
+
+ // Check HMAC of previous Hello packet stored in temporary buffer. The
+ // HMAC key of peer's Hello packet is peer's H2 that is contained in the
+ // Commit packet. Refer to chapter 9.1.
+ if (!checkMsgHmac(peerH2)) {
+ sendInfo(Severe, SevereHelloHMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+
+ // check if Commit contains "Mult" as pub key type
+ AlgorithmEnum* cp = &zrtpPubKeys.getByName((const char*)commit->getPubKeysType());
+ if (!cp->isValid() || *(int32_t*)(cp->getName()) != *(int32_t*)mult) {
+ *errMsg = UnsuppPKExchange;
+ return NULL;
+ }
+
+ // check if we support the commited cipher
+ cp = &zrtpSymCiphers.getByName((const char*)commit->getCipherType());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppCiphertype;
+ return NULL;
+ }
+ cipher = cp;
+
+ // check if we support the commited Authentication length
+ cp = &zrtpAuthLengths.getByName((const char*)commit->getAuthLen());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppSRTPAuthTag;
+ return NULL;
+ }
+ authLength = cp;
+
+ // check if we support the commited hash type
+ cp = &zrtpHashes.getByName((const char*)commit->getHashType());
+ if (!cp->isValid()) { // no match - something went wrong
+ *errMsg = UnsuppHashType;
+ return NULL;
+ }
+ // check if the peer's commited hash is the same that we used when
+ // preparing our commit packet. If not do the necessary resets and
+ // recompute some data.
+ if (*(int32_t*)(hash->getName()) != *(int32_t*)(cp->getName())) {
+ hash = cp;
+ setNegotiatedHash(hash);
+ }
+ myRole = Responder;
+
+ // We are responder. Release a possibly pre-computed SHA256 context
+ // because this was prepared for Initiator. Then create a new one.
+ if (msgShaContext != NULL) {
+ closeHashCtx(msgShaContext, NULL);
+ }
+ msgShaContext = createHashCtx();
+
+ // Hash messages to produce overall message hash:
+ // First the Responder's (my) Hello message, second the Commit
+ // (always Initator's)
+ // use negotiated hash
+ hashCtxFunction(msgShaContext, (unsigned char*)zrtpHello.getHeaderBase(), zrtpHello.getLength() * ZRTP_WORD_SIZE);
+ hashCtxFunction(msgShaContext, (unsigned char*)commit->getHeaderBase(), commit->getLength() * ZRTP_WORD_SIZE);
+
+ closeHashCtx(msgShaContext, messageHash);
+ msgShaContext = NULL;
+
+ generateKeysMultiStream();
+
+ // Fill in Confirm1 packet.
+ zrtpConfirm1.setMessageType((uint8_t*)Confirm1Msg);
+ zrtpConfirm1.setExpTime(0xFFFFFFFF);
+ zrtpConfirm1.setIv(randomIV);
+ zrtpConfirm1.setHashH0(H0);
+
+ uint8_t confMac[MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+
+ // Encrypt and HMAC with Responder's key - we are Respondere here
+ int32_t hmlen = (zrtpConfirm1.getLength() - 9) * ZRTP_WORD_SIZE;
+ cipher->getEncrypt()(zrtpKeyR, cipher->getKeylen(), randomIV, zrtpConfirm1.getHashH0(), hmlen);
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hmacKeyR, hashLength, (unsigned char*)zrtpConfirm1.getHashH0(), hmlen, confMac, &macLen);
+
+ zrtpConfirm1.setHmac(confMac);
+
+ // Store Commit data temporarily until we can check HMAC after receiving Confirm2
+ storeMsgTemp(commit);
+ return &zrtpConfirm1;
+}
+
+/*
+ * At this point we are Initiator.
+ */
+ZrtpPacketConfirm* ZRtp::prepareConfirm2(ZrtpPacketConfirm* confirm1, uint32_t* errMsg) {
+
+ sendInfo(Info, InfoInitConf1Received);
+
+ uint8_t confMac[MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+
+ // Use the Responder's keys here because we are Initiator here and
+ // receive packets from Responder
+ int16_t hmlen = (confirm1->getLength() - 9) * ZRTP_WORD_SIZE;
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hmacKeyR, hashLength, (unsigned char*)confirm1->getHashH0(), hmlen, confMac, &macLen);
+
+ if (memcmp(confMac, confirm1->getHmac(), HMAC_SIZE) != 0) {
+ *errMsg = ConfirmHMACWrong;
+ return NULL;
+ }
+ cipher->getDecrypt()(zrtpKeyR, cipher->getKeylen(), confirm1->getIv(), confirm1->getHashH0(), hmlen);
+
+ std::string cs(cipher->getReadable());
+ cs.append("/").append(pubKey->getName());
+
+ // Check HMAC of DHPart1 packet stored in temporary buffer. The
+ // HMAC key of the DHPart1 packet is peer's H0 that is contained in
+ // Confirm1. Refer to chapter 9.
+ if (!checkMsgHmac(confirm1->getHashH0())) {
+ sendInfo(Severe, SevereDH1HMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+ signatureLength = confirm1->getSignatureLength();
+ if (signSasSeen && signatureLength > 0) {
+ signatureData = confirm1->getSignatureData();
+ callback->checkSASSignature(sasHash);
+ // TODO: error handling if checkSASSignature returns false.
+ }
+ /*
+ * The Confirm1 is ok, handle the Retained secret stuff and inform
+ * GUI about state.
+ */
+ bool sasFlag = confirm1->isSASFlag();
+
+ // Initialize a ZID record to get peer's retained secrets
+ ZIDRecord zidRec(peerZid);
+
+ ZIDFile *zid = ZIDFile::getInstance();
+ zid->getRecord(&zidRec);
+
+ // Our peer did not confirm the SAS in last session, thus reset
+ // our SAS flag too. Reset the flag also if paranoidMode is true.
+ if (!sasFlag || paranoidMode) {
+ zidRec.resetSasVerified();
+ }
+ // get verified flag from current RS1 before set a new RS1. This
+ // may not be set even if peer's flag is set in confirm1 message.
+ sasFlag = zidRec.isSasVerified();
+
+ callback->srtpSecretsOn(cs, SAS, sasFlag);
+
+ // now we are ready to save the new RS1 which inherits the verified
+ // flag from old RS1
+ zidRec.setNewRs1((const uint8_t*)newRs1);
+ zid->saveRecord(&zidRec);
+
+ // now generate my Confirm2 message
+ zrtpConfirm2.setMessageType((uint8_t*)Confirm2Msg);
+ zrtpConfirm2.setHashH0(H0);
+
+ if (sasFlag) {
+ zrtpConfirm2.setSASFlag();
+ }
+ zrtpConfirm2.setExpTime(0xFFFFFFFF);
+ zrtpConfirm2.setIv(randomIV);
+
+ // Compute PBX secret if we are in enrollemnt mode (PBX user agent)
+ // or enrollment was enabled at normal user agent and flag in confirm packet
+ if (enrollmentMode || (enableMitmEnrollment && confirm1->isPBXEnrollment())) {
+ computePBXSecret();
+
+ // if this runs at PBX user agent enrollment service then set flag in confirm
+ // packet and store the MitM key. The PBX user agent service always stores
+ // its MitM key.
+ if (enrollmentMode) {
+ zrtpConfirm2.setPBXEnrollment();
+ writeEnrollmentPBX();
+ }
+ }
+ // Encrypt and HMAC with Initiator's key - we are Initiator here
+ hmlen = (zrtpConfirm2.getLength() - 9) * ZRTP_WORD_SIZE;
+ cipher->getEncrypt()(zrtpKeyI, cipher->getKeylen(), randomIV, zrtpConfirm2.getHashH0(), hmlen);
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hmacKeyI, hashLength, (unsigned char*)zrtpConfirm2.getHashH0(), hmlen, confMac, &macLen);
+
+ zrtpConfirm2.setHmac(confMac);
+
+ // Ask for enrollment only if enabled via configuration and the
+ // confirm1 packet contains the enrollment flag. The enrolling user
+ // agent stores the MitM key only if the user accepts the enrollment
+ // request.
+ if (enableMitmEnrollment && confirm1->isPBXEnrollment()) {
+ callback->zrtpAskEnrollment(EnrollmentRequest);
+ }
+ return &zrtpConfirm2;
+}
+
+/**
+ * Save the computed MitM secret to the ZID record of the peer
+ */
+void ZRtp::writeEnrollmentPBX() {
+ // Initialize a ZID record to get peer's retained secrets
+ ZIDRecord zidRec(peerZid);
+
+ ZIDFile *zid = ZIDFile::getInstance();
+ zid->getRecord(&zidRec);
+
+ if (pbxSecretTmp != NULL) {
+ zidRec.setMiTMData(pbxSecretTmp);
+ }
+ zid->saveRecord(&zidRec);
+}
+
+/*
+ * At this point we are Initiator.
+ */
+ZrtpPacketConfirm* ZRtp::prepareConfirm2MultiStream(ZrtpPacketConfirm* confirm1, uint32_t* errMsg) {
+
+ // check Confirm1 packet using the keys
+ // prepare Confirm2 packet
+ // don't update SAS, RS
+ sendInfo(Info, InfoInitConf1Received);
+
+ uint8_t confMac[MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+
+ closeHashCtx(msgShaContext, messageHash);
+ msgShaContext = NULL;
+ myRole = Initiator;
+
+ generateKeysMultiStream();
+
+ // Use the Responder's keys here because we are Initiator here and
+ // receive packets from Responder
+ int32_t hmlen = (confirm1->getLength() - 9) * ZRTP_WORD_SIZE;
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hmacKeyR, hashLength, (unsigned char*)confirm1->getHashH0(), hmlen, confMac, &macLen);
+
+ if (memcmp(confMac, confirm1->getHmac(), HMAC_SIZE) != 0) {
+ *errMsg = ConfirmHMACWrong;
+ return NULL;
+ }
+ cipher->getDecrypt()(zrtpKeyR, cipher->getKeylen(), confirm1->getIv(), confirm1->getHashH0(), hmlen);
+ std::string cs(cipher->getReadable());
+
+ // Because we are initiator the protocol engine didn't receive Commit and
+ // because we are using multi-stream mode here we also did not receive a DHPart1 and
+ // thus could not store a responder's H2 or H1. A two step hash is required to
+ // re-compute H1, H2.
+ // USe implicit hash function.
+ uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH];
+ hashFunctionImpl(confirm1->getHashH0(), HASH_IMAGE_SIZE, tmpHash); // Compute peer's H1 in tmpHash
+ hashFunctionImpl(tmpHash, HASH_IMAGE_SIZE, tmpHash); // Compute peer's H2 in tmpHash
+ memcpy(peerH2, tmpHash, HASH_IMAGE_SIZE); // copy and truncate to peerH2
+
+ // Check HMAC of previous Hello packet stored in temporary buffer. The
+ // HMAC key of the Hello packet is peer's H2 that was computed above.
+ // Refer to chapter 9.1 and chapter 10.
+ if (!checkMsgHmac(peerH2)) {
+ sendInfo(Severe, SevereHelloHMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+ // TODO: here we have a SAS signature from reponder, call checkSASsignature (save / compare in case of resend)
+
+ // Inform GUI about security state, don't show SAS and its state
+ std::string cs1("");
+ callback->srtpSecretsOn(cs, cs1, true);
+
+ // now generate my Confirm2 message
+ zrtpConfirm2.setMessageType((uint8_t*)Confirm2Msg);
+ zrtpConfirm2.setHashH0(H0);
+ zrtpConfirm2.setExpTime(0xFFFFFFFF);
+ zrtpConfirm2.setIv(randomIV);
+
+ // Encrypt and HMAC with Initiator's key - we are Initiator here
+ hmlen = (zrtpConfirm2.getLength() - 9) * ZRTP_WORD_SIZE;
+ cipher->getEncrypt()(zrtpKeyI, cipher->getKeylen(), randomIV, zrtpConfirm2.getHashH0(), hmlen);
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hmacKeyI, hashLength, (unsigned char*)zrtpConfirm2.getHashH0(), hmlen, confMac, &macLen);
+
+ zrtpConfirm2.setHmac(confMac);
+ return &zrtpConfirm2;
+}
+
+/*
+ * At this point we are Responder.
+ */
+ZrtpPacketConf2Ack* ZRtp::prepareConf2Ack(ZrtpPacketConfirm *confirm2, uint32_t* errMsg) {
+
+ sendInfo(Info, InfoRespConf2Received);
+
+ uint8_t confMac[MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+
+ // Use the Initiator's keys here because we are Responder here and
+ // reveice packets from Initiator
+ int16_t hmlen = (confirm2->getLength() - 9) * ZRTP_WORD_SIZE;
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hmacKeyI, hashLength,
+ (unsigned char*)confirm2->getHashH0(),
+ hmlen, confMac, &macLen);
+
+ if (memcmp(confMac, confirm2->getHmac(), HMAC_SIZE) != 0) {
+ *errMsg = ConfirmHMACWrong;
+ return NULL;
+ }
+ cipher->getDecrypt()(zrtpKeyI, cipher->getKeylen(), confirm2->getIv(), confirm2->getHashH0(), hmlen);
+
+ std::string cs(cipher->getReadable());
+
+ if (!multiStream) {
+ // Check HMAC of DHPart2 packet stored in temporary buffer. The
+ // HMAC key of the DHPart2 packet is peer's H0 that is contained in
+ // Confirm2. Refer to chapter 9.1 and chapter 10.
+ if (!checkMsgHmac(confirm2->getHashH0())) {
+ sendInfo(Severe, SevereDH2HMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+ signatureLength = confirm2->getSignatureLength();
+ if (signSasSeen && signatureLength > 0) {
+ signatureData = confirm2->getSignatureData();
+ callback->checkSASSignature(sasHash);
+ // TODO: error handling if checkSASSignature returns false.
+ }
+ /*
+ * The Confirm2 is ok, handle the Retained secret stuff and inform
+ * GUI about state.
+ */
+ bool sasFlag = confirm2->isSASFlag();
+
+ // Initialize a ZID record to get peer's retained secrets
+ ZIDRecord zidRec(peerZid);
+
+ ZIDFile *zid = ZIDFile::getInstance();
+ zid->getRecord(&zidRec);
+
+ // Our peer did not confirm the SAS in last session, thus reset
+ // our SAS flag too. Reset the flag also if paranoidMode is true.
+ if (!sasFlag || paranoidMode) {
+ zidRec.resetSasVerified();
+ }
+
+ // Now get the resulting SAS verified flag from current RS1 before setting a new RS1.
+ // It's a combination of our SAS verfied flag and peer's verified flag. Only if both
+ // were set (true) then sasFlag becomes true.
+ sasFlag = zidRec.isSasVerified();
+ cs.append("/").append(pubKey->getName());
+ callback->srtpSecretsOn(cs, SAS, sasFlag);
+
+ // save new RS1, this inherits the verified flag from old RS1
+ zidRec.setNewRs1((const uint8_t*)newRs1);
+ zid->saveRecord(&zidRec);
+
+ // Ask for enrollment only if enabled via configuration and the
+ // confirm packet contains the enrollment flag. The enrolling user
+ // agent stores the MitM key only if the user accepts the enrollment
+ // request.
+ if (enableMitmEnrollment && confirm2->isPBXEnrollment()) {
+ computePBXSecret();
+ callback->zrtpAskEnrollment(EnrollmentRequest);
+ }
+ }
+ else {
+ // Check HMAC of Commit packet stored in temporary buffer. The
+ // HMAC key of the Commit packet is initiator's H1
+ // use implicit hash function.
+ uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH];
+ hashFunctionImpl(confirm2->getHashH0(), HASH_IMAGE_SIZE, tmpHash); // Compute initiator's H1 in tmpHash
+
+ if (!checkMsgHmac(tmpHash)) {
+ sendInfo(Severe, SevereCommitHMACFailed);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+ std::string cs1("");
+
+ // Inform GUI about security state, don't show SAS and its state
+ callback->srtpSecretsOn(cs, cs1, true);
+ }
+ return &zrtpConf2Ack;
+}
+
+ZrtpPacketErrorAck* ZRtp::prepareErrorAck(ZrtpPacketError* epkt) {
+ sendInfo(ZrtpError, epkt->getErrorCode() * -1);
+ return &zrtpErrorAck;
+}
+
+ZrtpPacketError* ZRtp::prepareError(uint32_t errMsg) {
+ zrtpError.setErrorCode(errMsg);
+ return &zrtpError;
+}
+
+ZrtpPacketPingAck* ZRtp::preparePingAck(ZrtpPacketPing* ppkt) {
+ if (ppkt->getLength() != 6) // A PING packet must have a length of 6 words
+ return NULL;
+ // Because we do not support ZRTP proxy mode use the truncated ZID.
+ // If this code shall be used in ZRTP proxy implementation the computation
+ // of the endpoint hash must be enhanced (see chaps 5.15ff and 5.16)
+ zrtpPingAck.setLocalEpHash(zid);
+ zrtpPingAck.setRemoteEpHash(ppkt->getEpHash());
+ zrtpPingAck.setSSRC(peerSSRC);
+ return &zrtpPingAck;
+}
+
+ZrtpPacketRelayAck* ZRtp::prepareRelayAck(ZrtpPacketSASrelay* srly, uint32_t* errMsg) {
+ // handle and render SAS relay data only if the peer announced that it is a trusted
+ // PBX. Don't handle SAS relay in paranoidMode.
+ if (!mitmSeen || paranoidMode)
+ return &zrtpRelayAck;
+
+ uint8_t* hkey, *ekey;
+ // If we are responder then the PBX used it's Initiator keys
+ if (myRole == Responder) {
+ hkey = hmacKeyI;
+ ekey = zrtpKeyI;
+ }
+ else {
+ hkey = hmacKeyR;
+ ekey = zrtpKeyR;
+ }
+
+ uint8_t confMac[MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+
+ // Use the Initiator's keys here because we are Responder here and
+ // reveice packets from Initiator
+ int16_t hmlen = (srly->getLength() - 9) * ZRTP_WORD_SIZE;
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hkey, hashLength, (unsigned char*)srly->getFiller(), hmlen, confMac, &macLen);
+
+ if (memcmp(confMac, srly->getHmac(), HMAC_SIZE) != 0) {
+ *errMsg = ConfirmHMACWrong;
+ return NULL; // TODO - check error handling
+ }
+ cipher->getDecrypt()(ekey, cipher->getKeylen(), srly->getIv(), (uint8_t*)srly->getFiller(), hmlen);
+
+ const uint8_t* render = srly->getSas();
+ const uint8_t* newSasHash = srly->getTrustedSas();
+
+ bool sasHashNull = true;
+ for (int i = 0; i < HASH_IMAGE_SIZE; i++) {
+ if (newSasHash[i] != 0) {
+ sasHashNull = false;
+ break;
+ }
+ }
+ // Check if new SAS is null or a trusted MitM relationship doesn't exist.
+ // If this is the case then don't render and don't show the new SAS - use
+ // the computed SAS hash but we may use a different SAS rendering algorithm to
+ // render the computed SAS.
+ if (sasHashNull || !peerIsEnrolled) {
+ newSasHash = sasHash;
+ }
+ // If other SAS schemes required - check here and use others
+ AlgorithmEnum* renderAlgo = &zrtpSasTypes.getByName((const char*)render);
+ uint8_t sasBytes[4];;
+ if (renderAlgo->isValid()) {
+ sasBytes[0] = newSasHash[0];
+ sasBytes[1] = newSasHash[1];
+ sasBytes[2] = newSasHash[2] & 0xf0;
+ sasBytes[3] = 0;
+ }
+ SAS = Base32(sasBytes, 20).getEncoded();
+ std::string cs(cipher->getReadable());
+ cs.append("/").append(pubKey->getName()).append("/MitM");
+
+ callback->srtpSecretsOn(cs, SAS, false);
+ return &zrtpRelayAck;
+}
+
+// TODO Implement GoClear handling
+ZrtpPacketClearAck* ZRtp::prepareClearAck(ZrtpPacketGoClear* gpkt) {
+ sendInfo(Warning, WarningGoClearReceived);
+ return &zrtpClearAck;
+}
+
+ZrtpPacketGoClear* ZRtp::prepareGoClear(uint32_t errMsg) {
+ ZrtpPacketGoClear* gclr = &zrtpGoClear;
+ gclr->clrClearHmac();
+ return gclr;
+}
+
+/*
+ * The next functions look up and return a prefered algorithm. These
+ * functions work as follows:
+ * - If the Hello packet does not contain an algorithm (number of algorithms
+* is zero) then return the mandatory algorithm.
+ * - Build a list of algorithm names and ids from configuration data. If
+ * the configuration data does not contain a mandatory algorithm append
+ * the mandatory algorithm to the list and ids.
+ * - Build a list of algorithm names from the Hello message. If
+ * the Hello message does not contain a mandatory algorithm append
+ * the mandatory algorithm to the list.
+ * - Lookup a matching algorithm. The list built from Hello takes
+ * precedence in the lookup (indexed by the outermost loop).
+ *
+ * This guarantees that we always return a supported alogrithm respecting
+ * the order of algorithms in the Hello message
+ *
+ * The mandatory algorithms are: (internal enums are our prefered algoritms)
+ * Hash: S256 (SHA 256) (internal enum Sha256)
+ * Symmetric Cipher: AES1 (AES 128) (internal enum Aes128)
+ * SRTP Authentication: HS32 and HS80 (32/80 bits) (internal enum AuthLen32)
+ * Key Agreement: DH3k (3072 Diffie-Helman) (internal enum Dh3072)
+ *
+ */
+AlgorithmEnum* ZRtp::findBestHash(ZrtpPacketHello *hello) {
+
+ int i;
+ int ii;
+ int numAlgosOffered;
+ AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1];
+
+ int numAlgosConf;
+ AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1];
+
+ bool mandatoryFound = false;
+
+ // If Hello does not contain any hash names return Sha256, its mandatory
+ int num = hello->getNumHashes();
+ if (num == 0) {
+ return &zrtpHashes.getByName(mandatoryHash);
+ }
+ // Build list of configured hash algorithm names, append mandatory algos
+ // if necessary.
+ numAlgosConf = configureAlgos.getNumConfiguredAlgos(HashAlgorithm);
+ for (i = 0; i < numAlgosConf; i++) {
+ algosConf[i] = &configureAlgos.getAlgoAt(HashAlgorithm, i);
+ if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryHash) {
+ mandatoryFound = true;
+ }
+ }
+ if (!mandatoryFound) {
+ algosConf[numAlgosConf++] = &zrtpHashes.getByName(mandatoryHash);
+ }
+
+ // Build list of offered known algos in Hello, append mandatory algos if necessary
+ mandatoryFound = false;
+ for (numAlgosOffered = 0, i = 0; i < num; i++) {
+ algosOffered[numAlgosOffered] = &zrtpHashes.getByName((const char*)hello->getHashType(i));
+ if (!algosOffered[numAlgosOffered]->isValid())
+ continue;
+ if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryHash) {
+ mandatoryFound = true;
+ }
+ }
+ if (!mandatoryFound) {
+ algosOffered[numAlgosOffered++] = &zrtpHashes.getByName(mandatoryHash);
+ }
+
+ // Lookup offered algos in configured algos. Because of appended
+ // mandatory algorithms at least one match will happen
+ for (i = 0; i < numAlgosOffered; i++) {
+ for (ii = 0; ii < numAlgosConf; ii++) {
+ if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) {
+ return algosConf[ii];
+ }
+ }
+ }
+ return &zrtpHashes.getByName(mandatoryHash);
+}
+
+AlgorithmEnum* ZRtp::findBestCipher(ZrtpPacketHello *hello, AlgorithmEnum* pk) {
+
+ int i;
+ int ii;
+ int numAlgosOffered;
+ AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1];
+
+ int numAlgosConf;
+ AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1];
+
+ bool mandatoryFound = false;
+
+ int num = hello->getNumCiphers();
+ if (num == 0 || (*(int32_t*)(pk->getName()) == *(int32_t*)dh2k)) {
+ return &zrtpSymCiphers.getByName(aes1);
+ }
+
+ // Build list of configured cipher algorithm names, append mandatory algos
+ // if necessary.
+ numAlgosConf = configureAlgos.getNumConfiguredAlgos(CipherAlgorithm);
+ for (i = 0; i < numAlgosConf; i++) {
+ algosConf[i] = &configureAlgos.getAlgoAt(CipherAlgorithm, i);
+ if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryCipher) {
+ mandatoryFound = true;
+ }
+ }
+ if (!mandatoryFound) {
+ algosConf[numAlgosConf++] = &zrtpSymCiphers.getByName(mandatoryCipher);
+ }
+
+ // Build list of offered known algos names in Hello, append mandatory algos if
+ // necessary
+ mandatoryFound = false;
+ for (numAlgosOffered = 0, i = 0; i < num; i++) {
+ algosOffered[numAlgosOffered] = &zrtpSymCiphers.getByName((const char*)hello->getCipherType(i));
+ if (!algosOffered[numAlgosOffered]->isValid())
+ continue;
+ if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryCipher) {
+ mandatoryFound = true;
+ }
+ }
+
+ if (!mandatoryFound) {
+ algosOffered[numAlgosOffered++] = &zrtpSymCiphers.getByName(mandatoryCipher);
+ }
+
+ // Lookup offered algos in configured algos. Because of appended
+ // mandatory algorithms at least one match will happen
+ for (i = 0; i < numAlgosOffered; i++) {
+ for (ii = 0; ii < numAlgosConf; ii++) {
+ if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) {
+ return algosConf[ii];
+ }
+ }
+ }
+ return &zrtpSymCiphers.getByName(mandatoryCipher);
+}
+
+AlgorithmEnum* ZRtp::findBestPubkey(ZrtpPacketHello *hello) {
+
+ int i;
+ int ii;
+ int numAlgosOffered;
+ AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1];
+
+ int numAlgosConf;
+ AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1];
+
+ bool mandatoryFound = false;
+
+ int num = hello->getNumPubKeys();
+ if (num == 0) {
+ return &zrtpPubKeys.getByName(mandatoryPubKey);
+ }
+ // Build list of configured pubkey algorithm names, append mandatory algos
+ // if necessary.
+ // The list must include real public key algorithms only, so skip
+ // mult-stream mode, preshared and alike.
+ numAlgosConf = configureAlgos.getNumConfiguredAlgos(PubKeyAlgorithm);
+ for (i = 0, ii = 0; i < numAlgosConf; i++) {
+ algosConf[ii] = &configureAlgos.getAlgoAt(PubKeyAlgorithm, ii);
+ if (*(int32_t*)(algosConf[ii]->getName()) == *(int32_t*)mult) {
+ continue; // skip multi-stream mode
+ }
+ if (*(int32_t*)(algosConf[ii++]->getName()) == *(int32_t*)mandatoryPubKey) {
+ mandatoryFound = true;
+ }
+ }
+
+ numAlgosConf = ii;
+ if (!mandatoryFound) {
+ algosConf[numAlgosConf++] = &zrtpPubKeys.getByName(mandatoryPubKey);
+ }
+
+ // Build list of offered known algos in Hello, append mandatory algos if necessary
+ mandatoryFound = false;
+ for (numAlgosOffered = 0, i = 0; i < num; i++) {
+ algosOffered[numAlgosOffered] = &zrtpPubKeys.getByName((const char*)hello->getPubKeyType(i));
+ if (!algosOffered[numAlgosOffered]->isValid())
+ continue;
+ if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryPubKey) {
+ mandatoryFound = true;
+ }
+ }
+
+ if (!mandatoryFound) {
+ algosOffered[numAlgosOffered++] = &zrtpPubKeys.getByName(mandatoryPubKey);
+ }
+
+ // Lookup offered algos in configured algos. Because of appended
+ // mandatory algorithms at least one match will happen
+ for (i = 0; i < numAlgosOffered; i++) {
+ for (ii = 0; ii < numAlgosConf; ii++) {
+ if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) {
+ return algosConf[ii];
+ }
+ }
+ }
+ return &zrtpPubKeys.getByName(mandatoryPubKey);
+}
+
+AlgorithmEnum* ZRtp::findBestSASType(ZrtpPacketHello *hello) {
+
+ int i;
+ int ii;
+ int numAlgosOffered;
+ AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1];
+
+ int numAlgosConf;
+ AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1];
+
+ bool mandatoryFound = false;
+
+ int num = hello->getNumSas();
+ if (num == 0) {
+ return &zrtpSasTypes.getByName(mandatorySasType);
+ }
+ // Buildlist of configured SAS algorithm names, append mandatory algos
+ // if necessary.
+ numAlgosConf = configureAlgos.getNumConfiguredAlgos(SasType);
+ for (i = 0; i < numAlgosConf; i++) {
+ algosConf[i] = &configureAlgos.getAlgoAt(SasType, i);
+ if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatorySasType) {
+ mandatoryFound = true;
+ }
+ }
+
+ if (!mandatoryFound) {
+ algosConf[numAlgosConf++] = &zrtpSasTypes.getByName(mandatorySasType);
+ }
+
+ // Build list of offered known algos in Hello, append mandatory algos if necessary
+ for (numAlgosOffered = 0, i = 0; i < num; i++) {
+ algosOffered[numAlgosOffered] = &zrtpSasTypes.getByName((const char*)hello->getSasType(i));
+ if (!algosOffered[numAlgosOffered]->isValid())
+ continue;
+ if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatorySasType) {
+ mandatoryFound = true;
+ }
+ }
+
+ if (!mandatoryFound) {
+ algosOffered[numAlgosOffered++] = &zrtpSasTypes.getByName(mandatorySasType);
+ }
+
+ // Lookup offered algos in configured algos. Because of appended
+ // mandatory algorithms at least one match will happen
+ for (i = 0; i < numAlgosOffered; i++) {
+ for (ii = 0; ii < numAlgosConf; ii++) {
+ if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) {
+ return algosConf[ii];
+ }
+ }
+ }
+ return &zrtpSasTypes.getByName(mandatorySasType);
+}
+
+AlgorithmEnum* ZRtp::findBestAuthLen(ZrtpPacketHello *hello) {
+
+ int i;
+ int ii;
+ int numAlgosOffered;
+ AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+2];
+
+ int numAlgosConf;
+ AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+2];
+
+ bool mandatoryFound_1 = false;
+ bool mandatoryFound_2 = false;
+
+ int num = hello->getNumAuth();
+ if (num == 0) {
+ return &zrtpAuthLengths.getByName(mandatoryAuthLen_1);
+ }
+
+ // Build list of configured SAS algorithm names, append mandatory algos
+ // if necessary.
+ numAlgosConf = configureAlgos.getNumConfiguredAlgos(AuthLength);
+ for (i = 0; i < numAlgosConf; i++) {
+ algosConf[i] = &configureAlgos.getAlgoAt(AuthLength, i);
+ if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryAuthLen_1) {
+ mandatoryFound_1 = true;
+ }
+ if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryAuthLen_2) {
+ mandatoryFound_2 = true;
+ }
+ }
+
+ if (!mandatoryFound_1) {
+ algosConf[numAlgosConf++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_1);
+ }
+
+ if (!mandatoryFound_2) {
+ algosConf[numAlgosConf++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_2);
+ }
+
+ // Build list of offered known algos in Hello, append mandatory algos if necessary
+ for (numAlgosOffered = 0, i = 0; i < num; i++) {
+ algosOffered[numAlgosOffered] = &zrtpAuthLengths.getByName((const char*)hello->getAuthLen(i));
+ if (!algosOffered[numAlgosOffered]->isValid())
+ continue;
+ if (*(int32_t*)(algosOffered[numAlgosOffered]->getName()) == *(int32_t*)mandatoryAuthLen_1) {
+ mandatoryFound_1 = true;
+ }
+ if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryAuthLen_2) {
+ mandatoryFound_2 = true;
+ }
+ }
+ if (!mandatoryFound_1) {
+ algosOffered[numAlgosOffered++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_1);
+ }
+ if (!mandatoryFound_2) {
+ algosOffered[numAlgosOffered++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_2);
+ }
+ // Lookup offered algos in configured algos. Because of appended
+ // mandatory algorithms at least one match will happen
+ for (i = 0; i < numAlgosOffered; i++) {
+ for (ii = 0; ii < numAlgosConf; ii++) {
+ if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) {
+ return algosConf[ii];
+ }
+ }
+ }
+ return &zrtpAuthLengths.getByName(mandatoryAuthLen_1);
+}
+
+bool ZRtp::checkMultiStream(ZrtpPacketHello *hello) {
+
+ int i;
+ int num = hello->getNumPubKeys();
+
+ // Multi Stream mode is mandatory, thus if nothing is offered then it is supported :-)
+ if (num == 0) {
+ return true;
+ }
+ for (i = 0; i < num; i++) {
+ if (*(int32_t*)(hello->getPubKeyType(i)) == *(int32_t*)mult) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ZRtp::verifyH2(ZrtpPacketCommit *commit) {
+ uint8_t tmpH3[IMPL_MAX_DIGEST_LENGTH];
+
+ sha256(commit->getH2(), HASH_IMAGE_SIZE, tmpH3);
+ if (memcmp(tmpH3, peerH3, HASH_IMAGE_SIZE) != 0) {
+ return false;
+ }
+ return true;
+}
+
+void ZRtp::computeHvi(ZrtpPacketDHPart* dh, ZrtpPacketHello *hello) {
+
+ unsigned char* data[3];
+ unsigned int length[3];
+ /*
+ * populate the vector to compute the HVI hash according to the
+ * ZRTP specification.
+ */
+ data[0] = (uint8_t*)dh->getHeaderBase();
+ length[0] = dh->getLength() * ZRTP_WORD_SIZE;
+
+ data[1] = (uint8_t*)hello->getHeaderBase();
+ length[1] = hello->getLength() * ZRTP_WORD_SIZE;
+
+ data[2] = NULL; // terminate data chunks
+ hashListFunction(data, length, hvi);
+ return;
+}
+
+void ZRtp:: computeSharedSecretSet(ZIDRecord &zidRec) {
+
+ /*
+ * Compute the Initiator's and Reponder's retained shared secret Ids.
+ * Use negotiated HMAC.
+ */
+ uint8_t randBuf[RS_LENGTH];
+ uint32_t macLen;
+
+ if (!zidRec.isRs1Valid()) {
+ randomZRTP(randBuf, RS_LENGTH);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs1IDi, &macLen);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), rs1IDr, &macLen);
+ }
+ else {
+ rs1Valid = true;
+ hmacFunction((unsigned char*)zidRec.getRs1(), RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs1IDi, &macLen);
+ hmacFunction((unsigned char*)zidRec.getRs1(), RS_LENGTH, (unsigned char*)responder, strlen(responder), rs1IDr, &macLen);
+ }
+
+ if (!zidRec.isRs2Valid()) {
+ randomZRTP(randBuf, RS_LENGTH);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs2IDi, &macLen);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), rs2IDr, &macLen);
+ }
+ else {
+ rs2Valid = true;
+ hmacFunction((unsigned char*)zidRec.getRs2(), RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs2IDi, &macLen);
+ hmacFunction((unsigned char*)zidRec.getRs2(), RS_LENGTH, (unsigned char*)responder, strlen(responder), rs2IDr, &macLen);
+ }
+
+ /*
+ * For the time being we don't support this types of shared secrect. Could be
+ * easily done: somebody sets some data into our ZRtp object, check it here
+ * and use it. Otherwise use the random data.
+ */
+ randomZRTP(randBuf, RS_LENGTH);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), auxSecretIDi, &macLen);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), auxSecretIDr, &macLen);
+
+ if (!zidRec.isMITMKeyAvailable()) {
+ randomZRTP(randBuf, RS_LENGTH);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), pbxSecretIDi, &macLen);
+ hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), pbxSecretIDr, &macLen);
+ }
+ else {
+ hmacFunction((unsigned char*)zidRec.getMiTMData(), RS_LENGTH, (unsigned char*)initiator, strlen(initiator), pbxSecretIDi, &macLen);
+ hmacFunction((unsigned char*)zidRec.getMiTMData(), RS_LENGTH, (unsigned char*)responder, strlen(responder), pbxSecretIDr, &macLen);
+ }
+}
+
+/*
+ * The DH packet for this function is DHPart1 and contains the Responder's
+ * retained secret ids. Compare them with the expected secret ids (refer
+ * to chapter 5.3 in the specification).
+ * When using this method then we are in Initiator role.
+ */
+void ZRtp::generateKeysInitiator(ZrtpPacketDHPart *dhPart, ZIDRecord& zidRec) {
+ const uint8_t* setD[3];
+ int32_t rsFound = 0;
+
+ setD[0] = setD[1] = setD[2] = NULL;
+
+ /*
+ * Select the real secrets into setD. The dhPart is DHpart1 message
+ * received from responder. rs1IDr and rs2IDr are the expected ids using
+ * the initator's cached retained secrets.
+ */
+ int matchingSecrets = 0;
+ if (memcmp(rs1IDr, dhPart->getRs1Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs1();
+ rsFound = 0x1;
+ }
+ else if (memcmp(rs1IDr, dhPart->getRs2Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs1();
+ rsFound = 0x2;
+ }
+ else if (memcmp(rs2IDr, dhPart->getRs1Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs2();
+ rsFound = 0x4;
+ }
+ else if (memcmp(rs2IDr, dhPart->getRs2Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs2();
+ rsFound = 0x8;
+ }
+ /* *** Not yet supported
+ if (memcmp(auxSecretIDr, dhPart->getAuxSecretId(), 8) == 0) {
+ DEBUGOUT((fprintf(stdout, "%c: Match for aux secret found\n", zid[0])));
+ setD[matchingSecrets++] = auxSecret;
+ }
+ */
+ if (memcmp(pbxSecretIDr, dhPart->getPbxSecretId(), 8) == 0) {
+ DEBUGOUT((fprintf(stdout, "%c: Match for Other_secret found\n", zid[0])));
+ setD[matchingSecrets++] = zidRec.getMiTMData();
+ }
+ // Check if some retained secrets found
+ if (rsFound == 0) { // no RS matches found
+ if (rs1Valid || rs2Valid) { // but valid RS records in cache
+ sendInfo(Warning, WarningNoExpectedRSMatch);
+ zidRec.resetSasVerified();
+ }
+ else { // No valid RS record in cache
+ sendInfo(Warning, WarningNoRSMatch);
+ }
+ }
+ else { // at least one RS matches
+ sendInfo(Info, InfoRSMatchFound);
+ }
+ /*
+ * Ready to generate s0 here.
+ * The formular to compute S0 (Refer to ZRTP specification 5.4.4):
+ *
+ s0 = hash( counter | DHResult | "ZRTP-HMAC-KDF" | ZIDi | ZIDr | \
+ total_hash | len(s1) | s1 | len(s2) | s2 | len(s3) | s3)
+ *
+ * Note: in this function we are Initiator, thus ZIDi is our zid
+ * (zid), ZIDr is the peer's zid (peerZid).
+ */
+
+ /*
+ * These arrays hold the pointers and lengths of the data that must be
+ * hashed to create S0. According to the formula the max number of
+ * elements to hash is 12, add one for the terminating "NULL"
+ */
+ unsigned char* data[13];
+ unsigned int length[13];
+ uint32_t pos = 0; // index into the array
+
+ // we need a number of length data items, so define them here
+ uint32_t counter, sLen[3];
+
+ //Very first element is a fixed counter, big endian
+ counter = 1;
+ counter = htonl(counter);
+ data[pos] = (unsigned char*)&counter;
+ length[pos++] = sizeof(uint32_t);
+
+ // Next is the DH result itself
+ data[pos] = DHss;
+ length[pos++] = dhContext->getDhSize();
+
+ // Next the fixed string "ZRTP-HMAC-KDF"
+ data[pos] = (unsigned char*)KDFString;
+ length[pos++] = strlen(KDFString);
+
+ // Next is Initiator's id (ZIDi), in this case as Initiator
+ // it is zid
+ data[pos] = zid;
+ length[pos++] = ZID_SIZE;
+
+ // Next is Responder's id (ZIDr), in this case our peer's id
+ data[pos] = peerZid;
+ length[pos++] = ZID_SIZE;
+
+ // Next ist total hash (messageHash) itself
+ data[pos] = messageHash;
+ length[pos++] = hashLength;
+
+ /*
+ * For each matching shared secret hash the length of
+ * the shared secret as 32 bit big-endian number followd by the
+ * shared secret itself. The length of a shared seceret is
+ * currently fixed to RS_LENGTH. If a shared
+ * secret is not used _only_ its length is hased as zero
+ * length. NOTE: if implementing auxSecret and/or pbxSecret -> check
+ * this length stuff again.
+ */
+ int secretHashLen = RS_LENGTH;
+ secretHashLen = htonl(secretHashLen); // prepare 32 bit big-endian number
+
+ for (int32_t i = 0; i < 3; i++) {
+ if (setD[i] != NULL) { // a matching secret, set length, then secret
+ sLen[i] = secretHashLen;
+ data[pos] = (unsigned char*)&sLen[i];
+ length[pos++] = sizeof(uint32_t);
+ data[pos] = (unsigned char*)setD[i];
+ length[pos++] = RS_LENGTH;
+ }
+ else { // no machting secret, set length 0, skip secret
+ sLen[i] = 0;
+ data[pos] = (unsigned char*)&sLen[i];
+ length[pos++] = sizeof(uint32_t);
+ }
+ }
+
+ data[pos] = NULL;
+ hashListFunction(data, length, s0);
+// hexdump("S0 I", s0, hashLength);
+
+ memset(DHss, 0, dhContext->getDhSize());
+ delete[] DHss;
+ DHss = NULL;
+
+ computeSRTPKeys();
+ memset(s0, 0, MAX_DIGEST_LENGTH);
+}
+/*
+ * The DH packet for this function is DHPart2 and contains the Initiator's
+ * retained secret ids. Compare them with the expected secret ids (refer
+ * to chapter 5.3.1 in the specification).
+ */
+void ZRtp::generateKeysResponder(ZrtpPacketDHPart *dhPart, ZIDRecord& zidRec) {
+ const uint8_t* setD[3];
+ int32_t rsFound = 0;
+
+ setD[0] = setD[1] = setD[2] = NULL;
+
+ /*
+ * Select the real secrets into setD
+ */
+ int matchingSecrets = 0;
+ if (memcmp(rs1IDi, dhPart->getRs1Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs1();
+ rsFound = 0x1;
+ }
+ else if (memcmp(rs1IDi, dhPart->getRs2Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs1();
+ rsFound = 0x2;
+ }
+ else if (memcmp(rs2IDi, dhPart->getRs2Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs2();
+ rsFound |= 0x4;
+ }
+ else if (memcmp(rs2IDi, dhPart->getRs1Id(), HMAC_SIZE) == 0) {
+ setD[matchingSecrets++] = zidRec.getRs2();
+ rsFound |= 0x8;
+ }
+ /* ***** not yet supported
+ if (memcmp(auxSecretIDi, dhPart->getauxSecretId(), 8) == 0) {
+ DEBUGOUT((fprintf(stdout, "%c: Match for aux secret found\n", zid[0])));
+ setD[matchingSecrets++] = ;
+ }
+ */
+ if (memcmp(pbxSecretIDi, dhPart->getPbxSecretId(), 8) == 0) {
+ DEBUGOUT((fprintf(stdout, "%c: Match for PBX secret found\n", zid[0])));
+ setD[matchingSecrets++] = zidRec.getMiTMData();
+ }
+ // Check if some retained secrets found
+ if (rsFound == 0) { // no RS matches found
+ if (rs1Valid || rs2Valid) { // but valid RS records in cache
+ sendInfo(Warning, WarningNoExpectedRSMatch);
+ zidRec.resetSasVerified();
+ }
+ else { // No valid RS record in cache
+ sendInfo(Warning, WarningNoRSMatch);
+ }
+ }
+ else { // at least one RS matches
+ sendInfo(Info, InfoRSMatchFound);
+ }
+
+ /*
+ * ready to generate s0 here.
+ * The formular to compute S0 (Refer to ZRTP specification 5.4.4):
+ *
+ s0 = hash( counter | DHResult | "ZRTP-HMAC-KDF" | ZIDi | ZIDr | \
+ total_hash | len(s1) | s1 | len(s2) | s2 | len(s3) | s3)
+ *
+ * Note: in this function we are Responder, thus ZIDi is the peer's zid
+ * (peerZid), ZIDr is our zid.
+ */
+
+ /*
+ * These arrays hold the pointers and lengths of the data that must be
+ * hashed to create S0. According to the formula the max number of
+ * elements to hash is 12, add one for the terminating "NULL"
+ */
+ unsigned char* data[13];
+ unsigned int length[13];
+ uint32_t pos = 0; // index into the array
+
+
+ // we need a number of length data items, so define them here
+ uint32_t counter, sLen[3];
+
+ //Very first element is a fixed counter, big endian
+ counter = 1;
+ counter = htonl(counter);
+ data[pos] = (unsigned char*)&counter;
+ length[pos++] = sizeof(uint32_t);
+
+ // Next is the DH result itself
+ data[pos] = DHss;
+ length[pos++] = dhContext->getDhSize();
+
+ // Next the fixed string "ZRTP-HMAC-KDF"
+ data[pos] = (unsigned char*)KDFString;
+ length[pos++] = strlen(KDFString);
+
+ // Next is Initiator's id (ZIDi), in this case as Responder
+ // it is peerZid
+ data[pos] = peerZid;
+ length[pos++] = ZID_SIZE;
+
+ // Next is Responder's id (ZIDr), in this case our own zid
+ data[pos] = zid;
+ length[pos++] = ZID_SIZE;
+
+ // Next ist total hash (messageHash) itself
+ data[pos] = messageHash;
+ length[pos++] = hashLength;
+
+ /*
+ * For each matching shared secret hash the length of
+ * the shared secret as 32 bit big-endian number followd by the
+ * shared secret itself. The length of a shared seceret is
+ * currently fixed to SHA256_DIGEST_LENGTH. If a shared
+ * secret is not used _only_ its length is hased as zero
+ * length. NOTE: if implementing auxSecret and/or pbxSecret -> check
+ * this length stuff again.
+ */
+ int secretHashLen = RS_LENGTH;
+ secretHashLen = htonl(secretHashLen); // prepare 32 bit big-endian number
+
+ for (int32_t i = 0; i < 3; i++) {
+ if (setD[i] != NULL) { // a matching secret, set length, then secret
+ sLen[i] = secretHashLen;
+ data[pos] = (unsigned char*)&sLen[i];
+ length[pos++] = sizeof(uint32_t);
+ data[pos] = (unsigned char*)setD[i];
+ length[pos++] = RS_LENGTH;
+ }
+ else { // no machting secret, set length 0, skip secret
+ sLen[i] = 0;
+ data[pos] = (unsigned char*)&sLen[i];
+ length[pos++] = sizeof(uint32_t);
+ }
+ }
+
+ data[pos] = NULL;
+ hashListFunction(data, length, s0);
+// hexdump("S0 R", s0, hashLength);
+
+ memset(DHss, 0, dhContext->getDhSize());
+ delete[] DHss;
+ DHss = NULL;
+
+ computeSRTPKeys();
+ memset(s0, 0, MAX_DIGEST_LENGTH);
+}
+
+
+void ZRtp::KDF(uint8_t* key, uint32_t keyLength, uint8_t* label, int32_t labelLength,
+ uint8_t* context, int32_t contextLength, int32_t L, uint8_t* output) {
+
+ unsigned char* data[6];
+ uint32_t length[6];
+ uint32_t pos = 0; // index into the array
+ uint32_t maclen = 0;
+
+ // Very first element is a fixed counter, big endian
+ uint32_t counter = 1;
+ counter = htonl(counter);
+ data[pos] = (unsigned char*)&counter;
+ length[pos++] = sizeof(uint32_t);
+
+ // Next element is the label, null terminated, labelLength includes null byte.
+ data[pos] = label;
+ length[pos++] = labelLength;
+
+ // Next is the KDF context
+ data[pos] = context;
+ length[pos++] = contextLength;
+
+ // last element is HMAC length in bits, big endian
+ uint32_t len = htonl(L);
+ data[pos] = (unsigned char*)&len;
+ length[pos++] = sizeof(uint32_t);
+
+ data[pos] = NULL;
+
+ // Use negotiated hash.
+ hmacListFunction(key, keyLength, data, length, output, &maclen);
+}
+
+// Compute the Multi Stream mode s0
+void ZRtp::generateKeysMultiStream() {
+
+ // allocate the maximum size, compute real size to use
+ uint8_t KDFcontext[sizeof(peerZid)+sizeof(zid)+sizeof(messageHash)];
+ int32_t kdfSize = sizeof(peerZid)+sizeof(zid)+hashLength;
+
+ if (myRole == Responder) {
+ memcpy(KDFcontext, peerZid, sizeof(peerZid));
+ memcpy(KDFcontext+sizeof(peerZid), zid, sizeof(zid));
+ }
+ else {
+ memcpy(KDFcontext, zid, sizeof(zid));
+ memcpy(KDFcontext+sizeof(zid), peerZid, sizeof(peerZid));
+ }
+ memcpy(KDFcontext+sizeof(zid)+sizeof(peerZid), messageHash, hashLength);
+
+ KDF(zrtpSession, hashLength, (unsigned char*)zrtpMsk, strlen(zrtpMsk)+1, KDFcontext, kdfSize, hashLength*8, s0);
+
+ memset(KDFcontext, 0, sizeof(KDFcontext));
+
+ computeSRTPKeys();
+}
+
+void ZRtp::computePBXSecret() {
+ // Construct the KDF context as per ZRTP specification chap 7.3.1:
+ // ZIDi || ZIDr
+ uint8_t KDFcontext[sizeof(peerZid)+sizeof(zid)];
+ int32_t kdfSize = sizeof(peerZid)+sizeof(zid);
+
+ if (myRole == Responder) {
+ memcpy(KDFcontext, peerZid, sizeof(peerZid));
+ memcpy(KDFcontext+sizeof(peerZid), zid, sizeof(zid));
+ }
+ else {
+ memcpy(KDFcontext, zid, sizeof(zid));
+ memcpy(KDFcontext+sizeof(zid), peerZid, sizeof(peerZid));
+ }
+
+ KDF(zrtpSession, hashLength, (unsigned char*)zrtpTrustedMitm, strlen(zrtpTrustedMitm)+1, KDFcontext,
+ kdfSize, SHA256_DIGEST_LENGTH * 8, pbxSecretTmpBuffer);
+
+ pbxSecretTmp = pbxSecretTmpBuffer; // set pointer to buffer, signal PBX secret was computed
+}
+
+
+void ZRtp::computeSRTPKeys() {
+
+ // allocate the maximum size, compute real size to use
+ uint8_t KDFcontext[sizeof(peerZid)+sizeof(zid)+sizeof(messageHash)];
+ int32_t kdfSize = sizeof(peerZid)+sizeof(zid)+hashLength;
+
+ int32_t keyLen = cipher->getKeylen() * 8;
+
+ if (myRole == Responder) {
+ memcpy(KDFcontext, peerZid, sizeof(peerZid));
+ memcpy(KDFcontext+sizeof(peerZid), zid, sizeof(zid));
+ }
+ else {
+ memcpy(KDFcontext, zid, sizeof(zid));
+ memcpy(KDFcontext+sizeof(zid), peerZid, sizeof(peerZid));
+ }
+ memcpy(KDFcontext+sizeof(zid)+sizeof(peerZid), messageHash, hashLength);
+
+ // Inititiator key and salt
+ KDF(s0, hashLength, (unsigned char*)iniMasterKey, strlen(iniMasterKey)+1, KDFcontext, kdfSize, keyLen, srtpKeyI);
+ KDF(s0, hashLength, (unsigned char*)iniMasterSalt, strlen(iniMasterSalt)+1, KDFcontext, kdfSize, 112, srtpSaltI);
+
+ // Responder key and salt
+ KDF(s0, hashLength, (unsigned char*)respMasterKey, strlen(respMasterKey)+1, KDFcontext, kdfSize, keyLen, srtpKeyR);
+ KDF(s0, hashLength, (unsigned char*)respMasterSalt, strlen(respMasterSalt)+1, KDFcontext, kdfSize, 112, srtpSaltR);
+
+ // The HMAC keys for GoClear
+ KDF(s0, hashLength, (unsigned char*)iniHmacKey, strlen(iniHmacKey)+1, KDFcontext, kdfSize, hashLength*8, hmacKeyI);
+
+ KDF(s0, hashLength, (unsigned char*)respHmacKey, strlen(respHmacKey)+1, KDFcontext, kdfSize, hashLength*8, hmacKeyR);
+
+ // The keys for Confirm messages
+ KDF(s0, hashLength, (unsigned char*)iniZrtpKey, strlen(iniZrtpKey)+1, KDFcontext, kdfSize, keyLen, zrtpKeyI);
+ KDF(s0, hashLength, (unsigned char*)respZrtpKey, strlen(respZrtpKey)+1, KDFcontext, kdfSize, keyLen, zrtpKeyR);
+
+ if (!multiStream) {
+ // Compute the new Retained Secret
+ KDF(s0, hashLength, (unsigned char*)retainedSec, strlen(retainedSec)+1, KDFcontext, kdfSize, SHA256_DIGEST_LENGTH*8, newRs1);
+
+ // Compute the ZRTP Session Key
+ KDF(s0, hashLength, (unsigned char*)zrtpSessionKey, strlen(zrtpSessionKey)+1, KDFcontext, kdfSize, hashLength*8, zrtpSession);
+
+ // perform SAS generation according to chapter 5.5 and 8.
+ // we don't need a speciai sasValue filed. sasValue are the first
+ // (leftmost) 32 bits (4 bytes) of sasHash
+ uint8_t sasBytes[4];
+ KDF(s0, hashLength, (unsigned char*)sasString, strlen(sasString)+1, KDFcontext, kdfSize, SHA256_DIGEST_LENGTH*8, sasHash);
+
+ // according to chapter 8 only the leftmost 20 bits of sasValue (aka
+ // sasHash) are used to create the character SAS string of type SAS
+ // base 32 (5 bits per character)
+ sasBytes[0] = sasHash[0];
+ sasBytes[1] = sasHash[1];
+ sasBytes[2] = sasHash[2] & 0xf0;
+ sasBytes[3] = 0;
+ SAS = Base32(sasBytes, 20).getEncoded();
+ if (signSasSeen)
+ callback->signSAS(sasHash);
+ }
+ memset(KDFcontext, 0, sizeof(KDFcontext));
+}
+
+bool ZRtp::srtpSecretsReady(EnableSecurity part) {
+
+ SrtpSecret_t sec;
+
+ sec.symEncAlgorithm = cipher->getAlgoId();
+
+ sec.keyInitiator = srtpKeyI;
+ sec.initKeyLen = cipher->getKeylen() * 8;
+ sec.saltInitiator = srtpSaltI;
+ sec.initSaltLen = 112;
+
+ sec.keyResponder = srtpKeyR;
+ sec.respKeyLen = cipher->getKeylen() * 8;
+ sec.saltResponder = srtpSaltR;
+ sec.respSaltLen = 112;
+
+ sec.authAlgorithm = authLength->getAlgoId();
+ sec.srtpAuthTagLen = authLength->getKeylen();
+
+ sec.sas = SAS;
+ sec.role = myRole;
+
+ return callback->srtpSecretsReady(&sec, part);
+}
+
+
+void ZRtp::setNegotiatedHash(AlgorithmEnum* hash) {
+ switch (zrtpHashes.getOrdinal(*hash)) {
+ case 0:
+ hashLength = SHA256_DIGEST_LENGTH;
+ hashFunction = sha256;
+ hashListFunction = sha256;
+
+ hmacFunction = hmac_sha256;
+ hmacListFunction = hmac_sha256;
+
+ createHashCtx = createSha256Context;
+ closeHashCtx = closeSha256Context;
+ hashCtxFunction = sha256Ctx;
+ hashCtxListFunction = sha256Ctx;
+ break;
+
+ case 1:
+ hashLength = SHA384_DIGEST_LENGTH;
+ hashFunction = sha384;
+ hashListFunction = sha384;
+
+ hmacFunction = hmac_sha384;
+ hmacListFunction = hmac_sha384;
+
+ createHashCtx = createSha384Context;
+ closeHashCtx = closeSha384Context;
+ hashCtxFunction = sha384Ctx;
+ hashCtxListFunction = sha384Ctx;
+ break;
+ }
+}
+
+
+void ZRtp::srtpSecretsOff(EnableSecurity part) {
+ callback->srtpSecretsOff(part);
+}
+
+void ZRtp::SASVerified() {
+ if (paranoidMode)
+ return;
+
+ // Initialize a ZID record to get peer's retained secrets
+ ZIDRecord zidRec(peerZid);
+ ZIDFile *zid = ZIDFile::getInstance();
+
+ zid->getRecord(&zidRec);
+ zidRec.setSasVerified();
+ zid->saveRecord(&zidRec);
+}
+
+void ZRtp::resetSASVerified() {
+ // Initialize a ZID record to get peer's retained secrets
+ ZIDRecord zidRec(peerZid);
+ ZIDFile *zid = ZIDFile::getInstance();
+
+ zid->getRecord(&zidRec);
+ zidRec.resetSasVerified();
+ zid->saveRecord(&zidRec);
+}
+
+
+void ZRtp::sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) {
+
+ // We've reached secure state: overwrite the SRTP master key and master salt.
+ if (severity == Info && subCode == InfoSecureStateOn) {
+ memset(srtpKeyI, 0, cipher->getKeylen());
+ memset(srtpSaltI, 0, 112/8);
+ memset(srtpKeyR, 0, cipher->getKeylen());
+ memset(srtpSaltR, 0, 112/8);
+ }
+ callback->sendInfo(severity, subCode);
+}
+
+
+void ZRtp::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) {
+ callback->zrtpNegotiationFailed(severity, subCode);
+}
+
+void ZRtp::zrtpNotSuppOther() {
+ callback->zrtpNotSuppOther();
+}
+
+void ZRtp::synchEnter() {
+ callback->synchEnter();
+}
+
+void ZRtp::synchLeave() {
+ callback->synchLeave();
+}
+
+int32_t ZRtp::sendPacketZRTP(ZrtpPacketBase *packet) {
+ return ((packet == NULL) ? 0 :
+ callback->sendDataZRTP(packet->getHeaderBase(), (packet->getLength() * 4) + 4));
+}
+
+int32_t ZRtp::activateTimer(int32_t tm) {
+ return (callback->activateTimer(tm));
+}
+
+int32_t ZRtp::cancelTimer() {
+ return (callback->cancelTimer());
+}
+
+void ZRtp::setAuxSecret(uint8_t* data, int32_t length) {
+ if (length > 0) {
+ auxSecret = new uint8_t[length];
+ auxSecretLength = length;
+ memcpy(auxSecret, data, length);
+ }
+}
+
+void ZRtp::setClientId(std::string id) {
+ if (id.size() < CLIENT_ID_SIZE) {
+ unsigned char tmp[CLIENT_ID_SIZE +1] = {' '};
+ memcpy(tmp, id.c_str(), id.size());
+ tmp[CLIENT_ID_SIZE] = 0;
+ zrtpHello.setClientId(tmp);
+ } else {
+ zrtpHello.setClientId((unsigned char*)id.c_str());
+ }
+
+ int32_t len = zrtpHello.getLength() * ZRTP_WORD_SIZE;
+
+ // Hello packet is ready now, compute its HMAC
+ // (excluding the HMAC field (2*ZTP_WORD_SIZE)) and store in Hello
+ // use the implicit hash function
+ uint8_t hmac[IMPL_MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+ hmacFunctionImpl(H2, HASH_IMAGE_SIZE, (uint8_t*)zrtpHello.getHeaderBase(), len-(2*ZRTP_WORD_SIZE), hmac, &macLen);
+ zrtpHello.setHMAC(hmac);
+
+ // calculate hash over the final Hello packet, refer to chap 9.1 how to
+ // use this hash in SIP/SDP.
+ hashFunctionImpl((uint8_t*)zrtpHello.getHeaderBase(), len, helloHash);
+}
+
+void ZRtp::storeMsgTemp(ZrtpPacketBase* pkt) {
+ uint32_t length = pkt->getLength() * ZRTP_WORD_SIZE;
+ length = (length > sizeof(tempMsgBuffer)) ? sizeof(tempMsgBuffer) : length;
+ memset(tempMsgBuffer, 0, sizeof(tempMsgBuffer));
+ memcpy(tempMsgBuffer, (uint8_t*)pkt->getHeaderBase(), length);
+ lengthOfMsgData = length;
+}
+
+bool ZRtp::checkMsgHmac(uint8_t* key) {
+ uint8_t hmac[IMPL_MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+ int32_t len = lengthOfMsgData-(HMAC_SIZE); // compute HMAC, but exlude the stored HMAC :-)
+
+ // Use the implicit hash function
+ hmacFunctionImpl(key, HASH_IMAGE_SIZE, tempMsgBuffer, len, hmac, &macLen);
+ return (memcmp(hmac, tempMsgBuffer+len, (HMAC_SIZE)) == 0 ? true : false);
+}
+
+std::string ZRtp::getHelloHash() {
+ std::ostringstream stm;
+
+ uint8_t* hp = helloHash;
+
+ stm << zrtpVersion;
+ stm << " ";
+ stm.fill('0');
+ stm << hex;
+ for (int i = 0; i < hashLengthImpl; i++) {
+ stm.width(2);
+ stm << static_cast<uint32_t>(*hp++);
+ }
+ return stm.str();
+}
+
+std::string ZRtp::getPeerHelloHash() {
+ std::ostringstream stm;
+
+ if (peerHelloVersion[0] == 0)
+ return std::string();
+
+ uint8_t* hp = peerHelloHash;
+
+ stm << peerHelloVersion;
+ stm << " ";
+ stm.fill('0');
+ stm << hex;
+ for (int i = 0; i < hashLengthImpl; i++) {
+ stm.width(2);
+ stm << static_cast<uint32_t>(*hp++);
+ }
+ return stm.str();
+}
+
+std::string ZRtp::getMultiStrParams() {
+
+ // the string will hold binary data - it's opaque to the application
+ std::string str("");
+ char tmp[MAX_DIGEST_LENGTH + 1 + 1 + 1]; // hash length + cipher + authLength + hash
+
+ if (inState(SecureState) && !multiStream) {
+ // construct array that holds zrtpSession, cipher type, auth-length, and hash type
+ tmp[0] = zrtpHashes.getOrdinal(*hash);
+ tmp[1] = zrtpAuthLengths.getOrdinal(*authLength);
+ tmp[2] = zrtpSymCiphers.getOrdinal(*cipher);
+ memcpy(tmp+3, zrtpSession, hashLength);
+ str.assign(tmp, hashLength + 1 + 1 + 1); // set chars (bytes) to the string
+ }
+ return str;
+}
+
+void ZRtp::setMultiStrParams(std::string parameters) {
+
+ char tmp[MAX_DIGEST_LENGTH + 1 + 1 + 1]; // max. hash length + cipher + authLength + hash
+
+ // First get negotiated hash from parameters, set algorithms and length
+ int i = parameters.at(0) & 0xff;
+ hash = &zrtpHashes.getByOrdinal(i);
+ setNegotiatedHash(hash); // sets hashlength
+
+ // use string.copy(buffer, num, start=0) to retrieve chars (bytes) from the string
+ parameters.copy(tmp, hashLength + 1 + 1 + 1, 0);
+
+ i = tmp[1] & 0xff;
+ authLength = &zrtpAuthLengths.getByOrdinal(i);
+ i = tmp[2] & 0xff;
+ cipher = &zrtpSymCiphers.getByOrdinal(i);
+ memcpy(zrtpSession, tmp+3, hashLength);
+
+ // after setting zrtpSession, cipher, and auth-length set multi-stream to true
+ multiStream = true;
+ stateEngine->setMultiStream(true);
+}
+
+bool ZRtp::isMultiStream() {
+ return multiStream;
+}
+
+bool ZRtp::isMultiStreamAvailable() {
+ return multiStreamAvailable;
+}
+
+void ZRtp::acceptEnrollment(bool accepted) {
+ if (!accepted) {
+ callback->zrtpInformEnrollment(EnrollmentCanceled);
+ return;
+ }
+ // Get peer's zid record to store the pbx (MitM) secret
+ // Initialize a ZID record to get peer's retained secrets
+ ZIDRecord zidRec(peerZid);
+ ZIDFile* zid = ZIDFile::getInstance();
+ zid->getRecord(&zidRec);
+
+ if (pbxSecretTmp != NULL) {
+ zidRec.setMiTMData(pbxSecretTmp);
+ callback->zrtpInformEnrollment(EnrollmentOk);
+ }
+ else {
+ callback->zrtpInformEnrollment(EnrollmentFailed);
+ return;
+ }
+ zid->saveRecord(&zidRec);
+ return;
+}
+
+bool ZRtp::setSignatureData(uint8_t* data, int32_t length) {
+ if ((length % 4) != 0)
+ return false;
+
+ ZrtpPacketConfirm* cfrm = (myRole == Responder) ? &zrtpConfirm1 : &zrtpConfirm2;
+ cfrm->setSignatureLength(length / 4);
+ return cfrm->setSignatureData(data, length);
+}
+
+const uint8_t* ZRtp::getSignatureData() {
+ return signatureData;
+}
+
+int32_t ZRtp::getSignatureLength() {
+ return signatureLength * ZRTP_WORD_SIZE;
+}
+
+void ZRtp::conf2AckSecure() {
+ Event_t ev;
+
+ ev.type = ZrtpPacket;
+ ev.packet = (uint8_t*)&zrtpConf2Ack;
+
+ if (stateEngine != NULL) {
+ stateEngine->processEvent(&ev);
+ }
+}
+
+int32_t ZRtp::compareCommit(ZrtpPacketCommit *commit) {
+ // TODO: enhance to compare according to rules defined in chapter 4.2
+ int32_t len = 0;
+ len = !multiStream ? HVI_SIZE : (4 * ZRTP_WORD_SIZE);
+ return (memcmp(hvi, commit->getHvi(), len));
+}
+
+bool ZRtp::isEnrollmentMode() {
+ return enrollmentMode;
+}
+
+void ZRtp::setEnrollmentMode(bool enrollmentMode) {
+ this->enrollmentMode = enrollmentMode;
+}
+
+bool ZRtp::isPeerEnrolled() {
+ return peerIsEnrolled;
+}
+
+bool ZRtp::sendSASRelayPacket(uint8_t* sh, std::string render) {
+
+ uint8_t confMac[MAX_DIGEST_LENGTH];
+ uint32_t macLen;
+ uint8_t* hkey, *ekey;
+
+ // If we are responder then the PBX used it's Initiator keys
+ if (myRole == Responder) {
+ hkey = hmacKeyR;
+ ekey = zrtpKeyR;
+ // TODO: check signature length in zrtpConfirm1 and if not zero copy Signature data
+ }
+ else {
+ hkey = hmacKeyI;
+ ekey = zrtpKeyI;
+ // TODO: check signature length in zrtpConfirm2 and if not zero copy Signature data
+ }
+ // Prepare IV data that we will use during confirm packet encryption.
+ randomZRTP(randomIV, sizeof(randomIV));
+ zrtpSasRelay.setIv(randomIV);
+ zrtpSasRelay.setTrustedSas(sh);
+ zrtpSasRelay.setSas((uint8_t*)render.c_str());
+
+ // Encrypt and HMAC with Initiator's key - we are Initiator here
+ int16_t hmlen = (zrtpSasRelay.getLength() - 9) * ZRTP_WORD_SIZE;
+ cipher->getEncrypt()(ekey, cipher->getKeylen(), randomIV, (uint8_t*)zrtpSasRelay.getFiller(), hmlen);
+
+ // Use negotiated HMAC (hash)
+ hmacFunction(hkey, hashLength, (unsigned char*)zrtpSasRelay.getFiller(), hmlen, confMac, &macLen);
+
+ zrtpSasRelay.setHmac(confMac);
+
+ stateEngine->sendSASRelay(&zrtpSasRelay);
+ return true;
+}
+
+std::string ZRtp::getSasType() {
+ std::string sasT(sasType->getName());
+ return sasT;
+}
+
+uint8_t* ZRtp::getSasHash() {
+ return sasHash;
+}
+
+int32_t ZRtp::getPeerZid(uint8_t* data) {
+ memcpy(data, peerZid, IDENTIFIER_LEN);
+ return IDENTIFIER_LEN;
+}
+
+/** EMACS **
+ * Local variables:
+ * mode: c++
+ * c-default-style: ellemtel
+ * c-basic-offset: 4
+ * End:
+ */
+