void OutgoingDataQueue::putData(uint32 stamp, const unsigned char *data, size_t datalen) { if ( !data || !datalen ) return; size_t step = 0, offset = 0; while ( offset < datalen ) { // remainder and step take care of segmentation // according to getMaxSendSegmentSize() size_t remainder = datalen - offset; step = ( remainder > getMaxSendSegmentSize() ) ? getMaxSendSegmentSize() : remainder; CryptoContext* pcc = getOutQueueCryptoContext(getLocalSSRC()); if (pcc == NULL) { pcc = getOutQueueCryptoContext(0); if (pcc != NULL) { pcc = pcc->newCryptoContextForSSRC(getLocalSSRC(), 0, 0L); if (pcc != NULL) { pcc->deriveSrtpKeys(0); setOutQueueCryptoContext(pcc); } } } OutgoingRTPPkt* packet; if ( sendInfo.sendCC ) packet = new OutgoingRTPPkt(sendInfo.sendSources,15,data + offset,step, sendInfo.paddinglen, pcc); else packet = new OutgoingRTPPkt(data + offset,step,sendInfo.paddinglen, pcc); packet->setPayloadType(getCurrentPayloadType()); packet->setSeqNum(sendInfo.sendSeq++); packet->setTimestamp(stamp + getInitialTimestamp()); packet->setSSRCNetwork(getLocalSSRCNetwork()); if ( (0 == offset) && getMark() ) { packet->setMarker(true); setMark(false); } else { packet->setMarker(false); } if (pcc != NULL) { packet->protect(getLocalSSRC(), pcc); } // insert the packet into the "tail" of the sending queue sendLock.writeLock(); OutgoingRTPPktLink *link = new OutgoingRTPPktLink(packet,sendLast,NULL); if (sendLast) sendLast->setNext(link); else sendFirst = link; sendLast = link; sendLock.unlock(); offset += step; } }
bool CtZrtpStream::srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part) { CryptoContext* recvCryptoContext; CryptoContext* senderCryptoContext; CryptoContextCtrl* recvCryptoContextCtrl; CryptoContextCtrl* senderCryptoContextCtrl; int cipher; int authn; int authKeyLen; if (secrets->authAlgorithm == Sha1) { authn = SrtpAuthenticationSha1Hmac; authKeyLen = 20; } if (secrets->authAlgorithm == Skein) { authn = SrtpAuthenticationSkeinHmac; authKeyLen = 32; } if (secrets->symEncAlgorithm == Aes) cipher = SrtpEncryptionAESCM; if (secrets->symEncAlgorithm == TwoFish) cipher = SrtpEncryptionTWOCM; role = secrets->role; 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, // SSRC (used for lookup) 0, // Roll-Over-Counter (ROC) 0L, // keyderivation << 48, cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag lenA senderCryptoContextCtrl = new CryptoContextCtrl(0, // SSRC (used for lookup) cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len } else { senderCryptoContext = new CryptoContext(0, // SSRC (used for lookup) 0, // Roll-Over-Counter (ROC) 0L, // keyderivation << 48, cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len senderCryptoContextCtrl = new CryptoContextCtrl(0, // SSRC (used for lookup) cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len } if (senderCryptoContext == NULL) { return false; } senderCryptoContext->deriveSrtpKeys(0L); sendSrtp = senderCryptoContext; senderCryptoContextCtrl->deriveSrtcpKeys(); sendSrtcp = senderCryptoContextCtrl; } if (part == ForReceiver) { // To decrypt packets: intiator uses responder keys, // responder initiator keys // See comment above. if (secrets->role == Initiator) { recvCryptoContext = new CryptoContext(0, // SSRC (used for lookup) 0, // Roll-Over-Counter (ROC) 0L, // keyderivation << 48, cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len recvCryptoContextCtrl = new CryptoContextCtrl(0, // SSRC (used for lookup) cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len } else { recvCryptoContext = new CryptoContext(0, // SSRC (used for lookup) 0, // Roll-Over-Counter (ROC) 0L, // keyderivation << 48, cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len recvCryptoContextCtrl = new CryptoContextCtrl(0, // SSRC (used for lookup) cipher, // encryption algo authn, // 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 authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len } if (recvCryptoContext == NULL) { return false; } recvCryptoContext->deriveSrtpKeys(0L); recvSrtp = recvCryptoContext; recvCryptoContextCtrl->deriveSrtcpKeys(); recvSrtcp = recvCryptoContextCtrl; supressCounter = 0; // supress SRTP warnings for some packets after we switch to SRTP } if (zrtpHashMatch && recvSrtp != NULL && sendSrtp != NULL) { sdesActive = false; } return true; }
size_t ZrtpQueue::rtpDataPacket(unsigned char* buffer, int32 rtn, InetHostAddress network_address, tpport_t transport_port) { // Special handling of padding to take care of encrypted content. // In case of SRTP the padding length field is also encrypted, thus // it gives a wrong length. Check and clear padding bit before // creating the RTPPacket. Will be set and re-computed after a possible // SRTP decryption. uint8 padSet = (*buffer & 0x20); if (padSet) { *buffer = *buffer & ~0x20; // clear padding bit } // build a packet. It will link itself to its source IncomingRTPPkt* packet = new IncomingRTPPkt(buffer,rtn); // Generic header validity check. if ( !packet->isHeaderValid() ) { delete packet; return 0; } // 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) { pcc = getInQueueCryptoContext(0); if (pcc != NULL) { pcc = pcc->newCryptoContextForSSRC(packet->getSSRC(), 0, 0L); if (pcc != NULL) { pcc->deriveSrtpKeys(0); setInQueueCryptoContext(pcc); } } } // 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; } } if (started && zrtpEngine->inState(WaitConfAck)) { zrtpEngine->conf2AckSecure(); } } // virtual for profile-specific validation and processing. if (!onRTPPacketRecv(*packet) ) { delete packet; return 0; } if (padSet) { packet->reComputePayLength(true); } // 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 after we got a at least one RTP packet and // sent some as well or we are in multi-stream mode. if (!started && enableZrtp) { startZrtp(); } return rtn; }