mirror of
https://github.com/baresip/baresip
synced 2026-08-07 00:26:06 -04:00
731 lines
14 KiB
C++
731 lines
14 KiB
C++
/**
|
|
* @file stream.cpp GNU ZRTP: Stream class implementation
|
|
*
|
|
* Copyright (C) 2010 - 2017 Alfred E. Heggestad
|
|
*/
|
|
#include <stdint.h>
|
|
|
|
#include <re.h>
|
|
#include <baresip.h>
|
|
|
|
#include <libzrtpcpp/ZRtp.h>
|
|
#include <libzrtpcpp/ZrtpStateClass.h>
|
|
|
|
#include "session.h"
|
|
#include "stream.h"
|
|
#include "srtp.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)
|
|
{
|
|
#ifdef GZRTP_USE_RE_SRTP
|
|
// Standard ciphers only
|
|
zrtp.clear();
|
|
|
|
zrtp.addAlgo(HashAlgorithm, zrtpHashes.getByName(s256));
|
|
|
|
zrtp.addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes3));
|
|
zrtp.addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes1));
|
|
|
|
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec25));
|
|
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh3k));
|
|
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec38));
|
|
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh2k));
|
|
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(mult));
|
|
|
|
zrtp.addAlgo(SasType, zrtpSasTypes.getByName(b32));
|
|
|
|
zrtp.addAlgo(AuthLength, zrtpAuthLengths.getByName(hs32));
|
|
zrtp.addAlgo(AuthLength, zrtpAuthLengths.getByName(hs80));
|
|
#else
|
|
zrtp.setStandardConfig();
|
|
#endif
|
|
|
|
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(int ret_code, bool quiet)
|
|
{
|
|
const char *err_msg;
|
|
uint64_t *burst;
|
|
|
|
// Srtp::unprotect/unprotect_ctrl return codes
|
|
switch (ret_code) {
|
|
case 0:
|
|
++m_ok;
|
|
m_decode_burst = 0;
|
|
m_auth_burst = 0;
|
|
m_replay_burst = 0;
|
|
return;
|
|
case EBADMSG:
|
|
++m_decode;
|
|
burst = &m_decode_burst;
|
|
err_msg = "packet decode error";
|
|
break;
|
|
case EAUTH:
|
|
++m_auth;
|
|
burst = &m_auth_burst;
|
|
err_msg = "authentication failed";
|
|
break;
|
|
case EALREADY:
|
|
++m_replay;
|
|
burst = &m_replay_burst;
|
|
err_msg = "replay check failed";
|
|
break;
|
|
default:
|
|
warning("zrtp: %s unprotect failed: %m\n",
|
|
(m_control)? "SRTCP" : "SRTP", 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_srtp(NULL)
|
|
, m_recv_srtp(NULL)
|
|
, m_srtp_stat(this, false, SRTP_ERR_BURST_THRESHOLD)
|
|
, m_srtcp_stat(this, true, SRTP_ERR_BURST_THRESHOLD)
|
|
{
|
|
err = 0;
|
|
|
|
m_zrtp_seq = rand_u16() & 0x7fff;
|
|
sa_init(&m_raddr, AF_INET);
|
|
tmr_init(&m_zrtp_timer);
|
|
|
|
err |= mtx_init(&m_zrtp_mutex, mtx_plain) != thrd_success;
|
|
err |= mtx_init(&m_send_mutex, mtx_plain) != thrd_success;
|
|
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()
|
|
{
|
|
mem_deref(m_uh_rtp);
|
|
mem_deref(m_uh_rtcp);
|
|
|
|
stop();
|
|
|
|
delete m_zrtp;
|
|
|
|
mem_deref(m_rtpsock);
|
|
mem_deref(m_rtcpsock);
|
|
|
|
mtx_destroy(&m_zrtp_mutex);
|
|
mtx_destroy(&m_send_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_srtp && 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();
|
|
|
|
mtx_lock(&m_send_mutex);
|
|
delete m_send_srtp;
|
|
m_send_srtp = NULL;
|
|
mtx_unlock(&m_send_mutex);
|
|
|
|
delete m_recv_srtp;
|
|
m_recv_srtp = NULL;
|
|
|
|
debug("zrtp: Stream <%s> stopped\n", media_name());
|
|
}
|
|
|
|
|
|
int Stream::sdp_encode(struct sdp_media *sdpm)
|
|
{
|
|
(void)sdpm;
|
|
|
|
// NOTE: 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);
|
|
}
|
|
// NOTE: 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)
|
|
{
|
|
bool ret = false;
|
|
enum pkt_type ptype = get_packet_type(mb);
|
|
size_t len = mbuf_get_left(mb);
|
|
int rerr = 0;
|
|
(void)src;
|
|
|
|
mtx_lock(&m_send_mutex);
|
|
|
|
if (ptype == PKT_TYPE_RTCP && m_send_srtp && len > 8) {
|
|
|
|
rerr = m_send_srtp->protect_ctrl(mb);
|
|
}
|
|
else if (ptype == PKT_TYPE_RTP && m_send_srtp &&
|
|
len > RTP_HEADER_SIZE) {
|
|
|
|
rerr = m_send_srtp->protect(mb);
|
|
}
|
|
else
|
|
goto out;
|
|
|
|
if (rerr) {
|
|
warning("zrtp: protect/protect_ctrl failed (len=%u): %m\n",
|
|
len, rerr);
|
|
|
|
if (rerr == ENOMEM)
|
|
*err = rerr;
|
|
// drop
|
|
ret = true;
|
|
}
|
|
|
|
out:
|
|
mtx_unlock(&m_send_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
(void)src;
|
|
|
|
if (!started())
|
|
return false;
|
|
|
|
enum pkt_type ptype = get_packet_type(mb);
|
|
int err = 0;
|
|
|
|
if (ptype == PKT_TYPE_RTCP && m_recv_srtp) {
|
|
|
|
err = m_recv_srtp->unprotect_ctrl(mb);
|
|
|
|
m_srtcp_stat.update(err);
|
|
}
|
|
else if (ptype == PKT_TYPE_RTP && m_recv_srtp) {
|
|
|
|
err = m_recv_srtp->unprotect(mb);
|
|
|
|
m_srtp_stat.update(err);
|
|
|
|
if (!err) {
|
|
// 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 if (ptype == PKT_TYPE_ZRTP) {
|
|
return recv_zrtp(mb);
|
|
}
|
|
else
|
|
return false;
|
|
|
|
if (err)
|
|
// drop
|
|
return true;
|
|
|
|
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)
|
|
{
|
|
Srtp *s;
|
|
int err = 0;
|
|
|
|
debug("zrtp: Stream <%s>: secrets are ready for %s\n",
|
|
media_name(),
|
|
(part == ForSender)? "sender" : "receiver");
|
|
|
|
s = new Srtp(err, secrets, part);
|
|
if (!s || err) {
|
|
warning("zrtp: Stream <%s>: Srtp creation failed: %m\n",
|
|
media_name(), err);
|
|
delete s;
|
|
return false;
|
|
}
|
|
|
|
if (part == ForSender) {
|
|
mtx_lock(&m_send_mutex);
|
|
m_send_srtp = s;
|
|
mtx_unlock(&m_send_mutex);
|
|
}
|
|
else if (part == ForReceiver)
|
|
m_recv_srtp = s;
|
|
else
|
|
return false;
|
|
|
|
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) {
|
|
mtx_lock(&m_send_mutex);
|
|
delete m_send_srtp;
|
|
m_send_srtp = NULL;
|
|
mtx_unlock(&m_send_mutex);
|
|
}
|
|
|
|
if (part == ForReceiver) {
|
|
delete m_recv_srtp;
|
|
m_recv_srtp = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void Stream::srtpSecretsOn(std::string c, std::string s, bool verified)
|
|
{
|
|
m_sas = s;
|
|
m_ciphers = c;
|
|
char buf[128] = "";
|
|
|
|
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());
|
|
if (m_session->eventh) {
|
|
if (re_snprintf(buf, sizeof(buf), "%s,%d",
|
|
s.c_str(),
|
|
m_session->id()))
|
|
(m_session->eventh)
|
|
(MENC_EVENT_VERIFY_REQUEST,
|
|
buf,
|
|
NULL,
|
|
m_session->arg);
|
|
else
|
|
warning("zrtp: failed to print verify"
|
|
" arguments\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Stream::handleGoClear()
|
|
{
|
|
}
|
|
|
|
|
|
void Stream::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity,
|
|
int32_t subCode)
|
|
{
|
|
(void)severity;
|
|
(void)subCode;
|
|
}
|
|
|
|
|
|
void Stream::zrtpNotSuppOther()
|
|
{
|
|
}
|
|
|
|
|
|
void Stream::synchEnter()
|
|
{
|
|
mtx_lock(&m_zrtp_mutex);
|
|
}
|
|
|
|
|
|
void Stream::synchLeave()
|
|
{
|
|
mtx_unlock(&m_zrtp_mutex);
|
|
}
|
|
|
|
|
|
void Stream::zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info)
|
|
{
|
|
(void)info;
|
|
}
|
|
|
|
|
|
void Stream::zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info)
|
|
{
|
|
(void)info;
|
|
}
|
|
|
|
|
|
void Stream::signSAS(uint8_t* sasHash)
|
|
{
|
|
(void)sasHash;
|
|
}
|
|
|
|
|
|
bool Stream::checkSASSignature(uint8_t* sasHash)
|
|
{
|
|
(void)sasHash;
|
|
|
|
return true;
|
|
}
|
|
|