blob: 39e20a796788e3c73a205771ad729a2dcd686568 [file] [log] [blame]
// Test ZRTP extension for ccRTP
//
// Copyright (C) 2008 Werner Dittmann <Werner.Dittmann@t-online.de>
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <cstdlib>
#include <map>
#include <libzrtpcpp/zrtpccrtp.h>
#include <libzrtpcpp/ZrtpUserCallback.h>
#include <libzrtpcpp/ZrtpConfigure.h>
using namespace ost;
using namespace std;
using namespace GnuZrtpCodes;
/* maybe should be by special 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");
}
*/
class PacketsPattern
{
public:
inline const InetHostAddress&
getDestinationAddress() const
{
return destinationAddress;
}
inline const tpport_t
getDestinationPort() const
{
return destinationPort;
}
uint32
getPacketsNumber() const
{
return packetsNumber;
}
uint32
getSsrc() const
{
return 0xdeadbeef;
}
const unsigned char*
getPacketData(uint32 i)
{
return data[i%2];
}
const size_t
getPacketSize(uint32 i)
{
return strlen((char*)data[i%2]) + 1 ;
}
private:
static const InetHostAddress destinationAddress;
static const uint16 destinationPort = 5002;
static const uint32 packetsNumber = 10;
static const uint32 packetsSize = 12;
static const unsigned char* data[];
};
const InetHostAddress PacketsPattern::destinationAddress =
InetHostAddress("localhost");
const unsigned char* PacketsPattern::data[] = {
(unsigned char*)"0123456789\n",
(unsigned char*)"987654321\n"
};
PacketsPattern pattern;
class ZrtpRecvPacketTransmissionTestCB;
class ZrtpSendPacketTransmissionTestCB;
class MyUserCallback;
class MyUserCallbackMulti;
static ZrtpRecvPacketTransmissionTestCB* zrxcb = NULL;
static ZrtpSendPacketTransmissionTestCB* ztxcb = NULL;
static ZrtpRecvPacketTransmissionTestCB* zrxcbMulti = NULL;
static ZrtpSendPacketTransmissionTestCB* ztxcbMulti = NULL;
static bool enroll = false;
static bool mitm = false;
static bool untrusted = false;
static bool sender = false;
static bool recver = false;
static bool signsas = false;
/**
* SymmetricZRTPSession in security mode and using a callback class.
*
* The next two classes show how to use <code>SymmetricZRTPSession</code>
* using the standard ZRTP handshake an switching to encrypted (SRTP) mode.
* The application enables this by calling <code>initialize(...)</code>.
* In addition the application sets a callback class (see above). ZRTP calls
* the methods of the callback class and the application may implement
* appropriate methods to deal with these triggers.
*/
class
ZrtpSendPacketTransmissionTestCB : public Thread, public TimerPort {
private:
SymmetricZRTPSession* tx;
string multiParams;
string prefix;
public:
ZrtpSendPacketTransmissionTestCB(): tx(NULL), multiParams("") {};
void run() {
doTest();
}
int doTest();
string getMultiStrParams() {
return tx->getMultiStrParams();
}
void setMultiStrParams(string params) {
multiParams = params;
return;
}
};
class
ZrtpRecvPacketTransmissionTestCB: public Thread {
private:
SymmetricZRTPSession* rx;
string multiParams;
string prefix;
public:
ZrtpRecvPacketTransmissionTestCB(): rx(NULL), multiParams("") {};
void run() {
doTest();
}
int doTest();
string getMultiStrParams() {
return rx->getMultiStrParams();
}
void setMultiStrParams(string params) {
multiParams = params;
return;
}
};
/**
* Simple User Callback class
*
* This class overwrite some methods from ZrtpUserCallback to get information
* about ZRTP processing and information about ZRTP results. The standard
* implementation of this class just perform return, thus effectively
* supressing any callback or trigger.
*/
class MyUserCallback: public ZrtpUserCallback {
protected:
static map<int32, std::string*> infoMap;
static map<int32, std::string*> warningMap;
static map<int32, std::string*> severeMap;
static map<int32, std::string*> zrtpMap;
static map<int32, std::string*> enrollMap;
static bool initialized;
SymmetricZRTPSession* session;
std::string prefix;
public:
MyUserCallback(SymmetricZRTPSession* s): session(s), prefix("default: ") {
if (initialized) {
return;
}
infoMap.insert(pair<int32, std::string*>(InfoHelloReceived, new string("Hello received, preparing a Commit")));
infoMap.insert(pair<int32, std::string*>(InfoCommitDHGenerated, new string("Commit: Generated a public DH key")));
infoMap.insert(pair<int32, std::string*>(InfoRespCommitReceived, new string("Responder: Commit received, preparing DHPart1")));
infoMap.insert(pair<int32, std::string*>(InfoDH1DHGenerated, new string("DH1Part: Generated a public DH key")));
infoMap.insert(pair<int32, std::string*>(InfoInitDH1Received, new string("Initiator: DHPart1 received, preparing DHPart2")));
infoMap.insert(pair<int32, std::string*>(InfoRespDH2Received, new string("Responder: DHPart2 received, preparing Confirm1")));
infoMap.insert(pair<int32, std::string*>(InfoInitConf1Received, new string("Initiator: Confirm1 received, preparing Confirm2")));
infoMap.insert(pair<int32, std::string*>(InfoRespConf2Received, new string("Responder: Confirm2 received, preparing Conf2Ack")));
infoMap.insert(pair<int32, std::string*>(InfoRSMatchFound, new string("At least one retained secrets matches - security OK")));
infoMap.insert(pair<int32, std::string*>(InfoSecureStateOn, new string("Entered secure state")));
infoMap.insert(pair<int32, std::string*>(InfoSecureStateOff, new string("No more security for this session")));
warningMap.insert(pair<int32, std::string*>(WarningDHAESmismatch,
new string("Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096")));
warningMap.insert(pair<int32, std::string*>(WarningGoClearReceived, new string("Received a GoClear message")));
warningMap.insert(pair<int32, std::string*>(WarningDHShort,
new string("Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096")));
warningMap.insert(pair<int32, std::string*>(WarningNoRSMatch, new string("No retained secret matches - verify SAS")));
warningMap.insert(pair<int32, std::string*>(WarningCRCmismatch, new string("Internal ZRTP packet checksum mismatch - packet dropped")));
warningMap.insert(pair<int32, std::string*>(WarningSRTPauthError, new string("Dropping packet because SRTP authentication failed!")));
warningMap.insert(pair<int32, std::string*>(WarningSRTPreplayError, new string("Dropping packet because SRTP replay check failed!")));
warningMap.insert(pair<int32, std::string*>(WarningNoExpectedRSMatch,
new string("Valid retained shared secrets availabe but no matches found - must verify SAS")));
severeMap.insert(pair<int32, std::string*>(SevereHelloHMACFailed, new string("Hash HMAC check of Hello failed!")));
severeMap.insert(pair<int32, std::string*>(SevereCommitHMACFailed, new string("Hash HMAC check of Commit failed!")));
severeMap.insert(pair<int32, std::string*>(SevereDH1HMACFailed, new string("Hash HMAC check of DHPart1 failed!")));
severeMap.insert(pair<int32, std::string*>(SevereDH2HMACFailed, new string("Hash HMAC check of DHPart2 failed!")));
severeMap.insert(pair<int32, std::string*>(SevereCannotSend, new string("Cannot send data - connection or peer down?")));
severeMap.insert(pair<int32, std::string*>(SevereProtocolError, new string("Internal protocol error occured!")));
severeMap.insert(pair<int32, std::string*>(SevereNoTimer, new string("Cannot start a timer - internal resources exhausted?")));
severeMap.insert(pair<int32, std::string*>(SevereTooMuchRetries,
new string("Too much retries during ZRTP negotiation - connection or peer down?")));
zrtpMap.insert(pair<int32, std::string*>(MalformedPacket, new string("Malformed packet (CRC OK, but wrong structure)")));
zrtpMap.insert(pair<int32, std::string*>(CriticalSWError, new string("Critical software error")));
zrtpMap.insert(pair<int32, std::string*>(UnsuppZRTPVersion, new string("Unsupported ZRTP version")));
zrtpMap.insert(pair<int32, std::string*>(HelloCompMismatch, new string("Hello components mismatch")));
zrtpMap.insert(pair<int32, std::string*>(UnsuppHashType, new string("Hash type not supported")));
zrtpMap.insert(pair<int32, std::string*>(UnsuppCiphertype, new string("Cipher type not supported")));
zrtpMap.insert(pair<int32, std::string*>(UnsuppPKExchange, new string("Public key exchange not supported")));
zrtpMap.insert(pair<int32, std::string*>(UnsuppSRTPAuthTag, new string("SRTP auth. tag not supported")));
zrtpMap.insert(pair<int32, std::string*>(UnsuppSASScheme, new string("SAS scheme not supported")));
zrtpMap.insert(pair<int32, std::string*>(NoSharedSecret, new string("No shared secret available, DH mode required")));
zrtpMap.insert(pair<int32, std::string*>(DHErrorWrongPV, new string("DH Error: bad pvi or pvr ( == 1, 0, or p-1)")));
zrtpMap.insert(pair<int32, std::string*>(DHErrorWrongHVI, new string("DH Error: hvi != hashed data")));
zrtpMap.insert(pair<int32, std::string*>(SASuntrustedMiTM, new string("Received relayed SAS from untrusted MiTM")));
zrtpMap.insert(pair<int32, std::string*>(ConfirmHMACWrong, new string("Auth. Error: Bad Confirm pkt HMAC")));
zrtpMap.insert(pair<int32, std::string*>(NonceReused, new string("Nonce reuse")));
zrtpMap.insert(pair<int32, std::string*>(EqualZIDHello, new string("Equal ZIDs in Hello")));
zrtpMap.insert(pair<int32, std::string*>(GoCleatNotAllowed, new string("GoClear packet received, but not allowed")));
enrollMap.insert(pair<int32, std::string*>(EnrollmentRequest, new string("Trusted MitM enrollment requested")));
enrollMap.insert(pair<int32, std::string*>(EnrollmentCanceled, new string("Trusted MitM enrollment canceled by user")));
enrollMap.insert(pair<int32, std::string*>(EnrollmentFailed, new string("Trusted MitM enrollment failed")));
enrollMap.insert(pair<int32, std::string*>(EnrollmentOk, new string("Trusted MitM enrollment OK")));
initialized = true;
}
void showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) {
string* msg;
uint8_t sasHash[32];
if (sev == Info) {
msg = infoMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
// this sets up and starts off the multi-stream test
if (subCode == InfoSecureStateOn) {
if (zrxcbMulti != NULL) {
zrxcbMulti->setMultiStrParams(session->getMultiStrParams());
zrxcbMulti->start();
}
if (ztxcbMulti != NULL) {
ztxcbMulti->setMultiStrParams(session->getMultiStrParams());
ztxcbMulti->start();
}
if (sender) {
if (mitm && !enroll) { // sender now acts as trusted PBX in normal mode, not in enrollement service
std::string render = session->getSasType();
for (int i = 0; i < 32; i++) {
sasHash[i] = 0;
}
if (untrusted) { // treat receiver as non-enrolled receiver
cout << prefix << "send SAS relay to non-enrolled receiver" << endl;
session->sendSASRelayPacket(sasHash, render);
}
else {
sasHash[0] = 0x11;
sasHash[1] = 0x22;
sasHash[2] = 0x33;
sasHash[4] = 0x44;
cout << prefix << "send SAS relay to enrolled receiver" << endl;
session->sendSASRelayPacket(sasHash, render);
}
}
}
}
}
if (sev == Warning) {
msg = warningMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
if (sev == Severe) {
msg = severeMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
if (sev == ZrtpError) {
if (subCode < 0) { // received an error packet from peer
subCode *= -1;
cout << prefix << "Received error packet: ";
}
else {
cout << prefix << "Sent error packet: ";
}
msg = zrtpMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
}
void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) {
string* msg;
if (sev == ZrtpError) {
if (subCode < 0) { // received an error packet from peer
subCode *= -1;
cout << prefix << "Received error packet: ";
}
else {
cout << prefix << "Sent error packet: ";
}
msg = zrtpMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
else {
msg = severeMap[subCode];
cout << prefix << *msg << endl;
}
}
void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) {
string* msg = enrollMap[info];
cout << prefix << *msg << endl;
session->acceptEnrollment(true);
}
void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) {
string* msg = enrollMap[info];
cout << prefix << *msg << endl;
}
void secureOn(std::string cipher) {
cout << prefix << "Using cipher:" << cipher << endl;
cout << prefix << "peer hello hash: " << session->getPeerHelloHash() << endl;
}
void showSAS(std::string sas, bool verified) {
cout << prefix << "SAS is: " << sas << endl;
}
void signSAS(uint8_t* sasHash) {
cout << prefix << "SAS to sign" << endl;
uint8_t sign[12];
sign[0] = sasHash[0];
sign[1] = sasHash[1];
sign[2] = sasHash[2];
sign[3] = sasHash[3];
if (recver) {
sign[4] = 'R';
sign[5] = 'E';
sign[6] = 'C';
sign[7] = 'E';
sign[8] = 'I';
sign[9] = 'V';
sign[10] = 'E';
sign[11] = 'R';
}
else {
sign[4] = 'T';
sign[5] = 'R';
sign[6] = 'A';
sign[7] = 'N';
sign[8] = 'S';
sign[9] = 'M';
sign[10] = 'I';
sign[11] = 'T';
}
cout << prefix << "set signature data result: " << session->setSignatureData(sign, 12) << endl;
}
bool checkSASSignature(uint8_t* sasHash) {
cout << prefix << "check signature" << endl;
const uint8_t* sign = session->getSignatureData();
cout << prefix << "signature: " << sign << endl;
return true;
}
void setPrefix(std::string p) {
prefix = p;
}
};
map<int32, std::string*>MyUserCallback::infoMap;
map<int32, std::string*>MyUserCallback::warningMap;
map<int32, std::string*>MyUserCallback::severeMap;
map<int32, std::string*>MyUserCallback::zrtpMap;
map<int32, std::string*>MyUserCallback::enrollMap;
bool MyUserCallback::initialized = false;
class MyUserCallbackMulti: public MyUserCallback {
public:
MyUserCallbackMulti(SymmetricZRTPSession* s): MyUserCallback(s) {
}
void showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) {
string* msg;
if (sev == Info) {
msg = infoMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
if (sev == Warning) {
msg = warningMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
if (sev == Severe) {
msg = severeMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
if (sev == ZrtpError) {
if (subCode < 0) { // received an error packet from peer
subCode *= -1;
cout << prefix << "Received error packet: ";
}
else {
cout << prefix << "Sent error packet: ";
}
msg = zrtpMap[subCode];
if (msg != NULL) {
cout << prefix << *msg << endl;
}
}
}
};
int ZrtpSendPacketTransmissionTestCB::doTest() {
ZrtpConfigure config;
MyUserCallback* mcb;
if (!multiParams.empty()) {
tx = new SymmetricZRTPSession(pattern.getDestinationAddress(),
pattern.getDestinationPort()+2+10);
// tx->initialize("test_t.zid", true, &config);
tx->initialize("test_t.zid", true);
tx->setMultiStrParams(multiParams);
prefix = "TX Multi: ";
mcb = new MyUserCallbackMulti(tx);
mcb->setPrefix(prefix);
}
else {
tx = new SymmetricZRTPSession(pattern.getDestinationAddress(),
pattern.getDestinationPort()+2);
//config.addHashAlgo(Sha384);
// tx->initialize("test_t.zid", true, &config);
if (mitm) { // Act as trusted MitM - could be enrolled
tx->setMitmMode(true);
}
tx->setSignSas(signsas);
tx->initialize("test_t.zid", true);
if (enroll) // act as PBX enrollement service
tx->setEnrollmentMode(true);
prefix = "TX: ";
mcb = new MyUserCallback(tx);
mcb->setPrefix(prefix);
}
// At this point the Hello hash is available. See ZRTP specification
// chapter 9.1 for further information when an how to use the Hello
// hash.
cout << prefix << "Hello hash: " << tx->getHelloHash() << endl;
cout << prefix << "Hello hash length: " << tx->getHelloHash().length() << endl;
tx->setUserCallback(mcb);
tx->setSchedulingTimeout(10000);
tx->setExpireTimeout(1000000);
tx->startRunning();
tx->setPayloadFormat(StaticPayloadFormat(sptPCMU));
if (!multiParams.empty()) {
if (!tx->addDestination(pattern.getDestinationAddress(),
pattern.getDestinationPort()+10) ) {
return 1;
}
}
else {
if (!tx->addDestination(pattern.getDestinationAddress(),
pattern.getDestinationPort()) ) {
return 1;
}
}
tx->startZrtp();
// 2 packets per second (packet duration of 500ms)
uint32 period = 500;
uint16 inc = tx->getCurrentRTPClockRate()/2;
TimerPort::setTimer(period);
uint32 i;
for (i = 0; i < pattern.getPacketsNumber(); i++ ) {
tx->putData(i*inc,
pattern.getPacketData(i),
pattern.getPacketSize(i));
cout << prefix << "Sent some data: " << i << endl;
Thread::sleep(TimerPort::getTimer());
TimerPort::incTimer(period);
}
tx->putData(i*inc, (unsigned char*)"exit", 5);
Thread::sleep(TimerPort::getTimer());
delete tx;
return 0;
}
int ZrtpRecvPacketTransmissionTestCB::doTest() {
ZrtpConfigure config;
MyUserCallback* mcb;
if (!multiParams.empty()) {
rx = new SymmetricZRTPSession(pattern.getDestinationAddress(),
pattern.getDestinationPort()+10);
// rx->initialize("test_r.zid", true, &config);
rx->initialize("test_r.zid", true);
rx->setMultiStrParams(multiParams);
prefix = "RX Multi: ";
mcb = new MyUserCallbackMulti(rx);
mcb->setPrefix(prefix);
}
else {
rx = new SymmetricZRTPSession(pattern.getDestinationAddress(),
pattern.getDestinationPort());
config.setStandardConfig();
if (enroll)
config.setTrustedMitM(true); // allow a trusted MitM to start enrollment process
rx->setSignSas(signsas);
// config.addHashAlgo(Sha384);
rx->initialize("test_r.zid", true, &config);
// rx->initialize("test_r.zid", true);
prefix = "RX: ";
mcb = new MyUserCallback(rx);
mcb->setPrefix(prefix);
}
// At this point the Hello hash is available. See ZRTP specification
// chapter 9.1 for further information when an how to use the Hello
// hash.
cout << prefix << "Hello hash: " << rx->getHelloHash() << endl;
cout << prefix << "Hello hash length: " << rx->getHelloHash().length() << endl;
rx->setUserCallback(mcb);
rx->setSchedulingTimeout(10000);
rx->setExpireTimeout(1000000);
rx->startRunning();
rx->setPayloadFormat(StaticPayloadFormat(sptPCMU));
// arbitrary number of loops to provide time to start transmitter
if (!multiParams.empty()) {
if (!rx->addDestination(pattern.getDestinationAddress(),
pattern.getDestinationPort()+2+10) ) {
return 1;
}
}
else {
if (!rx->addDestination(pattern.getDestinationAddress(),
pattern.getDestinationPort()+2) ) {
return 1;
}
}
// rx->startZrtp();
for ( int i = 0; i < 5000 ; i++ ) {
const AppDataUnit* adu;
while ( (adu = rx->getData(rx->getFirstTimestamp())) ) {
cerr << prefix << "got some data: " << adu->getData() << endl;
if (*adu->getData() == 'e') {
delete adu;
delete rx;
return 0;
}
delete adu;
}
Thread::sleep(70);
}
delete rx;
return 0;
}
int main(int argc, char *argv[])
{
int result = 0;
char c;
/* check args */
while (1) {
c = getopt(argc, argv, "rsSmeu");
if (c == -1) {
break;
}
switch (c) {
case 'r':
recver = true;
break;
case 's':
sender = true;
break;
case 'm':
mitm = true;
break;
case 'e':
enroll = true;
break;
case 'u':
untrusted = true;
break;
case 'S':
signsas = true;
break;
default:
cerr << "Wrong Arguments, only -s and -r are accepted" << endl;
}
}
if (sender || recver) {
if (sender) {
cout << "Running as sender" << endl;
}
else {
cout << "Running as receiver" << endl;
}
}
else {
cerr << "No send or receive argument specificied" << endl;
exit(1);
}
if ( sender ) {
ztxcb = new ZrtpSendPacketTransmissionTestCB();
ztxcbMulti = new ZrtpSendPacketTransmissionTestCB();
ztxcb->start();
ztxcb->join();
ztxcbMulti->join();
} else if ( recver ) {
zrxcb = new ZrtpRecvPacketTransmissionTestCB();
zrxcbMulti = new ZrtpRecvPacketTransmissionTestCB();
zrxcb->start();
zrxcb->join();
zrxcbMulti->join();
}
exit(result);
}
/** EMACS **
* Local variables:
* mode: c++
* c-default-style: ellemtel
* c-basic-offset: 4
* End:
*/