* #40116: Add zrtp related files, update libzrtpcpp
diff --git a/jni/libzrtp/sources/zrtp/ZRtp.cpp b/jni/libzrtp/sources/zrtp/ZRtp.cpp
index 9fb8e0a..c7d2a46 100755
--- a/jni/libzrtp/sources/zrtp/ZRtp.cpp
+++ b/jni/libzrtp/sources/zrtp/ZRtp.cpp
@@ -1,8 +1,8 @@
/*
- Copyright (C) 2006-2009 Werner Dittmann
+ Copyright (C) 2006-2013 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
+ 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.
@@ -25,6 +25,12 @@
#include <crypto/sha256.h>
#include <crypto/hmac384.h>
#include <crypto/sha384.h>
+
+#include <crypto/skeinMac256.h>
+#include <crypto/skein256.h>
+#include <crypto/skeinMac384.h>
+#include <crypto/skein384.h>
+
#include <crypto/aesCFB.h>
#include <crypto/twoCFB.h>
@@ -52,7 +58,7 @@
}
fprintf(stderr, "\n");
}
- */
+ * */
/*
* This method simplifies detection of libzrtpcpp inside Automake, configure
@@ -73,9 +79,10 @@
callback(cb), dhContext(NULL), DHss(NULL), auxSecret(NULL), auxSecretLength(0), rs1Valid(false),
rs2Valid(false), msgShaContext(NULL), hash(NULL), cipher(NULL), pubKey(NULL), sasType(NULL), authLength(NULL),
multiStream(false), multiStreamAvailable(false), peerIsEnrolled(false), mitmSeen(false), pbxSecretTmp(NULL),
- enrollmentMode(false), configureAlgos(*config), zidRec(NULL) {
+ enrollmentMode(false), configureAlgos(*config), zidRec(NULL), saveZidRecord(true) {
enableMitmEnrollment = config->isTrustedMitM();
+ signatureData = NULL;
paranoidMode = config->isParanoidMode();
// setup the implicit hash function pointers and length
@@ -86,6 +93,8 @@
hmacFunctionImpl = hmac_sha256;
hmacListFunctionImpl = hmac_sha256;
+ memcpy(ownZid, myZid, ZID_SIZE); // save the ZID
+
/*
* Generate H0 as a random number (256 bits, 32 bytes) and then
* the hash chain, refer to chapter 9. Use the implicit hash function.
@@ -95,21 +104,40 @@
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
+ // configure all supported Hello packet versions
+ zrtpHello_11.configureHello(&configureAlgos);
+ zrtpHello_11.setH3(H3); // set H3 in Hello, included in helloHash
+ zrtpHello_11.setZid(ownZid);
+ zrtpHello_11.setVersion((uint8_t*)zrtpVersion_11);
+
+
+ zrtpHello_12.configureHello(&configureAlgos);
+ zrtpHello_12.setH3(H3); // set H3 in Hello, included in helloHash
+ zrtpHello_12.setZid(ownZid);
+ zrtpHello_12.setVersion((uint8_t*)zrtpVersion_12);
+
+ if (mitmm) { // this session acts for a trusted MitM (PBX)
+ zrtpHello_11.setMitmMode();
+ zrtpHello_12.setMitmMode();
+ }
+ if (sasSignSupport) { // the application supports SAS signing
+ zrtpHello_11.setSasSign();
+ zrtpHello_12.setSasSign();
+ }
+
+ // Keep array in ascending order (greater index -> greater version)
+ helloPackets[0].packet = &zrtpHello_11;
+ helloPackets[0].version = zrtpHello_11.getVersionInt();
+ setClientId(id, &helloPackets[0]); // set id, compute HMAC and final helloHash
+
+ helloPackets[1].packet = &zrtpHello_12;
+ helloPackets[1].version = zrtpHello_12.getVersionInt();
+ setClientId(id, &helloPackets[1]); // set id, compute HMAC and final helloHash
+
+ currentHelloPacket = helloPackets[SUPPORTED_ZRTP_VERSIONS-1].packet; // start with highest supported version
+ helloPackets[SUPPORTED_ZRTP_VERSIONS].packet = NULL;
peerHelloVersion[0] = 0;
- memcpy(ownZid, myZid, ZID_SIZE);
- zrtpHello.setZid(ownZid);
-
- 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);
}
@@ -235,7 +263,7 @@
}
ZrtpPacketHello* ZRtp::prepareHello() {
- return &zrtpHello;
+ return currentHelloPacket;
}
ZrtpPacketHelloAck* ZRtp::prepareHelloAck() {
@@ -249,16 +277,17 @@
*/
ZrtpPacketCommit* ZRtp::prepareCommit(ZrtpPacketHello *hello, uint32_t* errMsg) {
+ myRole = Initiator;
+
+ if (!hello->isLengthOk()) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
// Save data before detailed checks - may aid in analysing problems
peerClientId.assign((char*)hello->getClientId(), ZRTP_WORD_SIZE * 4);
memcpy(peerHelloVersion, hello->getVersion(), ZRTP_WORD_SIZE);
peerHelloVersion[ZRTP_WORD_SIZE] = 0;
- 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, ownZid, ZID_SIZE) == 0) { // peers have same ZID????
@@ -291,14 +320,15 @@
sasType = findBestSASType(hello);
if (!multiStream) {
- pubKey = findBestPubkey(hello); // Check for public key algorithm first, sets 'hash' as well
+ pubKey = findBestPubkey(hello); // Check for public key algorithm first, must set 'hash' as well
if (hash == NULL) {
*errMsg = UnsuppHashType;
return NULL;
}
if (cipher == NULL) // public key selection may have set the cipher already
cipher = findBestCipher(hello, pubKey);
- authLength = findBestAuthLen(hello);
+ if (authLength == NULL) // public key selection may have set the SRTP authLen already
+ authLength = findBestAuthLen(hello);
multiStreamAvailable = checkMultiStream(hello);
}
else {
@@ -440,7 +470,7 @@
}
/*
- * At this point we will take the role of the Responder. We may have been in
+ * At this point we will take the role of the Responder. We 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
@@ -452,8 +482,21 @@
sendInfo(Info, InfoRespCommitReceived);
- // The following code check the hash chain according chapter 10 to detect
- // false ZRTP packets.
+ if (!commit->isLengthOk(ZrtpPacketCommit::DhExchange)) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+
+ // Check if ZID in Commit is the same as we got in Hello
+ uint8_t tmpZid[ZID_SIZE];
+ memcpy(tmpZid, commit->getZid(), ZID_SIZE);
+ if (memcmp(peerZid, tmpZid, ZID_SIZE) != 0) { // ZIDs do not match????
+ sendInfo(Severe, SevereProtocolError);
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
+
+ // The following code checks 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);
@@ -511,8 +554,8 @@
*errMsg = UnsuppPKExchange;
return NULL;
}
- if (*(int32_t*)(cp->getName()) == *(int32_t*)ec38) {
- if (*(int32_t*)(hash->getName()) != *(int32_t*)s384) {
+ if (*(int32_t*)(cp->getName()) == *(int32_t*)ec38 || *(int32_t*)(cp->getName()) == *(int32_t*)e414) {
+ if (!(*(int32_t*)(hash->getName()) == *(int32_t*)s384 || *(int32_t*)(hash->getName()) == *(int32_t*)skn3)) {
*errMsg = UnsuppHashType;
return NULL;
}
@@ -528,7 +571,7 @@
sasType = cp;
// dhContext cannot be NULL - always setup during prepareCommit()
- // check if we can use the dhContext prepared by 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())) {
@@ -540,7 +583,11 @@
dhContext->getPubKeyBytes(pubKeyBytes);
+ // Re-compute auxSecretIDr because we changed roles *IDr with my H3, *IDi with peer's H3
// Setup a DHPart1 packet.
+ myRole = Responder;
+ computeAuxSecretIds(); // recompute AUX secret ids because we are now Responder, use different H3
+
zrtpDH1.setPubKeyType(pubKey->getName());
zrtpDH1.setMessageType((uint8_t*)DHPart1Msg);
zrtpDH1.setRs1Id(rs1IDr);
@@ -561,22 +608,20 @@
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.
+ // We are responder. Release the pre-computed SHA context because it was prepared for Initiator.
+ // Setup and compute for Responder.
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);
+ // 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*)currentHelloPacket->getHeaderBase(), currentHelloPacket->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);
@@ -595,6 +640,10 @@
sendInfo(Info, InfoInitDH1Received);
+ if (!dhPart1->isLengthOk()) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
// 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.
@@ -633,8 +682,6 @@
}
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.
@@ -667,6 +714,10 @@
sendInfo(Info, InfoRespDH2Received);
+ if (!dhPart2->isLengthOk()) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
// 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
@@ -689,7 +740,7 @@
// 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);
+ computeHvi(dhPart2, currentHelloPacket);
if (memcmp(hvi, peerHvi, HVI_SIZE) != 0) {
*errMsg = DHErrorWrongHVI;
return NULL;
@@ -706,8 +757,8 @@
return NULL;
}
dhContext->computeSecretKey(pvi, DHss);
- // Hash the Initiator's DH2 into the message Hash (other messages already
- // prepared, see method prepareDHPart1().
+
+ // 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);
@@ -770,6 +821,10 @@
sendInfo(Info, InfoRespCommitReceived);
+ if (!commit->isLengthOk(ZrtpPacketCommit::MultiStream)) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
// The following code checks the hash chain according chapter 10 to detect
// false ZRTP packets.
// Use implicit hash function
@@ -840,7 +895,7 @@
// 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*)currentHelloPacket->getHeaderBase(), currentHelloPacket->getLength() * ZRTP_WORD_SIZE);
hashCtxFunction(msgShaContext, (unsigned char*)commit->getHeaderBase(), commit->getLength() * ZRTP_WORD_SIZE);
closeHashCtx(msgShaContext, messageHash);
@@ -878,6 +933,10 @@
sendInfo(Info, InfoInitConf1Received);
+ if (!confirm1->isLengthOk()) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
uint8_t confMac[MAX_DIGEST_LENGTH];
uint32_t macLen;
@@ -903,7 +962,7 @@
return NULL;
}
signatureLength = confirm1->getSignatureLength();
- if (signSasSeen && signatureLength > 0) {
+ if (signSasSeen && signatureLength > 0 && confirm1->isSignatureLengthOk()) {
signatureData = confirm1->getSignatureData();
callback->checkSASSignature(sasHash);
// TODO: error handling if checkSASSignature returns false.
@@ -956,7 +1015,8 @@
zrtpConfirm2.setPBXEnrollment();
}
}
- getZidCacheInstance()->saveRecord(zidRec);
+ if (saveZidRecord)
+ getZidCacheInstance()->saveRecord(zidRec);
// Encrypt and HMAC with Initiator's key - we are Initiator here
hmlen = (zrtpConfirm2.getLength() - 9) * ZRTP_WORD_SIZE;
@@ -994,6 +1054,10 @@
// don't update SAS, RS
sendInfo(Info, InfoInitConf1Received);
+ if (!confirm1->isLengthOk()) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
uint8_t confMac[MAX_DIGEST_LENGTH];
uint32_t macLen;
@@ -1059,6 +1123,10 @@
sendInfo(Info, InfoRespConf2Received);
+ if (!confirm2->isLengthOk()) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
uint8_t confMac[MAX_DIGEST_LENGTH];
uint32_t macLen;
@@ -1088,7 +1156,7 @@
return NULL;
}
signatureLength = confirm2->getSignatureLength();
- if (signSasSeen && signatureLength > 0) {
+ if (signSasSeen && signatureLength > 0 && confirm2->isSignatureLengthOk() ) {
signatureData = confirm2->getSignatureData();
callback->checkSASSignature(sasHash);
// TODO: error handling if checkSASSignature returns false.
@@ -1106,7 +1174,8 @@
// save new RS1, this inherits the verified flag from old RS1
zidRec->setNewRs1((const uint8_t*)newRs1);
- getZidCacheInstance()->saveRecord(zidRec);
+ if (saveZidRecord)
+ getZidCacheInstance()->saveRecord(zidRec);
// Ask for enrollment only if enabled via configuration and the
// confirm packet contains the enrollment flag. The enrolling user
@@ -1141,7 +1210,10 @@
}
ZrtpPacketErrorAck* ZRtp::prepareErrorAck(ZrtpPacketError* epkt) {
- sendInfo(ZrtpError, epkt->getErrorCode() * -1);
+ if (epkt->getLength() < 4)
+ sendInfo(ZrtpError, CriticalSWError * -1);
+ else
+ sendInfo(ZrtpError, epkt->getErrorCode() * -1);
return &zrtpErrorAck;
}
@@ -1151,7 +1223,8 @@
}
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)
@@ -1167,6 +1240,10 @@
if (!mitmSeen || paranoidMode)
return &zrtpRelayAck;
+ if (!srly->isLengthOk()) {
+ *errMsg = CriticalSWError;
+ return NULL;
+ }
uint8_t* hkey, *ekey;
// If we are responder then the PBX used it's Initiator keys
if (myRole == Responder) {
@@ -1287,8 +1364,7 @@
if (num == 0) {
return &zrtpHashes.getByName(mandatoryHash);
}
- // Build list of configured hash algorithm names, append mandatory algos
- // if necessary.
+ // Build list of configured hash algorithm names.
numAlgosConf = configureAlgos.getNumConfiguredAlgos(HashAlgorithm);
for (i = 0; i < numAlgosConf; i++) {
algosConf[i] = &configureAlgos.getAlgoAt(HashAlgorithm, i);
@@ -1302,8 +1378,7 @@
numAlgosOffered++;
}
- // Lookup offered algos in configured algos. Because of appended
- // mandatory algorithms at least one match will happen
+ // Lookup offered algos in configured algos.
for (i = 0; i < numAlgosOffered; i++) {
for (ii = 0; ii < numAlgosConf; ii++) {
if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) {
@@ -1354,85 +1429,101 @@
return &zrtpSymCiphers.getByName(mandatoryCipher);
}
+// We can have the non-NIST in the list of orderedAlgos even if they are not available
+// in the code (refer to ZrtpConfigure.cpp). If they are not build in they cannot appear
+// in'configureAlgos' and thus not in the intersection lists. Thus a ZRTP build that
+// does not include the non-NIST curves also works without problems.
+//
AlgorithmEnum* ZRtp::findBestPubkey(ZrtpPacketHello *hello) {
- int i;
- int ii;
- int numAlgosIntersect;
- AlgorithmEnum* algosIntersect[ZrtpConfigure::maxNoOfAlgos+1];
-
- int numAlgosConf;
- AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1];
+ AlgorithmEnum* peerIntersect[ZrtpConfigure::maxNoOfAlgos+1];
+ AlgorithmEnum* ownIntersect[ZrtpConfigure::maxNoOfAlgos+1];
// Build list of own pubkey algorithm names, must follow the order
// defined in RFC 6189, chapter 4.1.2.
- const char *orderedAlgos[] = {dh2k, ec25, dh3k, ec38};
+ const char *orderedAlgos[] = {dh2k, e255, ec25, dh3k, e414, ec38};
int numOrderedAlgos = sizeof(orderedAlgos) / sizeof(const char*);
- int num = hello->getNumPubKeys();
- if (num == 0) {
- hash = &zrtpHashes.getByName(mandatoryHash); // set mandatory hash
+ int numAlgosPeer = hello->getNumPubKeys();
+ if (numAlgosPeer == 0) {
+ hash = findBestHash(hello); // find a hash algorithm
return &zrtpPubKeys.getByName(mandatoryPubKey);
}
- // 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) {
+ // Build own list of intersecting algos, keep own order or algorithms
+ // The list must include real public key algorithms only, so skip mult-stream mode,
+ // preshared and alike.
+ int numAlgosOwn = configureAlgos.getNumConfiguredAlgos(PubKeyAlgorithm);
+ int numOwnIntersect = 0;
+ for (int i = 0; i < numAlgosOwn; i++) {
+ ownIntersect[numOwnIntersect] = &configureAlgos.getAlgoAt(PubKeyAlgorithm, i);
+ if (*(int32_t*)(ownIntersect[numOwnIntersect]->getName()) == *(int32_t*)mult) {
continue; // skip multi-stream mode
}
- ii++;
- }
- numAlgosConf = ii;
-
- // Build list of intersecting algos: own and offered in Hello, intersect list is ordered according to offered algorithms
- for (numAlgosIntersect = 0, i = 0; i < num; i++) {
- for (ii = 0; ii < numAlgosConf; ii++) {
- algosIntersect[numAlgosIntersect] = &zrtpPubKeys.getByName((const char*)hello->getPubKeyType(i));
- if (*(int32_t*)(algosConf[ii]->getName()) == *(int32_t*)(algosIntersect[numAlgosIntersect]->getName())) {
- numAlgosIntersect++;
+ for (int ii = 0; ii < numAlgosPeer; ii++) {
+ if (*(int32_t*)(ownIntersect[numOwnIntersect]->getName()) == *(int32_t*)(zrtpPubKeys.getByName((const char*)hello->getPubKeyType(ii)).getName())) {
+ numOwnIntersect++;
+ break;
}
}
}
- if (numAlgosIntersect == 0) {
- // If we don't find a common algorithm - use the mandatory algorithms
- hash = &zrtpHashes.getByName(mandatoryHash);
+ // Build list of peer's intersecting algos: take own list as input and build a
+ // list of algorithms that we have in common. The order of the list is according
+ // to peer's Hello packet (peer's preferences).
+ int numPeerIntersect = 0;
+ for (int i = 0; i < numAlgosPeer; i++) {
+ peerIntersect[numPeerIntersect] = &zrtpPubKeys.getByName((const char*)hello->getPubKeyType(i));
+ for (int ii = 0; ii < numOwnIntersect; ii++) {
+ if (*(int32_t*)(ownIntersect[ii]->getName()) == *(int32_t*)(peerIntersect[numPeerIntersect]->getName())) {
+ numPeerIntersect++;
+ break;
+ }
+ }
+ }
+ if (numPeerIntersect == 0) { // If we don't have a common algorithm - use mandatory algorithms
+ hash = findBestHash(hello);
return &zrtpPubKeys.getByName(mandatoryPubKey);
}
+
+ // If we have only one algorithm in common or if the first entry matches - take it.
+ // Otherwise determine which algorithm from the intersection lists is first in the
+ // list of ordered algorithms and select it (RFC6189, section 4.1.2).
AlgorithmEnum* useAlgo;
- if (numAlgosIntersect > 1 && *(int32_t*)(algosConf[0]->getName()) != *(int32_t*)(algosIntersect[0]->getName())) {
+ if (numPeerIntersect > 1 && *(int32_t*)(ownIntersect[0]->getName()) != *(int32_t*)(peerIntersect[0]->getName())) {
int own, peer;
- const int32_t *name = (int32_t*)algosConf[0]->getName();
+ const int32_t *name = (int32_t*)ownIntersect[0]->getName();
for (own = 0; own < numOrderedAlgos; own++) {
if (*name == *(int32_t*)orderedAlgos[own])
break;
}
- name = (int32_t*)algosIntersect[0]->getName();
+ name = (int32_t*)peerIntersect[0]->getName();
for (peer = 0; peer < numOrderedAlgos; peer++) {
if (*name == *(int32_t*)orderedAlgos[peer])
break;
}
if (own < peer) {
- useAlgo = algosConf[0];
+ useAlgo = ownIntersect[0];
}
else {
- useAlgo = algosIntersect[0];
+ useAlgo = peerIntersect[0];
}
// find fastest of conf vs intersecting
}
else {
- useAlgo = algosIntersect[0];
+ useAlgo = peerIntersect[0];
}
+ int32_t algoName = *(int32_t*)(useAlgo->getName());
+
// select a corresponding strong hash if necessary.
- if (*(int32_t*)(useAlgo->getName()) == *(int32_t*)ec38) {
- hash = getStrongHashOffered(hello);
- cipher = getStrongCipherOffered(hello);
+ if (algoName == *(int32_t*)ec38 || algoName == *(int32_t*)e414) {
+ hash = getStrongHashOffered(hello, algoName);
+ cipher = getStrongCipherOffered(hello, algoName);
}
else {
- hash = findBestHash(hello);
+ hash = getHashOffered(hello, algoName);;
+ cipher = getCipherOffered(hello, algoName);
}
+ authLength = getAuthLenOffered(hello, algoName);
return useAlgo;
}
@@ -1450,14 +1541,14 @@
if (num == 0) {
return &zrtpSasTypes.getByName(mandatorySasType);
}
- // Buildlist of configured SAS algorithm names
+ // Build list of configured SAS algorithm names
numAlgosConf = configureAlgos.getNumConfiguredAlgos(SasType);
for (i = 0; i < numAlgosConf; i++) {
algosConf[i] = &configureAlgos.getAlgoAt(SasType, i);
}
// Build list of offered known algos in Hello,
for (numAlgosOffered = 0, i = 0; i < num; i++) {
- algosOffered[numAlgosOffered] = &zrtpSasTypes.getByName((const char*)hello->getSasType(i++));
+ algosOffered[numAlgosOffered] = &zrtpSasTypes.getByName((const char*)hello->getSasType(i));
if (!algosOffered[numAlgosOffered]->isValid())
continue;
numAlgosOffered++;
@@ -1515,27 +1606,109 @@
return &zrtpAuthLengths.getByName(mandatoryAuthLen_1);
}
-AlgorithmEnum* ZRtp::getStrongHashOffered(ZrtpPacketHello *hello) {
+// The following set of functions implement a 'non-NIST first policy' if nonNist computes
+// to true. They prefer nonNist algorithms if these are available. Otherwise they use the NIST
+// counterpart or simply call the according findBest*(...) function.
+//
+// Only the findBestPubkey(...) function calls them after it selected the public key algorithm.
+// If the public key algorithm is non-NIST and if the policy is set to PreferNonNist then
+// nonNist becomes true.
+//
+// The functions work according to the RFC6189 spec: the initiator can select every algorithm
+// that both parties support. Thus the Initiator can even select an algorithm the wasn't offered
+// in its own Hello packet but that the Initiator found in the peer's Hello and that is available
+// for it.
+//
+AlgorithmEnum* ZRtp::getStrongHashOffered(ZrtpPacketHello *hello, int32_t algoName) {
int numHash = hello->getNumHashes();
+ bool nonNist = (algoName == *(int32_t*)e414 || algoName == *(int32_t*)e255) && configureAlgos.getSelectionPolicy() == ZrtpConfigure::PreferNonNist;
+
+ if (nonNist) {
+ for (int i = 0; i < numHash; i++) {
+ int32_t nm = *(int32_t*)(hello->getHashType(i));
+ if (nm == *(int32_t*)skn3) {
+ return &zrtpHashes.getByName((const char*)hello->getHashType(i));
+ }
+ }
+ }
for (int i = 0; i < numHash; i++) {
- if (*(int32_t*)(hello->getHashType(i)) == *(int32_t*)s384) {
+ int32_t nm = *(int32_t*)(hello->getHashType(i));
+ if (nm == *(int32_t*)s384 || nm == *(int32_t*)skn3) {
return &zrtpHashes.getByName((const char*)hello->getHashType(i));
}
}
- return NULL;
+ return NULL; // returning NULL -> prepareCommit(...) terminates ZRTP, missing strong hash is an error
}
-AlgorithmEnum* ZRtp::getStrongCipherOffered(ZrtpPacketHello *hello) {
+AlgorithmEnum* ZRtp::getStrongCipherOffered(ZrtpPacketHello *hello, int32_t algoName) {
int num = hello->getNumCiphers();
+ bool nonNist = (algoName == *(int32_t*)e414 || algoName == *(int32_t*)e255) && configureAlgos.getSelectionPolicy() == ZrtpConfigure::PreferNonNist;
+
+ if (nonNist) {
+ for (int i = 0; i < num; i++) {
+ int32_t nm = *(int32_t*)(hello->getCipherType(i));
+ if (nm == *(int32_t*)two3) {
+ return &zrtpSymCiphers.getByName((const char*)hello->getCipherType(i));
+ }
+ }
+ }
for (int i = 0; i < num; i++) {
- if (*(int32_t*)(hello->getCipherType(i)) == *(int32_t*)aes3 ||
- *(int32_t*)(hello->getCipherType(i)) == *(int32_t*)two3) {
+ int32_t nm = *(int32_t*)(hello->getCipherType(i));
+ if (nm == *(int32_t*)aes3 || nm == *(int32_t*)two3) {
return &zrtpSymCiphers.getByName((const char*)hello->getCipherType(i));
}
}
- return NULL;
+ return NULL; // returning NULL -> prepareCommit(...) finds the best cipher
+}
+
+AlgorithmEnum* ZRtp::getHashOffered(ZrtpPacketHello *hello, int32_t algoName) {
+
+ int num = hello->getNumHashes();
+ bool nonNist = (algoName == *(int32_t*)e414 || algoName == *(int32_t*)e255) && configureAlgos.getSelectionPolicy() == ZrtpConfigure::PreferNonNist;
+
+ if (nonNist) {
+ for (int i = 0; i < num; i++) {
+ int32_t nm = *(int32_t*)(hello->getHashType(i));
+ if (nm == *(int32_t*)skn2 || nm == *(int32_t*)skn3) {
+ return &zrtpHashes.getByName((const char*)hello->getHashType(i));
+ }
+ }
+ }
+ return findBestHash(hello);
+}
+
+AlgorithmEnum* ZRtp::getCipherOffered(ZrtpPacketHello *hello, int32_t algoName) {
+
+ int num = hello->getNumCiphers();
+ bool nonNist = (algoName == *(int32_t*)e414 || algoName == *(int32_t*)e255) && configureAlgos.getSelectionPolicy() == ZrtpConfigure::PreferNonNist;
+
+ if (nonNist) {
+ for (int i = 0; i < num; i++) {
+ int32_t nm = *(int32_t*)(hello->getCipherType(i));
+ if (nm == *(int32_t*)two2 || nm == *(int32_t*)two3) {
+ return &zrtpSymCiphers.getByName((const char*)hello->getCipherType(i));
+ }
+ }
+ }
+ return NULL; // returning NULL -> prepareCommit(...) finds the best cipher
+}
+
+AlgorithmEnum* ZRtp::getAuthLenOffered(ZrtpPacketHello *hello, int32_t algoName) {
+
+ int num = hello->getNumAuth();
+ bool nonNist = (algoName == *(int32_t*)e414 || algoName == *(int32_t*)e255) && configureAlgos.getSelectionPolicy() == ZrtpConfigure::PreferNonNist;
+
+ if (nonNist) {
+ for (int i = 0; i < num; i++) {
+ int32_t nm = *(int32_t*)(hello->getAuthLen(i));
+ if (nm == *(int32_t*)sk32 || nm == *(int32_t*)sk64) {
+ return &zrtpAuthLengths.getByName((const char*)hello->getAuthLen(i));
+ }
+ }
+ }
+ return findBestAuthLen(hello);
}
bool ZRtp::checkMultiStream(ZrtpPacketHello *hello) {
@@ -1558,6 +1731,10 @@
bool ZRtp::verifyH2(ZrtpPacketCommit *commit) {
uint8_t tmpH3[IMPL_MAX_DIGEST_LENGTH];
+ // packet does not have the correct size, treat H2 verfication as failed.
+ if (!commit->isLengthOk(multiStream ? ZrtpPacketCommit::MultiStream : ZrtpPacketCommit::DhExchange))
+ return false;
+
sha256(commit->getH2(), HASH_IMAGE_SIZE, tmpH3);
if (memcmp(tmpH3, peerH3, HASH_IMAGE_SIZE) != 0) {
return false;
@@ -1593,6 +1770,7 @@
uint8_t randBuf[RS_LENGTH];
uint32_t macLen;
+ fprintf(stderr, "Compute shared secrets\n");
detailInfo.secretsCached = 0;
if (!zidRec->isRs1Valid()) {
randomZRTP(randBuf, RS_LENGTH);
@@ -1618,15 +1796,6 @@
detailInfo.secretsCached |= Rs2;
}
- /*
- * For the time being we don't support this type 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);
@@ -1638,6 +1807,28 @@
hmacFunction((unsigned char*)zidRec->getMiTMData(), RS_LENGTH, (unsigned char*)responder, strlen(responder), pbxSecretIDr, &macLen);
detailInfo.secretsCached |= Pbx;
}
+ computeAuxSecretIds();
+}
+
+void ZRtp::computeAuxSecretIds() {
+ uint8_t randBuf[RS_LENGTH];
+ uint32_t macLen;
+
+ if (auxSecret == NULL) {
+ randomZRTP(randBuf, RS_LENGTH);
+ hmacFunction(randBuf, RS_LENGTH, H3, HASH_IMAGE_SIZE, auxSecretIDi, &macLen);
+ hmacFunction(randBuf, RS_LENGTH, H3, HASH_IMAGE_SIZE, auxSecretIDr, &macLen);
+ }
+ else {
+ if (myRole == Initiator) { // I'm initiator thus use my H3 for initiator's IDi, peerH3 for respnder's IDr
+ hmacFunction(auxSecret, auxSecretLength, H3, HASH_IMAGE_SIZE, auxSecretIDi, &macLen);
+ hmacFunction(auxSecret, auxSecretLength, peerH3, HASH_IMAGE_SIZE, auxSecretIDr, &macLen);
+ }
+ else {
+ hmacFunction(auxSecret, auxSecretLength, peerH3, HASH_IMAGE_SIZE, auxSecretIDi, &macLen);
+ hmacFunction(auxSecret, auxSecretLength, H3, HASH_IMAGE_SIZE, auxSecretIDr, &macLen);
+ }
+ }
}
/*
@@ -1684,12 +1875,17 @@
rsFound = 0x8;
detailInfo.secretsMatched = Rs2;
}
- /* *** Not yet supported
+
if (memcmp(auxSecretIDr, dhPart->getAuxSecretId(), 8) == 0) {
- DEBUGOUT((fprintf(stdout, "%c: Match for aux secret found\n", zid[0])));
+ DEBUGOUT((fprintf(stdout, "Initiator: Match for aux secret found\n")));
setD[1] = auxSecret;
+ detailInfo.secretsMatched |= Aux;
+ detailInfo.secretsMatchedDH |= Aux;
}
- */
+ if (auxSecret != NULL && (detailInfo.secretsMatched & Aux) == 0) {
+ sendInfo(Warning, WarningNoExpectedAuxMatch);
+ }
+
// check if we have a matching PBX secret and place it third (s3)
if (memcmp(pbxSecretIDr, dhPart->getPbxSecretId(), HMAC_SIZE) == 0) {
DEBUGOUT((fprintf(stdout, "%c: Match for Other_secret found\n", zid[0])));
@@ -1704,6 +1900,7 @@
if (rs1Valid || rs2Valid) { // but valid RS records in cache
sendInfo(Warning, WarningNoExpectedRSMatch);
zidRec->resetSasVerified();
+ saveZidRecord = false; // Don't save RS until user verfied/confirmed SAS
}
else { // No valid RS record in cache
sendInfo(Warning, WarningNoRSMatch);
@@ -1780,7 +1977,7 @@
data[pos] = (unsigned char*)&sLen[i];
length[pos++] = sizeof(uint32_t);
data[pos] = (unsigned char*)setD[i];
- length[pos++] = RS_LENGTH;
+ length[pos++] = (i != 1) ? RS_LENGTH : auxSecretLength;
}
else { // no machting secret, set length 0, skip secret
sLen[i] = 0;
@@ -1842,12 +2039,17 @@
rsFound |= 0x8;
detailInfo.secretsMatched = Rs2;
}
- /* ***** not yet supported
- if (memcmp(auxSecretIDi, dhPart->getauxSecretId(), 8) == 0) {
- DEBUGOUT((fprintf(stdout, "%c: Match for aux secret found\n", ownZidzid[0])));
- setD[1] = ;
+
+ if (memcmp(auxSecretIDi, dhPart->getAuxSecretId(), 8) == 0) {
+ DEBUGOUT((fprintf(stdout, "Responder: Match for aux secret found\n")));
+ setD[1] = auxSecret;
+ detailInfo.secretsMatched |= Aux;
+ detailInfo.secretsMatchedDH |= Aux;
}
- */
+ // If we have an auxSecret but no match from peer - report this.
+ if (auxSecret != NULL && (detailInfo.secretsMatched & Aux) == 0) {
+ sendInfo(Warning, WarningNoExpectedAuxMatch);
+ }
if (memcmp(pbxSecretIDi, dhPart->getPbxSecretId(), 8) == 0) {
DEBUGOUT((fprintf(stdout, "%c: Match for PBX secret found\n", ownZid[0])));
@@ -1861,6 +2063,7 @@
if (rs1Valid || rs2Valid) { // but valid RS records in cache
sendInfo(Warning, WarningNoExpectedRSMatch);
zidRec->resetSasVerified();
+ saveZidRecord = false; // Don't save RS until user verfied/confirmed SAS
}
else { // No valid RS record in cache
sendInfo(Warning, WarningNoRSMatch);
@@ -1939,7 +2142,7 @@
data[pos] = (unsigned char*)&sLen[i];
length[pos++] = sizeof(uint32_t);
data[pos] = (unsigned char*)setD[i];
- length[pos++] = RS_LENGTH;
+ length[pos++] = (i != 1) ? RS_LENGTH : auxSecretLength;
}
else { // no machting secret, set length 0, skip secret
sLen[i] = 0;
@@ -2189,6 +2392,34 @@
hashCtxFunction = sha384Ctx;
hashCtxListFunction = sha384Ctx;
break;
+
+ case 2:
+ hashLength = SKEIN256_DIGEST_LENGTH;
+ hashFunction = skein256;
+ hashListFunction = skein256;
+
+ hmacFunction = macSkein256;
+ hmacListFunction = macSkein256;
+
+ createHashCtx = createSkein256Context;
+ closeHashCtx = closeSkein256Context;
+ hashCtxFunction = skein256Ctx;
+ hashCtxListFunction = skein256Ctx;
+ break;
+
+ case 3:
+ hashLength = SKEIN384_DIGEST_LENGTH;
+ hashFunction = skein384;
+ hashListFunction = skein384;
+
+ hmacFunction = macSkein384;
+ hmacListFunction = macSkein384;
+
+ createHashCtx = createSkein384Context;
+ closeHashCtx = closeSkein384Context;
+ hashCtxFunction = skein384Ctx;
+ hashCtxListFunction = skein384Ctx;
+ break;
}
}
@@ -2202,6 +2433,7 @@
return;
zidRec->setSasVerified();
+ saveZidRecord = true;
getZidCacheInstance()->saveRecord(zidRec);
}
@@ -2211,6 +2443,14 @@
getZidCacheInstance()->saveRecord(zidRec);
}
+void ZRtp::setRs2Valid() {
+
+ if (zidRec != NULL) {
+ zidRec->setRs2Valid();
+ if (saveZidRecord)
+ getZidCacheInstance()->saveRecord(zidRec);
+ }
+}
void ZRtp::sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) {
@@ -2262,33 +2502,32 @@
}
}
-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());
- }
+void ZRtp::setClientId(std::string id, HelloPacketVersion* hpv) {
- int32_t len = zrtpHello.getLength() * ZRTP_WORD_SIZE;
+ unsigned char tmp[CLIENT_ID_SIZE +1] = {' '};
+ memcpy(tmp, id.c_str(), id.size() > CLIENT_ID_SIZE ? CLIENT_ID_SIZE : id.size());
+ tmp[CLIENT_ID_SIZE] = 0;
- // Hello packet is ready now, compute its HMAC
+ hpv->packet->setClientId(tmp);
+
+ int32_t len = hpv->packet->getLength() * ZRTP_WORD_SIZE;
+
+ // Hello packets are 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);
+ hmacFunctionImpl(H2, HASH_IMAGE_SIZE, (uint8_t*)hpv->packet->getHeaderBase(), len-(2*ZRTP_WORD_SIZE), hmac, &macLen);
+ hpv->packet->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);
+ hashFunctionImpl((uint8_t*)hpv->packet->getHeaderBase(), len, hpv->helloHash);
}
void ZRtp::storeMsgTemp(ZrtpPacketBase* pkt) {
- int32_t length = pkt->getLength() * ZRTP_WORD_SIZE;
+ 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;
@@ -2304,12 +2543,18 @@
return (memcmp(hmac, tempMsgBuffer+len, (HMAC_SIZE)) == 0 ? true : false);
}
-std::string ZRtp::getHelloHash() {
+std::string ZRtp::getHelloHash(int32_t index) {
std::ostringstream stm;
- uint8_t* hp = helloHash;
+ if (index < 0 || index >= MAX_ZRTP_VERSIONS)
+ return std::string();
- stm << zrtpVersion;
+ uint8_t* hp = helloPackets[index].helloHash;
+
+ char version[5] = {'\0'};
+ strncpy(version, (const char*)helloPackets[index].packet->getVersion(), ZRTP_WORD_SIZE);
+
+ stm << version;
stm << " ";
stm.fill('0');
stm << hex;
@@ -2426,7 +2671,8 @@
Event_t ev;
ev.type = ZrtpPacket;
- ev.packet = (uint8_t*)&zrtpConf2Ack;
+ ev.packet = (uint8_t*)zrtpConf2Ack.getHeaderBase();
+ ev.length = sizeof (Conf2AckPacket_t) + 12; // 12 is fixed ZRTP (RTP) header size
if (stateEngine != NULL) {
stateEngine->processEvent(&ev);