summaryrefslogtreecommitdiff
path: root/modules/gzrtp
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
parent4c21ac818a9fed61d02b9f0966645b18265166bb (diff)
modules/gzrtp: new module using GNU ZRTP C++ library (#314)
Diffstat (limited to 'modules/gzrtp')
-rw-r--r--modules/gzrtp/gzrtp.cpp220
-rw-r--r--modules/gzrtp/messages.cpp256
-rw-r--r--modules/gzrtp/module.mk34
-rw-r--r--modules/gzrtp/session.cpp214
-rw-r--r--modules/gzrtp/session.h50
-rw-r--r--modules/gzrtp/stream.cpp824
-rw-r--r--modules/gzrtp/stream.h139
7 files changed, 1737 insertions, 0 deletions
diff --git a/modules/gzrtp/gzrtp.cpp b/modules/gzrtp/gzrtp.cpp
new file mode 100644
index 0000000..19aaf9d
--- /dev/null
+++ b/modules/gzrtp/gzrtp.cpp
@@ -0,0 +1,220 @@
+/**
+ * @file gzrtp.cpp GNU ZRTP: Media Path Key Agreement for Unicast Secure RTP
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include <string.h>
+
+#include <libzrtpcpp/ZRtp.h>
+
+#include "session.h"
+#include "stream.h"
+
+
+/**
+ * @defgroup gzrtp gzrtp
+ *
+ * ZRTP: Media Path Key Agreement for Unicast Secure RTP
+ *
+ * Experimental support for ZRTP
+ *
+ * See http://tools.ietf.org/html/rfc6189
+ *
+ *
+ * This module is using GNU ZRTP C++ library
+ *
+ * https://github.com/wernerd/ZRTPCPP
+ *
+ * Configuration options:
+ *
+ \verbatim
+ zrtp_parallel {yes,no} # Start all streams at once
+ \endverbatim
+ *
+ */
+
+
+static ZRTPConfig *s_zrtp_config = NULL;
+
+
+struct menc_sess {
+ Session *session;
+};
+
+
+struct menc_media {
+ Stream *stream;
+};
+
+
+static void session_destructor(void *arg)
+{
+ struct menc_sess *st = (struct menc_sess *)arg;
+
+ delete st->session;
+}
+
+
+static void media_destructor(void *arg)
+{
+ struct menc_media *st = (struct menc_media *)arg;
+
+ delete st->stream;
+}
+
+
+static int session_alloc(struct menc_sess **sessp, struct sdp_session *sdp,
+ bool offerer, menc_error_h *errorh, void *arg)
+{
+ struct menc_sess *st;
+ (void)offerer;
+ (void)errorh;
+ (void)arg;
+ int err = 0;
+
+ if (!sessp || !sdp)
+ return EINVAL;
+
+ st = (struct menc_sess *)mem_zalloc(sizeof(*st), session_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->session = new Session(*s_zrtp_config);
+ if (!st->session)
+ err = ENOMEM;
+
+ if (err)
+ mem_deref(st);
+ else
+ *sessp = st;
+
+ return err;
+}
+
+
+static int media_alloc(struct menc_media **stp, struct menc_sess *sess,
+ struct rtp_sock *rtp,
+ int proto, void *rtpsock, void *rtcpsock,
+ struct sdp_media *sdpm)
+{
+ struct menc_media *st;
+ int err = 0;
+ StreamMediaType med_type;
+ const char *med_name;
+
+ if (!stp || !sess || !sess->session || proto != IPPROTO_UDP)
+ return EINVAL;
+
+ st = *stp;
+ if (st)
+ goto start;
+
+ st = (struct menc_media *)mem_zalloc(sizeof(*st), media_destructor);
+ if (!st)
+ return ENOMEM;
+
+ med_name = sdp_media_name(sdpm);
+ if (str_cmp(med_name, "audio") == 0)
+ med_type = MT_AUDIO;
+ else if (str_cmp(med_name, "video") == 0)
+ med_type = MT_VIDEO;
+ else if (str_cmp(med_name, "text") == 0)
+ med_type = MT_TEXT;
+ else if (str_cmp(med_name, "application") == 0)
+ med_type = MT_APPLICATION;
+ else if (str_cmp(med_name, "message") == 0)
+ med_type = MT_MESSAGE;
+ else
+ med_type = MT_UNKNOWN;
+
+ st->stream = sess->session->create_stream(
+ *s_zrtp_config,
+ (struct udp_sock *)rtpsock,
+ (struct udp_sock *)rtcpsock,
+ rtp_sess_ssrc(rtp), med_type);
+ if (!st->stream) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ st->stream->sdp_encode(sdpm);
+
+ out:
+ if (err) {
+ mem_deref(st);
+ return err;
+ }
+ else
+ *stp = st;
+
+ start:
+ if (sa_isset(sdp_media_raddr(sdpm), SA_ALL)) {
+ st->stream->sdp_decode(sdpm);
+ err = sess->session->start_stream(st->stream);
+ if (err) {
+ warning("zrtp: stream start failed: %d\n", err);
+ }
+ }
+
+ return err;
+}
+
+
+static struct menc menc_zrtp = {
+ LE_INIT, "zrtp", "RTP/AVP", session_alloc, media_alloc
+};
+
+
+static const struct cmd cmdv[] = {
+ {"zrtp_verify", 0, CMD_PRM, "Verify ZRTP SAS <session ID>",
+ Session::cmd_verify_sas },
+ {"zrtp_unverify", 0, CMD_PRM, "Unverify ZRTP SAS <session ID>",
+ Session::cmd_unverify_sas },
+};
+
+
+static int module_init(void)
+{
+ char config_path[256];
+ int err = 0;
+
+ err = conf_path_get(config_path, sizeof(config_path));
+ if (err) {
+ warning("zrtp: could not get config path: %m\n", err);
+ return err;
+ }
+
+ s_zrtp_config = new ZRTPConfig(conf_cur(), config_path);
+ if (!s_zrtp_config)
+ return ENOMEM;
+
+ menc_register(baresip_mencl(), &menc_zrtp);
+
+ return cmd_register(baresip_commands(), cmdv, ARRAY_SIZE(cmdv));
+}
+
+
+static int module_close(void)
+{
+ delete s_zrtp_config;
+ s_zrtp_config = NULL;
+
+ cmd_unregister(baresip_commands(), cmdv);
+
+ menc_unregister(&menc_zrtp);
+
+ return 0;
+}
+
+
+extern "C" EXPORT_SYM const struct mod_export DECL_EXPORTS(gzrtp) = {
+ "gzrtp",
+ "menc",
+ module_init,
+ module_close
+};
diff --git a/modules/gzrtp/messages.cpp b/modules/gzrtp/messages.cpp
new file mode 100644
index 0000000..db3a7db
--- /dev/null
+++ b/modules/gzrtp/messages.cpp
@@ -0,0 +1,256 @@
+/**
+ * @file messages.cpp GNU ZRTP: Engine messages
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include <libzrtpcpp/ZRtp.h>
+
+#include "stream.h"
+
+
+using namespace GnuZrtpCodes;
+
+
+#define NO_MESSAGE "NO MESSAGE DEFINED"
+
+
+static const char *info_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case InfoHelloReceived:
+ msg = "Hello received and prepared a Commit, "
+ "ready to get peer's hello hash";
+ break;
+ case InfoCommitDHGenerated:
+ msg = "Commit: Generated a public DH key";
+ break;
+ case InfoRespCommitReceived:
+ msg = "Responder: Commit received, preparing DHPart1";
+ break;
+ case InfoDH1DHGenerated:
+ msg = "DH1Part: Generated a public DH key";
+ break;
+ case InfoInitDH1Received:
+ msg = "Initiator: DHPart1 received, preparing DHPart2";
+ break;
+ case InfoRespDH2Received:
+ msg = "Responder: DHPart2 received, preparing Confirm1";
+ break;
+ case InfoInitConf1Received:
+ msg = "Initiator: Confirm1 received, preparing Confirm2";
+ break;
+ case InfoRespConf2Received:
+ msg = "Responder: Confirm2 received, preparing Conf2Ack";
+ break;
+ case InfoRSMatchFound:
+ msg = "At least one retained secret matches - security OK";
+ break;
+ case InfoSecureStateOn:
+ msg = "Entered secure state";
+ break;
+ case InfoSecureStateOff:
+ msg = "No more security for this session";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+static const char *warning_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case WarningDHAESmismatch:
+ msg = "Commit contains an AES256 cipher but does not offer a "
+ "Diffie-Helman 4096 - not used DH4096 was discarded";
+ break;
+ case WarningGoClearReceived:
+ msg = "Received a GoClear message";
+ break;
+ case WarningDHShort:
+ msg = "Hello offers an AES256 cipher but does not offer a "
+ "Diffie-Helman 4096- not used DH4096 was discarded";
+ break;
+ case WarningNoRSMatch:
+ msg = "No retained shared secrets available - must verify SAS";
+ break;
+ case WarningCRCmismatch:
+ msg = "Internal ZRTP packet checksum mismatch - "
+ "packet dropped";
+ break;
+ case WarningSRTPauthError:
+ msg = "Dropping packet because SRTP authentication failed!";
+ break;
+ case WarningSRTPreplayError:
+ msg = "Dropping packet because SRTP replay check failed!";
+ break;
+ case WarningNoExpectedRSMatch:
+ msg = "Valid retained shared secrets availabe but no matches "
+ "found - must verify SAS";
+ break;
+ case WarningNoExpectedAuxMatch:
+ msg = "Our AUX secret was set but the other peer's AUX secret "
+ "does not match ours";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+static const char *severe_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case SevereHelloHMACFailed:
+ msg = "Hash HMAC check of Hello failed!";
+ break;
+ case SevereCommitHMACFailed:
+ msg = "Hash HMAC check of Commit failed!";
+ break;
+ case SevereDH1HMACFailed:
+ msg = "Hash HMAC check of DHPart1 failed!";
+ break;
+ case SevereDH2HMACFailed:
+ msg = "Hash HMAC check of DHPart2 failed!";
+ break;
+ case SevereCannotSend:
+ msg = "Cannot send data - connection or peer down?";
+ break;
+ case SevereProtocolError:
+ msg = "Internal protocol error occured!";
+ break;
+ case SevereNoTimer:
+ msg = "Cannot start a timer - internal resources exhausted?";
+ break;
+ case SevereTooMuchRetries:
+ msg = "Too much retries during ZRTP negotiation - connection "
+ "or peer down?";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+static const char *zrtp_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case MalformedPacket:
+ msg = "Malformed packet (CRC OK, but wrong structure)";
+ break;
+ case CriticalSWError:
+ msg = "Critical software error";
+ break;
+ case UnsuppZRTPVersion:
+ msg = "Unsupported ZRTP version";
+ break;
+ case HelloCompMismatch:
+ msg = "Hello components mismatch";
+ break;
+ case UnsuppHashType:
+ msg = "Hash type not supported";
+ break;
+ case UnsuppCiphertype:
+ msg = "Cipher type not supported";
+ break;
+ case UnsuppPKExchange:
+ msg = "Public key exchange not supported";
+ break;
+ case UnsuppSRTPAuthTag:
+ msg = "SRTP auth. tag not supported";
+ break;
+ case UnsuppSASScheme:
+ msg = "SAS scheme not supported";
+ break;
+ case NoSharedSecret:
+ msg = "No shared secret available, DH mode required";
+ break;
+ case DHErrorWrongPV:
+ msg = "DH Error: bad pvi or pvr ( == 1, 0, or p-1)";
+ break;
+ case DHErrorWrongHVI:
+ msg = "DH Error: hvi != hashed data";
+ break;
+ case SASuntrustedMiTM:
+ msg = "Received relayed SAS from untrusted MiTM";
+ break;
+ case ConfirmHMACWrong:
+ msg = "Auth. Error: Bad Confirm pkt HMAC";
+ break;
+ case NonceReused:
+ msg = "Nonce reuse";
+ break;
+ case EqualZIDHello:
+ msg = "Equal ZIDs in Hello";
+ break;
+ case GoCleatNotAllowed:
+ msg = "GoClear packet received, but not allowed";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+void Stream::print_message(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subcode)
+{
+ switch (severity) {
+ case Info:
+ debug("zrtp: INFO<%s>: %s\n",
+ media_name(), info_msg(subcode));
+ break;
+ case Warning:
+ warning("zrtp: WARNING<%s>: %s\n",
+ media_name(), warning_msg(subcode));
+ break;
+ case Severe:
+ warning("zrtp: SEVERE<%s>: %s\n",
+ media_name(), severe_msg(subcode));
+ break;
+ case ZrtpError:
+ warning("zrtp: ZRTP_ERR<%s>: %s\n",
+ media_name(), zrtp_msg(subcode));
+ break;
+ default:
+ return;
+ }
+}
+
+
+const char *Stream::media_name() const
+{
+ switch (m_media_type) {
+ case MT_AUDIO: return "audio";
+ case MT_VIDEO: return "video";
+ case MT_TEXT: return "text";
+ case MT_APPLICATION: return "application";
+ case MT_MESSAGE: return "message";
+ default: return "UNKNOWN";
+ }
+}
diff --git a/modules/gzrtp/module.mk b/modules/gzrtp/module.mk
new file mode 100644
index 0000000..b95b61d
--- /dev/null
+++ b/modules/gzrtp/module.mk
@@ -0,0 +1,34 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 - 2017 Creytiv.com
+#
+
+#
+# To build libzrtpcppcore run the following commands:
+#
+# git clone https://github.com/wernerd/ZRTPCPP.git
+# cd ZRTPCPP
+# mkdir build
+# cd build
+# cmake -DCMAKE_POSITION_INDEPENDENT_CODE=1 -DCORE_LIB=1 -DSDES=1 \
+# -DBUILD_STATIC=1 ..
+# make
+#
+
+# GNU ZRTP C++ library (ZRTPCPP) source directory
+ZRTP_PATH ?= ../ZRTPCPP
+
+ZRTP_LIB := $(shell find $(ZRTP_PATH) -name libzrtpcppcore.a)
+
+MOD := gzrtp
+$(MOD)_SRCS += gzrtp.cpp session.cpp stream.cpp messages.cpp
+$(MOD)_LFLAGS += $(ZRTP_LIB) -lstdc++
+$(MOD)_CXXFLAGS += \
+ -I$(ZRTP_PATH) \
+ -I$(ZRTP_PATH)/zrtp \
+ -I$(ZRTP_PATH)/srtp
+
+$(MOD)_CXXFLAGS += -O2 -Wall -fPIC
+
+include mk/mod.mk
diff --git a/modules/gzrtp/session.cpp b/modules/gzrtp/session.cpp
new file mode 100644
index 0000000..422bee4
--- /dev/null
+++ b/modules/gzrtp/session.cpp
@@ -0,0 +1,214 @@
+/**
+ * @file session.h GNU ZRTP: Session class implementation
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include "session.h"
+
+
+std::vector<Session *> Session::s_sessl;
+
+
+Session::Session(const ZRTPConfig& config)
+ : m_start_parallel(config.start_parallel)
+ , m_master(NULL)
+ , m_encrypted(0)
+{
+ int newid = 1;
+ for (std::vector<Session *>::iterator it = s_sessl.begin();
+ it != s_sessl.end(); ++it) {
+
+ if ((*it)->id() >= newid)
+ newid = (*it)->id() + 1;
+ }
+
+ m_id = newid;
+
+ s_sessl.push_back(this);
+
+ debug("zrtp: New session <%d>\n", id());
+}
+
+
+Session::~Session()
+{
+ for (std::vector<Session *>::iterator it = s_sessl.begin();
+ it != s_sessl.end(); ++it) {
+
+ if (*it == this) {
+ s_sessl.erase(it);
+ break;
+ }
+ }
+
+ debug("zrtp: Session <%d> is destroyed\n", id());
+}
+
+
+Stream *Session::create_stream(const ZRTPConfig& config,
+ udp_sock *rtpsock,
+ udp_sock *rtcpsock,
+ uint32_t local_ssrc,
+ StreamMediaType media_type)
+{
+ int err = 0;
+
+ Stream *st = new Stream (err, config, this, rtpsock, rtcpsock,
+ local_ssrc, media_type);
+ if (!st || err) {
+ delete st;
+ return NULL;
+ }
+
+ return st;
+}
+
+
+int Session::start_stream(Stream *stream)
+{
+ if (stream->started())
+ return 0;
+
+ m_streams.push_back(stream);
+
+ // Start all streams in parallel using DH mode. This is a kind of
+ // probing. The first stream to receive HelloACK will be the master
+ // stream. If disabled, only the first stream starts in DH (master)
+ // mode.
+ if (m_start_parallel) {
+ if (m_master && m_encrypted)
+ // If we already have a master in secure state,
+ // start in multistream mode
+ return stream->start(m_master);
+ else
+ // Start a new stream in DH mode
+ return stream->start(NULL);
+ }
+ else {
+ if (!m_master) {
+ // Start the first stream in DH mode
+ m_master = stream;
+ return stream->start(NULL);
+ }
+ else if (m_encrypted) {
+ // Master is in secure state; multistream
+ return stream->start(m_master);
+ }
+ }
+
+ return 0;
+}
+
+
+bool Session::request_master(Stream *stream)
+{
+ if (!m_start_parallel)
+ return true;
+
+ if (m_master)
+ return false;
+
+ // This is the first stream to receive HelloACK. It will be
+ // used as the master for the other streams in the session.
+ m_master = stream;
+ // Stop other DH-mode streams. They will be started in the
+ // multistream mode after the master enters secure state.
+ for (std::vector<Stream *>::iterator it = m_streams.begin();
+ it != m_streams.end(); ++it) {
+
+ if (*it != m_master) {
+ (*it)->stop();
+ }
+ }
+
+ return true;
+}
+
+
+void Session::on_secure(Stream *stream)
+{
+ ++m_encrypted;
+
+ if (m_encrypted == m_streams.size() && m_master) {
+ info("zrtp: All streams are encrypted (%s), "
+ "SAS is [%s] (%s)\n",
+ m_master->get_ciphers(),
+ m_master->get_sas(),
+ (m_master->sas_verified())? "verified" : "NOT VERIFIED");
+ return;
+ }
+
+ if (stream != m_master)
+ return;
+
+ // Master stream has just entered secure state. Start other
+ // streams in the multistream mode.
+
+ debug("zrtp: Starting other streams (%d)\n", m_streams.size() - 1);
+
+ for (std::vector<Stream *>::iterator it = m_streams.begin();
+ it != m_streams.end(); ++it) {
+
+ if (*it != m_master) {
+ (*it)->start(m_master);
+ }
+ }
+}
+
+
+int Session::cmd_verify_sas(struct re_printf *pf, void *arg)
+{
+ return cmd_sas(true, pf, arg);
+}
+
+
+int Session::cmd_unverify_sas(struct re_printf *pf, void *arg)
+{
+ return cmd_sas(false, pf, arg);
+}
+
+
+int Session::cmd_sas(bool verify, struct re_printf *pf, void *arg)
+{
+ const struct cmd_arg *carg = (struct cmd_arg *)arg;
+ (void)pf;
+ int id = -1;
+ Session *sess = NULL;
+
+ if (str_isset(carg->prm))
+ id = atoi(carg->prm);
+
+ for (std::vector<Session *>::iterator it = s_sessl.begin();
+ it != s_sessl.end(); ++it) {
+
+ if ((*it)->id() == id) {
+ sess = *it;
+ break;
+ }
+ }
+
+ if (!sess) {
+ warning("zrtp: No session with id %d\n", id);
+ return EINVAL;
+ }
+
+ if (!sess->m_master) {
+ warning("zrtp: No master stream for the session with id %d\n",
+ sess->id());
+ return EFAULT;
+ }
+
+ sess->m_master->verify_sas(verify);
+
+ info("zrtp: Session <%d>: SAS [%s] is %s\n", sess->id(),
+ sess->m_master->get_sas(),
+ (sess->m_master->sas_verified())? "verified" : "NOT VERIFIED");
+
+ return 0;
+}
+
diff --git a/modules/gzrtp/session.h b/modules/gzrtp/session.h
new file mode 100644
index 0000000..90c12d6
--- /dev/null
+++ b/modules/gzrtp/session.h
@@ -0,0 +1,50 @@
+/**
+ * @file session.h GNU ZRTP: Session class
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#ifndef __SESSION_H
+#define __SESSION_H
+
+
+#include "stream.h"
+
+
+class Stream;
+class ZRTPConfig;
+
+class Session {
+public:
+ Session(const ZRTPConfig& config);
+
+ ~Session();
+
+ Stream *create_stream(const ZRTPConfig& config,
+ udp_sock *rtpsock,
+ udp_sock *rtcpsock,
+ uint32_t local_ssrc,
+ StreamMediaType media_type);
+
+ int start_stream(Stream *stream);
+ int id() const { return m_id; }
+
+ bool request_master(Stream *stream);
+ void on_secure(Stream *stream);
+
+ static int cmd_verify_sas(struct re_printf *pf, void *arg);
+ static int cmd_unverify_sas(struct re_printf *pf, void *arg);
+ static int cmd_sas(bool verify, struct re_printf *pf, void *arg);
+
+private:
+ static std::vector<Session *> s_sessl;
+
+ const bool m_start_parallel;
+ int m_id;
+ std::vector<Stream *> m_streams;
+ Stream *m_master;
+ unsigned int m_encrypted;
+};
+
+
+#endif // __SESSION_H
+
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;
+}
+
diff --git a/modules/gzrtp/stream.h b/modules/gzrtp/stream.h
new file mode 100644
index 0000000..4f2f987
--- /dev/null
+++ b/modules/gzrtp/stream.h
@@ -0,0 +1,139 @@
+/**
+ * @file stream.h GNU ZRTP: Stream class
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#ifndef __STREAM_H
+#define __STREAM_H
+
+
+#include <libzrtpcpp/ZRtp.h>
+
+
+enum StreamMediaType {
+ MT_UNKNOWN = 0,
+ MT_AUDIO,
+ MT_VIDEO,
+ MT_TEXT,
+ MT_APPLICATION,
+ MT_MESSAGE
+};
+
+
+class ZRTPConfig {
+public:
+ ZRTPConfig(const struct conf *conf, const char *conf_dir);
+private:
+ friend class Stream;
+ friend class Session;
+
+ ZrtpConfigure zrtp;
+
+ char client_id[CLIENT_ID_SIZE + 1];
+ char zid_filename[256];
+
+ bool start_parallel;
+};
+
+
+class Stream;
+
+class SRTPStat {
+public:
+ SRTPStat(const Stream *st, bool srtcp, uint64_t threshold);
+ void update(int32_t ret_code, bool quiet = false);
+ void reset();
+ uint64_t ok() { return m_ok; }
+private:
+ const Stream *m_stream;
+ const bool m_control;
+ const uint64_t m_threshold;
+ uint64_t m_ok, m_decode, m_auth, m_replay;
+ uint64_t m_decode_burst, m_auth_burst, m_replay_burst;
+};
+
+
+class Session;
+class CryptoContext;
+class CryptoContextCtrl;
+
+class Stream : public ZrtpCallback {
+public:
+ Stream(int& err, const ZRTPConfig& config, Session *session,
+ udp_sock *rtpsock, udp_sock *rtcpsock,
+ uint32_t local_ssrc, StreamMediaType media_type);
+
+ virtual ~Stream();
+
+ int start(Stream *master);
+ void stop();
+ bool started() { return m_started; }
+
+ int sdp_encode(struct sdp_media *sdpm);
+ int sdp_decode(const struct sdp_media *sdpm);
+
+ const char *media_name() const;
+
+ const char *get_sas() const { return m_sas.c_str(); }
+ const char *get_ciphers() const { return m_ciphers.c_str(); }
+ bool sas_verified();
+ void verify_sas(bool verify);
+
+private:
+ static void zrtp_timer_cb(void *arg);
+ static bool udp_helper_send_cb(int *err, struct sa *src,
+ struct mbuf *mb, void *arg);
+ static bool udp_helper_recv_cb(struct sa *src, struct mbuf *mb,
+ void *arg);
+
+ bool udp_helper_send(int *err, struct sa *src, struct mbuf *mb);
+ bool udp_helper_recv(struct sa *src, struct mbuf *mb);
+ bool recv_zrtp(struct mbuf *mb);
+
+ void print_message(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subcode);
+
+ Session *m_session;
+ ZRtp *m_zrtp;
+ bool m_started;
+ struct tmr m_zrtp_timer;
+ pthread_mutex_t m_zrtp_mutex;
+ uint16_t m_zrtp_seq;
+ uint32_t m_local_ssrc, m_peer_ssrc;
+ struct sa m_raddr;
+ struct udp_sock *m_rtpsock, *m_rtcpsock;
+ struct udp_helper *m_uh_rtp;
+ struct udp_helper *m_uh_rtcp;
+ StreamMediaType m_media_type;
+ CryptoContext *m_send_cc, *m_recv_cc;
+ CryptoContextCtrl *m_send_cc_ctrl, *m_recv_cc_ctrl;
+ SRTPStat m_srtp_stat, m_srtcp_stat;
+ std::string m_sas, m_ciphers;
+
+protected:
+ virtual int32_t sendDataZRTP(const uint8_t* data, int32_t length);
+ virtual int32_t activateTimer(int32_t time);
+ virtual int32_t cancelTimer();
+ virtual void sendInfo(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subCode);
+ virtual bool srtpSecretsReady(SrtpSecret_t* secrets,
+ EnableSecurity part);
+ virtual void srtpSecretsOff(EnableSecurity part);
+ virtual void srtpSecretsOn(std::string c, std::string s,
+ bool verified);
+ virtual void handleGoClear();
+ virtual void zrtpNegotiationFailed(
+ GnuZrtpCodes::MessageSeverity severity,
+ int32_t subCode);
+ virtual void zrtpNotSuppOther();
+ virtual void synchEnter();
+ virtual void synchLeave();
+ virtual void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info);
+ virtual void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info);
+ virtual void signSAS(uint8_t* sasHash);
+ virtual bool checkSASSignature(uint8_t* sasHash);
+};
+
+
+#endif // __STREAM_H
+