summaryrefslogtreecommitdiff
path: root/modules/gzrtp/stream.cpp
diff options
context:
space:
mode:
authorglenvt18 <glenvt18@gmail.com>2017-10-28 22:06:44 +0300
committerAlfred E. Heggestad <alfred.heggestad@gmail.com>2017-10-28 21:06:44 +0200
commitb576ea027970338a418be2adb831c2dcf6484b2b (patch)
tree6d64d8eb50bf190b9b313a400872e1089aa13a8e /modules/gzrtp/stream.cpp
parent4c21ac818a9fed61d02b9f0966645b18265166bb (diff)
modules/gzrtp: new module using GNU ZRTP C++ library (#314)
Diffstat (limited to 'modules/gzrtp/stream.cpp')
-rw-r--r--modules/gzrtp/stream.cpp824
1 files changed, 824 insertions, 0 deletions
diff --git a/modules/gzrtp/stream.cpp b/modules/gzrtp/stream.cpp
new file mode 100644
index 0000000..b367112
--- /dev/null
+++ b/modules/gzrtp/stream.cpp
@@ -0,0 +1,824 @@
+/**
+ * @file stream.cpp GNU ZRTP: Stream class implementation
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+#include <pthread.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include <libzrtpcpp/ZRtp.h>
+#include <libzrtpcpp/ZrtpStateClass.h>
+#include <srtp/CryptoContext.h>
+#include <srtp/CryptoContextCtrl.h>
+#include <srtp/SrtpHandler.h>
+
+#include "session.h"
+#include "stream.h"
+
+
+// A burst of SRTP/SRTCP errors enough to display a warning
+// Set to 1 to display all warnings
+#define SRTP_ERR_BURST_THRESHOLD 20
+
+
+enum {
+ PRESZ = 36 /* Preamble size for TURN/STUN header */
+};
+
+
+enum pkt_type {
+ PKT_TYPE_UNKNOWN = 0,
+ PKT_TYPE_RTP = 1,
+ PKT_TYPE_RTCP = 2,
+ PKT_TYPE_ZRTP = 4
+};
+
+
+static enum pkt_type get_packet_type(const struct mbuf *mb)
+{
+ uint8_t b, pt;
+ uint32_t magic;
+
+ if (mbuf_get_left(mb) < 8)
+ return PKT_TYPE_UNKNOWN;
+
+ b = mbuf_buf(mb)[0];
+
+ if (127 < b && b < 192) {
+ pt = mbuf_buf(mb)[1] & 0x7f;
+ if (72 <= pt && pt <= 76)
+ return PKT_TYPE_RTCP;
+ else
+ return PKT_TYPE_RTP;
+ }
+ else {
+ memcpy(&magic, &mbuf_buf(mb)[4], 4);
+ magic = ntohl(magic);
+ if (magic == ZRTP_MAGIC)
+ return PKT_TYPE_ZRTP;
+ }
+
+ return PKT_TYPE_UNKNOWN;
+}
+
+
+ZRTPConfig::ZRTPConfig(const struct conf *conf, const char *conf_dir)
+{
+ zrtp.setStandardConfig();
+
+ str_ncpy(client_id, "baresip/gzrtp", sizeof(client_id));
+
+ re_snprintf(zid_filename, sizeof(zid_filename),
+ "%s/gzrtp.zid", conf_dir);
+
+ start_parallel = true;
+ (void)conf_get_bool(conf, "zrtp_parallel", &start_parallel);
+}
+
+SRTPStat::SRTPStat(const Stream *st, bool srtcp, uint64_t threshold)
+ : m_stream(st)
+ , m_control(srtcp)
+ , m_threshold(threshold)
+{
+ reset();
+}
+
+
+void SRTPStat::update(int32_t ret_code, bool quiet)
+{
+ const char *err_msg;
+ uint64_t *burst;
+
+ // SrtpHandler::unprotect/unprotectCtrl return codes
+ switch (ret_code) {
+ case 1:
+ ++m_ok;
+ m_decode_burst = 0;
+ m_auth_burst = 0;
+ m_replay_burst = 0;
+ return;
+ case 0:
+ ++m_decode;
+ burst = &m_decode_burst;
+ err_msg = "packet decode error";
+ break;
+ case -1:
+ ++m_auth;
+ burst = &m_auth_burst;
+ err_msg = "authentication failed";
+ break;
+ case -2:
+ ++m_replay;
+ burst = &m_replay_burst;
+ err_msg = "replay check failed";
+ break;
+ default:
+ warning("zrtp: Unknown return code from unprotect: %d\n",
+ ret_code);
+ return;
+ }
+
+ ++(*burst);
+ if (*burst == m_threshold) {
+ *burst = 0;
+
+ if (!quiet)
+ warning("zrtp: Stream <%s>: %s %s, %d packets\n",
+ m_stream->media_name(),
+ (m_control)? "SRTCP" : "SRTP",
+ err_msg,
+ m_threshold);
+ }
+}
+
+
+void SRTPStat::reset()
+{
+ m_ok = 0;
+ m_decode = 0; m_auth = 0; m_replay = 0;
+ m_decode_burst = 0; m_auth_burst = 0; m_replay_burst = 0;
+}
+
+
+Stream::Stream(int& err, const ZRTPConfig& config, Session *session,
+ udp_sock *rtpsock, udp_sock *rtcpsock,
+ uint32_t local_ssrc, StreamMediaType media_type)
+ : m_session(session)
+ , m_zrtp(NULL)
+ , m_started(false)
+ , m_local_ssrc(local_ssrc)
+ , m_peer_ssrc(0)
+ , m_rtpsock(NULL)
+ , m_rtcpsock(NULL)
+ , m_uh_rtp(NULL)
+ , m_uh_rtcp(NULL)
+ , m_media_type(media_type)
+ , m_send_cc(NULL)
+ , m_recv_cc(NULL)
+ , m_send_cc_ctrl(NULL)
+ , m_recv_cc_ctrl(NULL)
+ , m_srtp_stat(this, false, SRTP_ERR_BURST_THRESHOLD)
+ , m_srtcp_stat(this, true, SRTP_ERR_BURST_THRESHOLD)
+{
+ err = 0;
+
+ m_zrtp_seq = 1; // TODO: randomize
+ sa_init(&m_raddr, AF_INET);
+ tmr_init(&m_zrtp_timer);
+
+ pthread_mutexattr_t attr;
+ err = pthread_mutexattr_init(&attr);
+ err |= pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+ err |= pthread_mutex_init(&m_zrtp_mutex, &attr);
+ if (err)
+ return;
+
+ int layer = 10; // above zero
+ if (rtpsock) {
+ m_rtpsock = (struct udp_sock *)mem_ref(rtpsock);
+ err |= udp_register_helper(&m_uh_rtp, rtpsock, layer,
+ Stream::udp_helper_send_cb,
+ Stream::udp_helper_recv_cb,
+ this);
+ }
+ if (rtcpsock && (rtcpsock != rtpsock)) {
+ m_rtcpsock = (struct udp_sock *)mem_ref(rtcpsock);
+ err |= udp_register_helper(&m_uh_rtcp, rtcpsock, layer,
+ Stream::udp_helper_send_cb,
+ Stream::udp_helper_recv_cb,
+ this);
+ }
+ if (err)
+ return;
+
+ ZIDCache* zf = getZidCacheInstance();
+ if (!zf->isOpen()) {
+ if (zf->open((char *)config.zid_filename) == -1) {
+ warning("zrtp: Couldn't open/create ZID file %s\n",
+ config.zid_filename);
+ err = ENOENT;
+ return;
+ }
+ }
+
+ m_zrtp = new ZRtp((uint8_t *)zf->getZid(), this, config.client_id,
+ (ZrtpConfigure *)&config.zrtp, false, false);
+ if (!m_zrtp) {
+ err = ENOMEM;
+ return;
+ }
+
+ return;
+}
+
+
+Stream::~Stream()
+{
+ stop();
+
+ delete m_zrtp;
+
+ mem_deref(m_uh_rtp);
+ mem_deref(m_uh_rtcp);
+ mem_deref(m_rtpsock);
+ mem_deref(m_rtcpsock);
+
+ pthread_mutex_destroy(&m_zrtp_mutex);
+
+ tmr_cancel(&m_zrtp_timer);
+}
+
+
+int Stream::start(Stream *master)
+{
+ if (started())
+ return EPERM;
+
+ if (master) {
+ ZRtp *zrtp_master;
+
+ std::string params =
+ master->m_zrtp->getMultiStrParams(&zrtp_master);
+ if (params.empty())
+ return EPROTO;
+
+ m_zrtp->setMultiStrParams(params, zrtp_master);
+ }
+
+ debug("zrtp: Starting <%s> stream%s\n", media_name(),
+ (m_zrtp->isMultiStream())? " (multistream)" : "");
+
+ m_srtp_stat.reset();
+ m_srtcp_stat.reset();
+ m_sas.clear();
+ m_ciphers.clear();
+
+ m_started = true;
+ m_zrtp->startZrtpEngine();
+
+ return 0;
+}
+
+
+void Stream::stop()
+{
+ if (!started())
+ return;
+
+ m_started = false;
+
+ // If we got only a small amount of valid SRTP packets after ZRTP
+ // negotiation then assume that our peer couldn't store the RS data,
+ // thus make sure we have a second retained shared secret available.
+ // Refer to RFC 6189bis, chapter 4.6.1 50 packets are about 1 second
+ // of audio data
+ if (!m_zrtp->isMultiStream() && m_recv_cc && m_srtp_stat.ok() < 20) {
+
+ debug("zrtp: Stream <%s>: received too few valid SRTP "
+ "packets (%u), storing RS2\n",
+ media_name(), m_srtp_stat.ok());
+
+ m_zrtp->setRs2Valid();
+ }
+
+ debug("zrtp: Stopping <%s> stream\n", media_name());
+
+ m_zrtp->stopZrtp();
+
+ delete m_send_cc;
+ m_send_cc = NULL;
+
+ delete m_recv_cc;
+ m_recv_cc = NULL;
+
+ delete m_send_cc_ctrl;
+ m_send_cc_ctrl = NULL;
+
+ delete m_recv_cc_ctrl;
+ m_recv_cc_ctrl = NULL;
+
+ debug("zrtp: Stream <%s> stopped\n", media_name());
+}
+
+
+int Stream::sdp_encode(struct sdp_media *sdpm)
+{
+ // TODO: signaling hash
+ return 0;
+}
+
+
+int Stream::sdp_decode(const struct sdp_media *sdpm)
+{
+ if (sa_isset(sdp_media_raddr(sdpm), SA_ALL)) {
+ m_raddr = *sdp_media_raddr(sdpm);
+ }
+ // TODO: signaling hash
+
+ return 0;
+}
+
+
+bool Stream::udp_helper_send_cb(int *err, struct sa *src, struct mbuf *mb,
+ void *arg)
+{
+ Stream *st = (Stream *)arg;
+
+ if (st)
+ return st->udp_helper_send(err, src, mb);
+
+ return false;
+}
+
+
+bool Stream::udp_helper_send(int *err, struct sa *src, struct mbuf *mb)
+{
+ if (!started())
+ return false;
+
+ enum pkt_type ptype = get_packet_type(mb);
+ size_t len = mbuf_get_left(mb);
+ size_t newlen = 0;
+ bool rc = false;
+
+ if (ptype == PKT_TYPE_RTCP && m_send_cc_ctrl && len > 8) {
+
+ int32_t extra = (mbuf_get_space(mb) > len)?
+ mbuf_get_space(mb) - len : 0;
+
+ if (m_send_cc_ctrl->getTagLength() + 4 +
+ m_send_cc_ctrl->getMkiLength() > extra) {
+ warning("zrtp: Stream <%s>: No space left for SRTCP "
+ "packet\n", media_name());
+ *err = ENOMEM;
+ return true;
+ }
+
+ rc = SrtpHandler::protectCtrl(m_send_cc_ctrl, mbuf_buf(mb),
+ len, &newlen);
+ }
+ else if (ptype == PKT_TYPE_RTP && m_send_cc && len > RTP_HEADER_SIZE) {
+
+ int32_t extra = (mbuf_get_space(mb) > len)?
+ mbuf_get_space(mb) - len : 0;
+
+ if (m_send_cc->getTagLength() +
+ m_send_cc->getMkiLength() > extra) {
+ warning("zrtp: Stream <%s>: No space left for SRTP "
+ "packet\n", media_name());
+ *err = ENOMEM;
+ return true;
+ }
+
+ rc = SrtpHandler::protect(m_send_cc, mbuf_buf(mb),
+ len, &newlen);
+ }
+ else
+ return false;
+
+ if (!rc) {
+ warning("zrtp: protect/protectCtrl failed, len: %u\n", len);
+ // drop
+ return true;
+ }
+
+ if (newlen > mbuf_get_space(mb)) {
+ // this should never happen
+ error_msg("zrtp: udp_helper_send: length > space (%u > %u)\n",
+ newlen, mbuf_get_space(mb));
+ abort();
+ }
+
+ mb->end = mb->pos + newlen;
+
+ return false;
+}
+
+
+bool Stream::udp_helper_recv_cb(struct sa *src, struct mbuf *mb, void *arg)
+{
+ Stream *st = (Stream *)arg;
+
+ if (st)
+ return st->udp_helper_recv(src, mb);
+
+ return false;
+}
+
+
+bool Stream::udp_helper_recv(struct sa *src, struct mbuf *mb)
+{
+ if (!started())
+ return false;
+
+ enum pkt_type ptype = get_packet_type(mb);
+ size_t len = mbuf_get_left(mb);
+ size_t newlen = 0;
+ uint32_t rc;
+
+ if (ptype == PKT_TYPE_RTCP && m_recv_cc_ctrl) {
+
+ rc = SrtpHandler::unprotectCtrl(m_recv_cc_ctrl, mbuf_buf(mb),
+ len, &newlen);
+
+ m_srtcp_stat.update(rc);
+
+ if (rc != 1)
+ // drop
+ return true;
+ }
+ else if (ptype == PKT_TYPE_RTP && m_recv_cc) {
+
+ rc = SrtpHandler::unprotect(m_recv_cc, mbuf_buf(mb),
+ len, &newlen, NULL);
+
+ m_srtp_stat.update(rc);
+
+ if (rc == 1) {
+ // Got a good SRTP, check state and if in WaitConfAck
+ // (an Initiator state) then simulate a conf2Ack,
+ // refer to RFC 6189, chapter 4.6, last paragraph
+ if (m_zrtp->inState(WaitConfAck))
+ m_zrtp->conf2AckSecure();
+ }
+ else {
+ // drop
+ return true;
+ }
+ }
+ else if (ptype == PKT_TYPE_ZRTP) {
+ return recv_zrtp(mb);
+ }
+ else
+ return false;
+
+ mb->end = mb->pos + newlen;
+
+ return false;
+}
+
+
+// <RTP> + <ext. header> + <ZRTP message type> + CRC32
+#define ZRTP_MIN_PACKET_LENGTH (RTP_HEADER_SIZE + 4 + 8 + 4)
+
+bool Stream::recv_zrtp(struct mbuf *mb)
+{
+ uint32_t crc32;
+ uint8_t *buf = mbuf_buf(mb);
+ size_t size = mbuf_get_left(mb);
+
+ if (size < ZRTP_MIN_PACKET_LENGTH) {
+ warning("zrtp: incoming packet size (%d) is too small\n",
+ size);
+ return false;
+ }
+
+ // check CRC
+ memcpy(&crc32, buf + size - 4, 4);
+ crc32 = ntohl(crc32);
+ if (!zrtpCheckCksum(buf, size - 4, crc32)) {
+ sendInfo(GnuZrtpCodes::Warning,
+ GnuZrtpCodes::WarningCRCmismatch);
+ return false;
+ }
+
+ // store peer's SSRC for creating the CryptoContext
+ memcpy(&m_peer_ssrc, buf + 8, 4);
+ m_peer_ssrc = ntohl(m_peer_ssrc);
+
+ m_zrtp->processZrtpMessage(buf + RTP_HEADER_SIZE, m_peer_ssrc, size);
+
+ return true;
+}
+
+
+void Stream::verify_sas(bool verify)
+{
+ if (verify)
+ m_zrtp->SASVerified();
+ else
+ m_zrtp->resetSASVerified();
+}
+
+
+bool Stream::sas_verified()
+{
+ return m_zrtp->isSASVerified();
+}
+
+
+//
+// callbacks
+//
+
+
+int32_t Stream::sendDataZRTP(const uint8_t* data, int32_t length)
+{
+ struct mbuf *mb;
+ uint8_t *crc_buf;
+ uint32_t crc32;
+ size_t start_pos = PRESZ;
+ int err = 0;
+
+ if (!sa_isset(&m_raddr, SA_ALL))
+ return 0;
+
+ mb = mbuf_alloc(start_pos + RTP_HEADER_SIZE + length);
+ if (!mb)
+ return 0;
+
+ mbuf_set_end(mb, start_pos);
+ mbuf_set_pos(mb, start_pos);
+ crc_buf = mbuf_buf(mb);
+
+ // write RTP header
+ err = mbuf_write_u8(mb, 0x10);
+ err |= mbuf_write_u8(mb, 0x00);
+ err |= mbuf_write_u16(mb, htons(m_zrtp_seq++));
+ err |= mbuf_write_u32(mb, htonl(ZRTP_MAGIC));
+ err |= mbuf_write_u32(mb, htonl(m_local_ssrc));
+
+ // copy ZRTP message data
+ err |= mbuf_write_mem(mb, data, length - 4);
+
+ // compute CRC
+ crc32 = zrtpGenerateCksum(crc_buf, RTP_HEADER_SIZE + length - 4);
+ crc32 = zrtpEndCksum(crc32);
+
+ // store CRC
+ err |= mbuf_write_u32(mb, htonl(crc32));
+ if (err)
+ goto out;
+
+ // send ZRTP packet using RTP socket
+ mbuf_set_pos(mb, start_pos);
+ err = udp_send_helper(m_rtpsock, &m_raddr, mb, m_uh_rtp);
+ if (err)
+ warning("zrtp: udp_send_helper: %m\n", err);
+
+ out:
+ mem_deref(mb);
+
+ return (err == 0);
+}
+
+
+void Stream::zrtp_timer_cb(void *arg)
+{
+ Stream *s = (Stream *)arg;
+
+ s->m_zrtp->processTimeout();
+}
+
+
+int32_t Stream::activateTimer(int32_t time)
+{
+ tmr_start(&m_zrtp_timer, time, &Stream::zrtp_timer_cb, this);
+ return 1;
+}
+
+
+int32_t Stream::cancelTimer()
+{
+ tmr_cancel(&m_zrtp_timer);
+ return 1;
+}
+
+
+void Stream::sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode)
+{
+ print_message(severity, subCode);
+
+ if (severity == GnuZrtpCodes::Info) {
+ if (subCode == GnuZrtpCodes::InfoSecureStateOn) {
+ m_session->on_secure(this);
+ }
+ else if (subCode == GnuZrtpCodes::InfoHelloReceived &&
+ !m_zrtp->isMultiStream()) {
+
+ m_session->request_master(this);
+ }
+ }
+}
+
+
+bool Stream::srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part)
+{
+ CryptoContext *cc = NULL;
+ CryptoContextCtrl *cc_ctrl = NULL;
+ int cipher;
+ int authn;
+ int auth_key_len;
+ const uint8_t *key, *salt;
+ uint32_t key_len, salt_len;
+
+ debug("zrtp: Stream <%s>: secrets are ready for %s\n",
+ media_name(),
+ (part == ForSender)? "sender" : "receiver");
+
+ switch (secrets->authAlgorithm) {
+ case Sha1:
+ authn = SrtpAuthenticationSha1Hmac;
+ auth_key_len = 20;
+ break;
+ case Skein:
+ authn = SrtpAuthenticationSkeinHmac;
+ auth_key_len = 32;
+ break;
+ default:
+ return false;
+ }
+
+ switch (secrets->symEncAlgorithm) {
+ case Aes:
+ cipher = SrtpEncryptionAESCM;
+ break;
+ case TwoFish:
+ cipher = SrtpEncryptionTWOCM;
+ break;
+ default:
+ return false;
+ }
+
+ if (part == ForSender) {
+ // To encrypt packets: intiator uses initiator keys,
+ // responder uses responder keys
+ if (secrets->role == Initiator) {
+ key = secrets->keyInitiator;
+ key_len = secrets->initKeyLen / 8;
+ salt = secrets->saltInitiator;
+ salt_len = secrets->initSaltLen / 8;
+ }
+ else {
+ key = secrets->keyResponder;
+ key_len = secrets->respKeyLen / 8;
+ salt = secrets->saltResponder;
+ salt_len = secrets->respSaltLen / 8;
+ }
+ }
+ else if (part == ForReceiver) {
+ // To decrypt packets: intiator uses responder keys,
+ // responder initiator keys
+ if (secrets->role == Initiator) {
+ key = secrets->keyResponder;
+ key_len = secrets->respKeyLen / 8;
+ salt = secrets->saltResponder;
+ salt_len = secrets->respSaltLen / 8;
+ }
+ else {
+ key = secrets->keyInitiator;
+ key_len = secrets->initKeyLen / 8;
+ salt = secrets->saltInitiator;
+ salt_len = secrets->initSaltLen / 8;
+ }
+ }
+ else {
+ return false;
+ }
+
+ cc = new CryptoContext(
+ 0, // SSRC (used for lookup)
+ 0, // Roll-Over-Counter (ROC)
+ 0L, // keyderivation << 48,
+ cipher, // encryption algo
+ authn, // authtentication algo
+ (uint8_t *)key, // Master Key
+ key_len, // Master Key length
+ (uint8_t *)salt, // Master Salt
+ salt_len, // Master Salt length
+ key_len, // encryption keyl
+ auth_key_len, // authentication key len
+ salt_len, // session salt len
+ secrets->srtpAuthTagLen / 8); // authentication tag lenA
+
+ cc_ctrl = new CryptoContextCtrl(
+ 0, // SSRC (used for lookup)
+ cipher, // encryption algo
+ authn, // authtentication algo
+ (uint8_t *)key, // Master Key
+ key_len, // Master Key length
+ (uint8_t *)salt, // Master Salt
+ salt_len, // Master Salt length
+ key_len, // encryption keyl
+ auth_key_len, // authentication key len
+ salt_len, // session salt len
+ secrets->srtpAuthTagLen / 8); // authentication tag lenA
+
+ if (!cc || !cc_ctrl) {
+ delete cc;
+ delete cc_ctrl;
+
+ return false;
+ }
+
+ cc->deriveSrtpKeys(0L);
+ cc_ctrl->deriveSrtcpKeys();
+
+ if (part == ForSender) {
+ m_send_cc = cc;
+ m_send_cc_ctrl = cc_ctrl;
+ }
+ else {
+ m_recv_cc = cc;
+ m_recv_cc_ctrl = cc_ctrl;
+ }
+
+ return true;
+}
+
+
+void Stream::srtpSecretsOff(EnableSecurity part)
+{
+ debug("zrtp: Stream <%s>: secrets are off for %s\n",
+ media_name(),
+ (part == ForSender)? "sender" : "receiver");
+
+ if (part == ForSender) {
+ delete m_send_cc;
+ delete m_send_cc_ctrl;
+ m_send_cc = NULL;
+ m_send_cc_ctrl = NULL;
+ }
+
+ if (part == ForReceiver) {
+ delete m_recv_cc;
+ delete m_recv_cc_ctrl;
+ m_recv_cc = NULL;
+ m_recv_cc_ctrl = NULL;
+ }
+}
+
+
+void Stream::srtpSecretsOn(std::string c, std::string s, bool verified)
+{
+ m_sas = s;
+ m_ciphers = c;
+
+ if (s.empty()) {
+ info("zrtp: Stream <%s> is encrypted (%s)\n",
+ media_name(), c.c_str());
+ }
+ else {
+ info("zrtp: Stream <%s> is encrypted (%s), "
+ "SAS is [%s] (%s)\n",
+ media_name(), c.c_str(), s.c_str(),
+ (verified)? "verified" : "NOT VERIFIED");
+ if (!verified)
+ warning("zrtp: SAS is not verified, type "
+ "'/zrtp_verify %d' to verify\n",
+ m_session->id());
+ }
+}
+
+
+void Stream::handleGoClear()
+{
+}
+
+
+void Stream::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subCode)
+{
+}
+
+
+void Stream::zrtpNotSuppOther()
+{
+}
+
+
+void Stream::synchEnter()
+{
+ pthread_mutex_lock(&m_zrtp_mutex);
+}
+
+
+void Stream::synchLeave()
+{
+ pthread_mutex_unlock(&m_zrtp_mutex);
+}
+
+
+void Stream::zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info)
+{
+}
+
+
+void Stream::zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info)
+{
+}
+
+
+void Stream::signSAS(uint8_t* sasHash)
+{
+}
+
+
+bool Stream::checkSASSignature(uint8_t* sasHash)
+{
+ return true;
+}
+