/*
Copyright (C) 2006-2007 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 .
*/
/*
* Authors: Werner Dittmann
*/
#include
#include
#include
#include
#include
#include
static TimeoutProvider* staticTimeoutProvider = NULL;
#ifdef CCXX_NAMESPACES
namespace ost {
#endif
int32_t
ZrtpQueue::initialize(const char *zidFilename)
{
if (staticTimeoutProvider == NULL) {
staticTimeoutProvider = new TimeoutProvider();
staticTimeoutProvider->start();
}
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("GNUccRTP.zid");
zidFilename = fname.c_str();
}
ZIDFile *zf = ZIDFile::getInstance();
if (zf->open((char *)zidFilename) < 0) {
enableZrtp = false;
return -1;
}
return 1;
}
ZrtpQueue::ZrtpQueue(uint32 size, RTPApplication& app) :
AVPQueue(size,app)
{
init();
}
ZrtpQueue::ZrtpQueue(uint32 ssrc, uint32 size, RTPApplication& app) :
AVPQueue(ssrc,size,app)
{
init();
}
void ZrtpQueue::init()
{
zrtpUserCallback = NULL;
enableZrtp = true;
secureParts = 0;
zrtpEngine = NULL;
senderCryptoContext = NULL;
recvCryptoContext = NULL;
senderZrtpSeqNo = 1;
clientIdString = clientId;
}
ZrtpQueue::~ZrtpQueue() {
cancelTimer();
stop();
if (zrtpUserCallback != NULL) {
delete zrtpUserCallback;
zrtpUserCallback = NULL;
}
if (recvCryptoContext != NULL) {
delete recvCryptoContext;
recvCryptoContext = NULL;
}
if (senderCryptoContext != NULL) {
delete senderCryptoContext;
senderCryptoContext = NULL;
}
}
void ZrtpQueue::start() {
ZIDFile *zid = ZIDFile::getInstance();
const uint8_t* ownZid = zid->getZid();
if (zrtpEngine == NULL) {
zrtpEngine = new ZRtp((uint8_t*)ownZid, (ZrtpCallback*)this);
zrtpEngine->setClientId(clientIdString);
zrtpEngine->startZrtpEngine();
}
}
void ZrtpQueue::stop() {
endQueue();
if (zrtpEngine != NULL) {
zrtpEngine->stopZrtp();
delete zrtpEngine;
zrtpEngine = NULL;
}
}
/*
* The takeInDataPacket implementation for ZRTPQueue.
*/
size_t
ZrtpQueue::takeInDataPacket(void)
{
InetHostAddress network_address;
tpport_t transport_port;
uint32 nextSize = (uint32)getNextDataPacketSize();
unsigned char* buffer = new unsigned char[nextSize];
int32 rtn = (int32)recvData(buffer, nextSize, network_address, transport_port);
if ( (rtn < 0) || ((uint32)rtn > getMaxRecvPacketSize()) ){
delete buffer;
return 0;
}
IncomingZRTPPkt* packet = NULL;
// check if this could be a real RTP/SRTP packet.
if ((*buffer & 0xf0) != 0x10) {
// Could be real RTP, build a packet.
IncomingRTPPkt* pkt = new IncomingRTPPkt(buffer,rtn);
// Generic header validity check. If valid perform standard RTP handling
if (pkt->isHeaderValid()) {
return (rtpDataPacket(pkt, rtn, network_address, transport_port));
}
delete pkt;
return 0;
}
// We assume all other packets are ZRTP packets here. Process
// if ZRTP processing is enabled. Because valid RTP packets are
// already handled we delete any packets here after processing.
if (enableZrtp) {
// Get CRC value into crc (see above how to compute the offset)
uint16_t temp = rtn - CRC_SIZE;
uint32_t crc = *(uint32_t*)(buffer + temp);
crc = ntohl(crc);
if (!zrtpCheckCksum(buffer, temp, crc)) {
delete buffer;
zrtpUserCallback->showMessage(Error, "ZRTP packet checksum mismatch");
return 0;
}
packet = new IncomingZRTPPkt(buffer,rtn);
uint32 magic = packet->getZrtpMagic();
// Check if it is really a ZRTP packet, if not delete it and return 0
if (magic != ZRTP_MAGIC || zrtpEngine == NULL) {
delete packet;
return 0;
}
unsigned char* extHeader =
const_cast(packet->getHdrExtContent());
// this now points beyond the undefined and length field.
// We need them, thus adjust
extHeader -= 4;
zrtpEngine->processZrtpMessage(extHeader);
}
delete packet;
return 0;
}
size_t
ZrtpQueue::rtpDataPacket(IncomingRTPPkt* packet, int32 rtn,
InetHostAddress network_address,
tpport_t transport_port)
{
// Look for a CryptoContext for this packet's SSRC
CryptoContext* pcc = getInQueueCryptoContext(packet->getSSRC());
// If no crypto context is available for this SSRC but we are already in
// Secure state then create a CryptoContext for this SSRC.
// Assumption: every SSRC stream sent via this connection is secured
// _and_ uses the same crypto parameters.
if (pcc == NULL && zrtpEngine && recvCryptoContext) {
pcc = recvCryptoContext->newCryptoContextForSSRC(packet->getSSRC(), 0, 0L);
if (pcc != NULL) {
pcc->deriveSrtpKeys(packet->getSeqNum());
setInQueueCryptoContext(pcc);
}
else {
srtpSecretsOff(ForSender);
}
}
// If no crypto context: then either ZRTP is off or in early state
// If crypto context is available then unprotect data here. If an error
// occurs report the error and discard the packet.
if (pcc != NULL) {
int32 ret;
if ((ret = packet->unprotect(pcc)) < 0) {
if (!onSRTPPacketError(*packet, ret)) {
delete packet;
return 0;
}
}
}
// virtual for profile-specific validation and processing.
if (!onRTPPacketRecv(*packet) ) {
delete packet;
return 0;
}
// get time of arrival
struct timeval recvtime;
gettimeofday(&recvtime,NULL);
bool source_created;
SyncSourceLink* sourceLink =
getSourceBySSRC(packet->getSSRC(),source_created);
SyncSource* s = sourceLink->getSource();
if ( source_created ) {
// Set data transport address.
setDataTransportPort(*s,transport_port);
// Network address is assumed to be the same as the control one
setNetworkAddress(*s,network_address);
sourceLink->initStats();
// First packet arrival time.
sourceLink->setInitialDataTime(recvtime);
sourceLink->setProbation(getMinValidPacketSequence());
if ( sourceLink->getHello() )
onNewSyncSource(*s);
}
else if ( 0 == s->getDataTransportPort() ) {
// Test if RTCP packets had been received but this is the
// first data packet from this source.
setDataTransportPort(*s,transport_port);
}
// Before inserting in the queue,
// 1) check for collisions and loops. If the packet cannot be
// assigned to a source, it will be rejected.
// 2) check the source is a sufficiently well known source
// TODO: also check CSRC identifiers.
if (checkSSRCInIncomingRTPPkt(*sourceLink, source_created,
network_address, transport_port) &&
recordReception(*sourceLink,*packet,recvtime) ) {
// now the packet link is linked in the queues
IncomingRTPPktLink* packetLink =
new IncomingRTPPktLink(packet,
sourceLink,
recvtime,
packet->getTimestamp() -
sourceLink->getInitialDataTimestamp(),
NULL,NULL,NULL,NULL);
insertRecvPacket(packetLink);
} else {
// must be discarded due to collision or loop or
// invalid source
delete packet;
return 0;
}
// Start the ZRTP engine only after we got a at least one RTP packet and
// sent some as well
if (enableZrtp && zrtpEngine == NULL && getSendPacketCount() >= 1) {
start();
}
return rtn;
}
bool
ZrtpQueue::onSRTPPacketError(IncomingRTPPkt& pkt, int32 errorCode)
{
if (errorCode == -1) {
sendInfo(Error, "Dropping packet because of authentication error!");
}
else {
sendInfo(Error, "Dropping packet because replay check failed!");
}
return false;
}
void
ZrtpQueue::putData(uint32 stamp, const unsigned char* data, size_t len)
{
OutgoingDataQueue::putData(stamp, data, len);
}
void
ZrtpQueue::sendImmediate(uint32 stamp, const unsigned char* data, size_t len)
{
OutgoingDataQueue::sendImmediate(stamp, data, len);
}
/*
* Here the callback methods required by the ZRTP implementation
*/
int32_t ZrtpQueue::sendDataZRTP(const unsigned char *data, int32_t length) {
OutgoingZRTPPkt* packet = new OutgoingZRTPPkt(data, length);
packet->setSSRC(getLocalSSRC());
packet->setSeqNum(senderZrtpSeqNo++);
/*
* Compute the ZRTP CRC over the full ZRTP packet. Thus include
* the fixed packet header into the calculation.
*/
uint16_t temp = packet->getRawPacketSize() - CRC_SIZE;
uint8_t* pt = (uint8_t*)packet->getRawPacket();
uint32_t crc = zrtpGenerateCksum(pt, temp);
// convert and store CRC in crc field of ZRTP packet.
crc = zrtpEndCksum(crc);
// advance pointer to CRC storage
pt += temp;
*(uint32_t*)pt = htonl(crc);
dispatchImmediate(packet);
delete packet;
return 1;
}
bool ZrtpQueue::srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part)
{
CryptoContext* pcc;
if (part == ForSender) {
// To encrypt packets: intiator uses initiator keys,
// responder uses responder keys
// Create a "half baked" crypto context first and store it. This is
// the main crypto context for the sending part of the connection.
if (secrets->role == Initiator) {
senderCryptoContext = new CryptoContext(
0,
0,
0L, // keyderivation << 48,
SrtpEncryptionAESCM, // encryption algo
SrtpAuthenticationSha1Hmac, // authtentication algo
(unsigned char*)secrets->keyInitiator, // Master Key
secrets->initKeyLen / 8, // Master Key length
(unsigned char*)secrets->saltInitiator, // Master Salt
secrets->initSaltLen / 8, // Master Salt length
secrets->initKeyLen / 8, // encryption keyl
20, // authentication key len
secrets->initSaltLen / 8, // session salt len
secrets->srtpAuthTagLen / 8); // authentication tag lenA
}
else {
senderCryptoContext = new CryptoContext(
0,
0,
0L, // keyderivation << 48,
SrtpEncryptionAESCM, // encryption algo
SrtpAuthenticationSha1Hmac, // authtentication algo
(unsigned char*)secrets->keyResponder, // Master Key
secrets->respKeyLen / 8, // Master Key length
(unsigned char*)secrets->saltResponder, // Master Salt
secrets->respSaltLen / 8, // Master Salt length
secrets->respKeyLen / 8, // encryption keyl
20, // authentication key len
secrets->respSaltLen / 8, // session salt len
secrets->srtpAuthTagLen / 8); // authentication tag len
}
if (senderCryptoContext == NULL) {
return false;
}
// Create a SRTP crypto context for real SSRC sender stream.
// Note: key derivation can be done at this time only if the
// key derivation rate is 0 (disabled). For ZRTP this is the
// case: the key derivation is defined as 2^48
// which is effectively 0.
pcc = senderCryptoContext->newCryptoContextForSSRC(getLocalSSRC(), 0, 0L);
if (pcc == NULL) {
return false;
}
pcc->deriveSrtpKeys(0L);
setOutQueueCryptoContext(pcc);
secureParts++;
}
if (part == ForReceiver) {
// To decrypt packets: intiator uses responder keys,
// responder initiator keys
// See comment above.
if (secrets->role == Initiator) {
recvCryptoContext = new CryptoContext(
0,
0,
0L, // keyderivation << 48,
SrtpEncryptionAESCM, // encryption algo
SrtpAuthenticationSha1Hmac, // authtication algo
(unsigned char*)secrets->keyResponder, // Master Key
secrets->respKeyLen / 8, // Master Key length
(unsigned char*)secrets->saltResponder, // Master Salt
secrets->respSaltLen / 8, // Master Salt length
secrets->respKeyLen / 8, // encryption keyl
20, // authentication key len
secrets->respSaltLen / 8, // session salt len
secrets->srtpAuthTagLen / 8); // authentication tag len
}
else {
recvCryptoContext = new CryptoContext(
0,
0,
0L, // keyderivation << 48,
SrtpEncryptionAESCM, // encryption algo
SrtpAuthenticationSha1Hmac, // authtication algo
(unsigned char*)secrets->keyInitiator, // Master Key
secrets->initKeyLen / 8, // Master Key length
(unsigned char*)secrets->saltInitiator, // Master Salt
secrets->initSaltLen / 8, // Master Salt length
secrets->initKeyLen / 8, // encryption keyl
20, // authentication key len
secrets->initSaltLen / 8, // session salt len
secrets->srtpAuthTagLen / 8); // authentication tag len
}
if (recvCryptoContext == NULL) {
return false;
}
secureParts++;
}
}
void ZrtpQueue::srtpSecretsOn(const char* c, const char* s)
{
if (c != NULL && zrtpUserCallback != NULL) {
zrtpUserCallback->secureOn(c);
}
if (s != NULL && zrtpUserCallback != NULL) {
zrtpUserCallback->showSAS(s);
}
}
void ZrtpQueue::srtpSecretsOff(EnableSecurity part)
{
if (part == ForSender) {
removeOutQueueCryptoContext(NULL);
}
if (part == ForReceiver) {
removeInQueueCryptoContext(NULL);
}
secureParts = 0;
if (zrtpUserCallback != NULL) {
zrtpUserCallback->secureOff();
}
}
int32_t
ZrtpQueue::activateTimer(int32_t time)
{
std::string s("ZRTP");
staticTimeoutProvider->requestTimeout(time, this, s);
return 1;
}
int32_t
ZrtpQueue::cancelTimer()
{
std::string s("ZRTP");
staticTimeoutProvider->cancelRequest(this, s);
return 1;
}
void ZrtpQueue::handleGoClear()
{
fprintf(stderr, "Need to process a GoClear message!");
}
void ZrtpQueue::sendInfo(MessageSeverity severity, char* msg) {
if (zrtpUserCallback != NULL) {
zrtpUserCallback->showMessage(severity, msg);
}
else {
fprintf(stderr, "Severity: %d - %s\n", severity, msg);
}
}
void ZrtpQueue::zrtpNegotiationFailed(MessageSeverity severity, char* msg) {
if (zrtpUserCallback != NULL) {
zrtpUserCallback->zrtpNegotiationFailed(severity, msg);
}
else {
fprintf(stderr, "Severity: %d - %s\n", severity, msg);
}
}
void ZrtpQueue::zrtpNotSuppOther() {
if (zrtpUserCallback != NULL) {
zrtpUserCallback->zrtpNotSuppOther();
}
else {
fprintf(stderr, "The other (remote) peer does not support ZRTP\n");
}
}
IncomingZRTPPkt::IncomingZRTPPkt(const unsigned char* const block, size_t len) :
IncomingRTPPkt(block,len)
{
}
OutgoingZRTPPkt::OutgoingZRTPPkt(
const unsigned char* const hdrext, uint32 hdrextlen) :
OutgoingRTPPkt(NULL, 0, hdrext, hdrextlen, NULL ,0, 0, NULL)
{
getHeader()->version = 0;
getHeader()->timestamp = htonl(ZRTP_MAGIC);
}
#ifdef CCXX_NAMESPACES
}
#endif
/** EMACS **
* Local variables:
* mode: c++
* c-default-style: ellemtel
* c-basic-offset: 4
* End:
*/