blob: 85887440ed46c70a99d7dddd2cbba51efe297b36 [file] [log] [blame]
/*
* Tivi client glue code for ZRTP.
* Copyright (c) 2012 Slient Circle LLC. All rights reserved.
*
*
* @author Werner Dittmann <Werner.Dittmann@t-online.de>
*/
#include <string>
#include <stdio.h>
#include <libzrtpcpp/ZIDCache.h>
#include <libzrtpcpp/ZRtp.h>
#include <CtZrtpStream.h>
#include <CtZrtpCallback.h>
#include <CtZrtpSession.h>
#include <common/Thread.h>
static CMutexClass sessionLock;
const char *getZrtpBuildInfo()
{
return zrtpBuildInfo;
}
CtZrtpSession::CtZrtpSession() : mitmMode(false), signSas(false), enableParanoidMode(false), isReady(false),
zrtpEnabled(true), sdesEnabled(true) {
clientIdString = clientId;
streams[AudioStream] = NULL;
streams[VideoStream] = NULL;
}
int CtZrtpSession::initCache(const char *zidFilename) {
ZIDCache* zf = getZidCacheInstance();
if (!zf->isOpen()) {
std::string fname;
if (zidFilename == NULL) {
char *home = getenv("HOME");
std::string baseDir = (home != NULL) ? (std::string(home) + std::string("/."))
: std::string(".");
fname = baseDir + std::string("GNUZRTP.zid");
zidFilename = fname.c_str();
}
if (zf->open((char *)zidFilename) < 0) {
return -1;
}
}
return 1;
}
int CtZrtpSession::init(bool audio, bool video, ZrtpConfigure* config)
{
int32_t ret = 1;
synchEnter();
ZrtpConfigure* configOwn = NULL;
if (config == NULL) {
config = configOwn = new ZrtpConfigure();
setupConfiguration(config);
config->setTrustedMitM(true);
}
config->setParanoidMode(enableParanoidMode);
ZIDCache* zf = getZidCacheInstance();
if (!zf->isOpen()) {
ret = -1;
}
if (ret > 0) {
const uint8_t* ownZid = zf->getZid();
CtZrtpStream *stream;
// Create CTZrtpStream object only once, they are availbe for the whole
// lifetime of the session.
if (audio) {
if (streams[AudioStream] == NULL)
streams[AudioStream] = new CtZrtpStream();
stream = streams[AudioStream];
stream->zrtpEngine = new ZRtp((uint8_t*)ownZid, stream, clientIdString, config, mitmMode, signSas);
stream->type = Master;
stream->index = AudioStream;
stream->session = this;
}
if (video) {
if (streams[VideoStream] == NULL)
streams[VideoStream] = new CtZrtpStream();
stream = streams[VideoStream];
stream->zrtpEngine = new ZRtp((uint8_t*)ownZid, stream, clientIdString, config);
stream->type = Slave;
stream->index = VideoStream;
stream->session = this;
}
}
if (configOwn != NULL) {
delete configOwn;
}
synchLeave();
isReady = true;
return ret;
}
CtZrtpSession::~CtZrtpSession() {
delete streams[AudioStream];
delete streams[VideoStream];
}
void zrtp_log(const char *tag, const char *buf);
void CtZrtpSession::setupConfiguration(ZrtpConfigure *conf) {
// Set _WITHOUT_TIVI_ENV to a real name that is TRUE if the Tivi client is compiled/built.
#ifdef _WITHOUT_TIVI_ENV
#define GET_CFG_I(RET,_KEY)
#else
void *findGlobalCfgKey(char *key, int iKeyLen, int &iSize, char **opt, int *type);
#define GET_CFG_I(RET,_KEY) {int *p=(int*)findGlobalCfgKey((char*)_KEY,sizeof(_KEY)-1,iSZ,&opt,&type);if(p && iSZ==4)RET=*p;else RET=-1;}
#endif
// The next three vars are used in case of a real Tivi compile, see macro above.
int iSZ;
char *opt;
int type;
void zrtp_log( const char *tag, const char *buf);
int b32sas = 0, iDisableDH2K = 0, iDisableAES256 = 0, iPreferDH2K = 0;
int iDisableECDH256 = 0, iDisableECDH384 = 0, iEnableSHA384 = 1;
int iDisableSkein = 0, iDisableTwofish = 0, iPreferNIST = 0;
int iDisableSkeinHash = 0, iDisableBernsteinCurve25519 = 0, iDisableBernsteinCurve3617 = 0;
GET_CFG_I(b32sas, "iDisable256SAS");
GET_CFG_I(iDisableAES256, "iDisableAES256");
GET_CFG_I(iDisableDH2K, "iDisableDH2K");
GET_CFG_I(iPreferDH2K, "iPreferDH2K");
GET_CFG_I(iDisableECDH256, "iDisableECDH256");
GET_CFG_I(iDisableECDH384, "iDisableECDH384");
GET_CFG_I(iEnableSHA384, "iEnableSHA384");
GET_CFG_I(iDisableSkein, "iDisableSkein");
GET_CFG_I(iDisableTwofish, "iDisableTwofish");
GET_CFG_I(iPreferNIST, "iPreferNIST");
GET_CFG_I(iDisableSkeinHash, "iDisableSkeinHash");
GET_CFG_I(iDisableBernsteinCurve25519, "iDisableBernsteinCurve25519");
GET_CFG_I(iDisableBernsteinCurve3617, "iDisableBernsteinCurve3617");
conf->clear();
/*
* Setting the selection policy is a more generic policy than the iPreferNIST
* configuration set by the user. The selection policy is a decision of the
* client, not the user
*/
conf->setSelectionPolicy(ZrtpConfigure::PreferNonNist);
/*
* Handling of iPreferNIST: if this is false (== 0) then we add the non-NIST algorithms
* to the configuration and place them in front of the NIST algorithms. Refer to RFC6189
* section 4.1.2 regarding selection of the public key algorithm.
*
* With the configuration flags we can enable/disable each ECC PK algorithm separately.
*
*/
if (iPreferNIST == 0) {
if (iDisableBernsteinCurve3617 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("E414"));
if (iDisableECDH384 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("EC38"));
}
else {
if (iDisableECDH384 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("EC38"));
if (iDisableBernsteinCurve3617 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("E414"));
}
if (iPreferNIST == 0) {
if (iDisableBernsteinCurve25519 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("E255"));
if (iDisableECDH256 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("EC25"));
}
else {
if (iDisableECDH256 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("EC25"));
if (iDisableBernsteinCurve25519 == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("E255"));
}
// DH2K handling: if DH2K not disabled and prefered put it infrom of DH3K,
// If not preferred and not disabled put if after DH3K. Don't use DH2K if
// it's not enabled at all (iDisableDH2K == 1)
if (iPreferDH2K && iDisableDH2K == 0) {
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("DH2k"));
}
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("DH3k"));
if (iPreferDH2K == 0 && iDisableDH2K == 0)
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("DH2k"));
conf->addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName("Mult"));
// Handling of Hash algorithms: similar to PK, if PreferNIST is false
// then put Skein in fromt oF SHA. Regardless if the Hash is enabled or
// not: if configuration enables a large curve then also use the large
// hashes.
if (iPreferNIST == 0) {
if (iDisableSkeinHash == 0 || iDisableBernsteinCurve3617 == 0)
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("SKN3"));
if (iEnableSHA384 == 1 || iDisableECDH384 == 0)
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("S384"));
}
else {
if (iEnableSHA384 == 1 || iDisableECDH384 == 0)
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("S384"));
if (iDisableSkeinHash == 0 || iDisableBernsteinCurve3617 == 0)
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("SKN3"));
}
if (iPreferNIST == 0) {
if (iDisableSkeinHash == 0)
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("SKN2"));
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("S256"));
}
else {
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("S256"));
if (iDisableSkeinHash == 0)
conf->addAlgo(HashAlgorithm, zrtpHashes.getByName("SKN2"));
}
// Handling of Symmetric algorithms: always prefer twofish (regardless
// of NIST setting) if it is not disabled. iDisableAES256 means: disable
// large ciphers
if (iDisableAES256 == 0) {
if (iDisableTwofish == 0)
conf->addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName("2FS3"));
conf->addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName("AES3"));
}
if (iDisableTwofish == 0)
conf->addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName("2FS1"));
conf->addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName("AES1"));
if (b32sas == 1) {
conf->addAlgo(SasType, zrtpSasTypes.getByName("B32 "));
}
else {
conf->addAlgo(SasType, zrtpSasTypes.getByName("B256"));
conf->addAlgo(SasType, zrtpSasTypes.getByName("B32 "));
}
if (iPreferNIST == 0) {
if (iDisableSkein == 0) {
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("SK32"));
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("SK64"));
}
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("HS32"));
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("HS80"));
}
else {
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("HS32"));
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("HS80"));
if (iDisableSkein == 0) {
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("SK32"));
conf->addAlgo(AuthLength, zrtpAuthLengths.getByName("SK64"));
}
}
}
void CtZrtpSession::setUserCallback(CtZrtpCb* ucb, streamName streamNm) {
if (!(streamNm >= 0 && streamNm <= AllStreams && streams[streamNm] != NULL))
return;
if (streamNm == AllStreams) {
for (int sn = 0; sn < AllStreams; sn++)
streams[sn]->setUserCallback(ucb);
}
else
streams[streamNm]->setUserCallback(ucb);
}
void CtZrtpSession::setSendCallback(CtZrtpSendCb* scb, streamName streamNm) {
if (!(streamNm >= 0 && streamNm <= AllStreams && streams[streamNm] != NULL))
return;
if (streamNm == AllStreams) {
for (int sn = 0; sn < AllStreams; sn++)
streams[sn]->setSendCallback(scb);
}
else
streams[streamNm]->setSendCallback(scb);
}
void CtZrtpSession::masterStreamSecure(CtZrtpStream *masterStream) {
// Here we know that the AudioStream is the master and VideoStream the slave.
// Otherwise we need to loop and find the Master stream and the Slave streams.
multiStreamParameter = masterStream->zrtpEngine->getMultiStrParams();
CtZrtpStream *strm = streams[VideoStream];
if (strm->enableZrtp) {
strm->zrtpEngine->setMultiStrParams(multiStreamParameter);
strm->zrtpEngine->startZrtpEngine();
strm->started = true;
strm->tiviState = eLookingPeer;
if (strm->zrtpUserCallback != 0)
strm->zrtpUserCallback->onNewZrtpStatus(this, NULL, strm->index);
}
}
int CtZrtpSession::startIfNotStarted(unsigned int uiSSRC, int streamNm) {
if (!(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return 0;
if ((streamNm == VideoStream && !isSecure(AudioStream)) || streams[streamNm]->started)
return 0;
start(uiSSRC, streamNm == VideoStream ? CtZrtpSession::VideoStream : CtZrtpSession::AudioStream);
return 0;
}
void CtZrtpSession::start(unsigned int uiSSRC, CtZrtpSession::streamName streamNm) {
if (!zrtpEnabled || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return;
CtZrtpStream *stream = streams[streamNm];
stream->ownSSRC = uiSSRC;
stream->enableZrtp = true;
if (stream->type == Master) {
stream->zrtpEngine->startZrtpEngine();
stream->started = true;
stream->tiviState = eLookingPeer;
if (stream->zrtpUserCallback != 0)
stream->zrtpUserCallback->onNewZrtpStatus(this, NULL, stream->index);
return;
}
// Process a Slave stream.
if (!multiStreamParameter.empty()) { // Multi-stream parameters available
stream->zrtpEngine->setMultiStrParams(multiStreamParameter);
stream->zrtpEngine->startZrtpEngine();
stream->started = true;
stream->tiviState = eLookingPeer;
if (stream->zrtpUserCallback != 0)
stream->zrtpUserCallback->onNewZrtpStatus(this, NULL, stream->index);
}
}
void CtZrtpSession::stop(streamName streamNm) {
if (!(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return;
streams[streamNm]->isStopped = true;
}
void CtZrtpSession::release() {
release(AudioStream);
release(VideoStream);
}
void CtZrtpSession::release(streamName streamNm) {
if (!(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return;
CtZrtpStream *stream = streams[streamNm];
stream->stopStream(); // stop and reset stream
}
void CtZrtpSession::setLastPeerNameVerify(const char *name, int iIsMitm) {
CtZrtpStream *stream = streams[AudioStream];
if (!isReady || !stream || stream->isStopped)
return;
uint8_t peerZid[IDENTIFIER_LEN];
std::string nm(name);
stream->zrtpEngine->getPeerZid(peerZid);
getZidCacheInstance()->putPeerName(peerZid, nm);
setVerify(1);
}
int CtZrtpSession::isSecure(streamName streamNm) {
if (!(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return 0;
CtZrtpStream *stream = streams[streamNm];
return stream->isSecure();
}
bool CtZrtpSession::processOutoingRtp(uint8_t *buffer, size_t length, size_t *newLength, streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return false;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return false;
return stream->processOutgoingRtp(buffer, length, newLength);
}
int32_t CtZrtpSession::processIncomingRtp(uint8_t *buffer, size_t length, size_t *newLength, streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return fail;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return fail;
return stream->processIncomingRtp(buffer, length, newLength);
}
bool CtZrtpSession::isStarted(streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return false;
return streams[streamNm]->isStarted();
}
bool CtZrtpSession::isEnabled(streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return false;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return false;
return stream->isEnabled();
}
CtZrtpSession::tiviStatus CtZrtpSession::getCurrentState(streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return eWrongStream;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return eWrongStream;
return stream->getCurrentState();
}
CtZrtpSession::tiviStatus CtZrtpSession::getPreviousState(streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return eWrongStream;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return eWrongStream;
return stream->getPreviousState();
}
bool CtZrtpSession::isZrtpEnabled() {
return zrtpEnabled;
}
bool CtZrtpSession::isSdesEnabled() {
return sdesEnabled;
}
void CtZrtpSession::setZrtpEnabled(bool yesNo) {
zrtpEnabled = yesNo;
}
void CtZrtpSession::setSdesEnabled(bool yesNo) {
sdesEnabled = yesNo;
}
int CtZrtpSession::getSignalingHelloHash(char *helloHash, streamName streamNm, int32_t index) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return 0;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return 0;
return stream->getSignalingHelloHash(helloHash, index);
}
void CtZrtpSession::setSignalingHelloHash(const char *helloHash, streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return;
stream->setSignalingHelloHash(helloHash);
}
void CtZrtpSession::setVerify(int iVerified) {
CtZrtpStream *stream = streams[AudioStream];
if (!isReady || !stream || stream->isStopped)
return;
if (iVerified) {
stream->zrtpEngine->SASVerified();
stream->sasVerified = true;
}
else {
stream->zrtpEngine->resetSASVerified();
stream->sasVerified = false;
}
}
int CtZrtpSession::getInfo(const char *key, uint8_t *buffer, size_t maxLen, streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return fail;
CtZrtpStream *stream = streams[streamNm];
return stream->getInfo(key, (char*)buffer, (int)maxLen);
}
int CtZrtpSession::enrollAccepted(char *p) {
if (!isReady || !(streams[AudioStream] != NULL))
return fail;
CtZrtpStream *stream = streams[AudioStream];
int ret = stream->enrollAccepted(p);
setVerify(true);
return ret;
}
int CtZrtpSession::enrollDenied() {
if (!isReady || !(streams[AudioStream] != NULL))
return fail;
CtZrtpStream *stream = streams[AudioStream];
int ret = stream->enrollDenied();
setVerify(true); // TODO : Janis -> is that correct in this case?
return ret;
}
void CtZrtpSession::setClientId(std::string id) {
clientIdString = id;
}
bool CtZrtpSession::createSdes(char *cryptoString, size_t *maxLen, streamName streamNm, const sdesSuites suite) {
if (!isReady || !sdesEnabled || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return fail;
CtZrtpStream *stream = streams[streamNm];
return stream->createSdes(cryptoString, maxLen, static_cast<ZrtpSdesStream::sdesSuites>(suite));
}
bool CtZrtpSession::parseSdes(char *recvCryptoStr, size_t recvLength, char *sendCryptoStr,
size_t *sendLength, bool sipInvite, streamName streamNm) {
if (!isReady || !sdesEnabled || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return fail;
CtZrtpStream *stream = streams[streamNm];
return stream->parseSdes(recvCryptoStr, recvLength, sendCryptoStr, sendLength, sipInvite);
}
bool CtZrtpSession::getSavedSdes(char *sendCryptoStr, size_t *sendLength, streamName streamNm) {
if (!isReady || !sdesEnabled || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return fail;
CtZrtpStream *stream = streams[streamNm];
return stream->getSavedSdes(sendCryptoStr, sendLength);
}
bool CtZrtpSession::isSdesActive(streamName streamNm) {
if (!isReady || !sdesEnabled || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return fail;
CtZrtpStream *stream = streams[streamNm];
return stream->isSdesActive();
}
int CtZrtpSession::getCryptoMixAttribute(char *algoNames, size_t length, streamName streamNm) {
if (!isReady || !sdesEnabled || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return 0;
CtZrtpStream *stream = streams[streamNm];
return stream->getCryptoMixAttribute(algoNames, length);
}
bool CtZrtpSession::setCryptoMixAttribute(const char *algoNames, streamName streamNm) {
if (!isReady || !sdesEnabled || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return fail;
CtZrtpStream *stream = streams[streamNm];
return stream->setCryptoMixAttribute(algoNames);
}
void CtZrtpSession::resetSdesContext(streamName streamNm, bool force) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return;
CtZrtpStream *stream = streams[streamNm];
stream->resetSdesContext(force);
}
int32_t CtZrtpSession::getNumberSupportedVersions(streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return 0;
CtZrtpStream *stream = streams[streamNm];
return stream->getNumberSupportedVersions();
}
const char* CtZrtpSession::getZrtpEncapAttribute(streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return NULL;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return NULL;
return stream->getZrtpEncapAttribute();
}
void CtZrtpSession::setZrtpEncapAttribute(const char *attribute, streamName streamNm) {
if (!isReady || !(streamNm >= 0 && streamNm < AllStreams && streams[streamNm] != NULL))
return;
CtZrtpStream *stream = streams[streamNm];
if (stream->isStopped)
return;
stream->setZrtpEncapAttribute(attribute);
}
void CtZrtpSession::setAuxSecret(const unsigned char *secret, int length) {
if (!isReady || !(streams[AudioStream] != NULL))
return;
CtZrtpStream *stream = streams[AudioStream];
if (stream->isStopped)
return;
stream->setAuxSecret(secret, length);
}
void CtZrtpSession::cleanCache() {
getZidCacheInstance()->cleanup();
}
void CtZrtpSession::synchEnter() {
sessionLock.Lock();
}
void CtZrtpSession::synchLeave() {
sessionLock.Unlock();
}