Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Implement XEP-0320: Use of DTLS-SRTP in Jingle Sessions #298

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/base/QXmppConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ const char* ns_message_correct = "urn:xmpp:message-correct:0";
const char* ns_mam = "urn:xmpp:mam:2";
// XEP-0319: Last User Interaction in Presence
const char* ns_idle = "urn:xmpp:idle:1";
// XEP-0320: Use of DTLS-SRTP in Jingle Sessions
const char* ns_jingle_dtls = "urn:xmpp:jingle:apps:dtls:0";
// XEP-0333: Chat Markers
const char* ns_chat_markers = "urn:xmpp:chat-markers:0";
// XEP-0334: Message Processing Hints
Expand Down
2 changes: 2 additions & 0 deletions src/base/QXmppConstants_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ extern const char* ns_message_correct;
extern const char* ns_mam;
// XEP-0319: Last User Interaction in Presence
extern const char* ns_idle;
// XEP-0320: Use of DTLS-SRTP in Jingle Sessions
extern const char* ns_jingle_dtls;
// XEP-0333: Char Markers
extern const char* ns_chat_markers;
// XEP-0334: Message Processing Hints:
Expand Down
1 change: 0 additions & 1 deletion src/base/QXmppJingleIq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
static const int RTP_COMPONENT = 1;

static const char *ns_jingle_rtp_info = "urn:xmpp:jingle:apps:rtp:info:1";
static const char *ns_jingle_dtls = "urn:xmpp:jingle:apps:dtls:0";

static const char *jingle_actions[] = {
"content-accept",
Expand Down
50 changes: 31 additions & 19 deletions src/client/QXmppCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,9 @@ void QXmppCallPrivate::padAdded(GstPad *pad)
if (nameParts.size() < 4) {
return;
}
if (nameParts[0] == QLatin1String("send") &&
nameParts[1] == QLatin1String("rtp") &&
if (nameParts[0] == QLatin1String("recv") ||
nameParts[1] == QLatin1String("rtp") ||
nameParts[2] == QLatin1String("src")) {
if (nameParts.size() != 4) {
return;
}
int sessionId = nameParts[3].toInt();
auto stream = findStreamById(sessionId);
stream->d->addRtpSender(pad);
} else if (nameParts[0] == QLatin1String("recv") ||
nameParts[1] == QLatin1String("rtp") ||
nameParts[2] == QLatin1String("src")) {
if (nameParts.size() != 6) {
return;
}
Expand Down Expand Up @@ -331,6 +322,10 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq)
return;
}

if (useDtls && content.transportFingerprintSetup() == QLatin1String("passive")) {
stream->d->toDtlsClientMode();
}

// check for call establishment
setState(QXmppCall::ActiveState);

Expand Down Expand Up @@ -401,7 +396,8 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq)
iq.setType(QXmppIq::Set);
iq.setAction(QXmppJingleIq::ContentAccept);
iq.setSid(q->sid());
iq.addContent(localContent(stream));
iq.addContent(localContent(stream, QLatin1String("active")));
stream->d->toDtlsClientMode();
sendRequest(iq);

} else if (iq.action() == QXmppJingleIq::TransportInfo) {
Expand Down Expand Up @@ -435,7 +431,7 @@ QXmppCallStream *QXmppCallPrivate::createStream(const QString &media, const QStr
return nullptr;
}

QXmppCallStream *stream = new QXmppCallStream(pipeline, rtpbin, media, creator, name, ++nextId);
QXmppCallStream *stream = new QXmppCallStream(pipeline, rtpbin, media, creator, name, ++nextId, useDtls);

// Fill local payload payload types
auto &codecs = media == AUDIO_MEDIA ? audioCodecs : videoCodecs;
Expand Down Expand Up @@ -470,7 +466,7 @@ QXmppCallStream *QXmppCallPrivate::createStream(const QString &media, const QStr
return stream;
}

QXmppJingleIq::Content QXmppCallPrivate::localContent(QXmppCallStream *stream) const
QXmppJingleIq::Content QXmppCallPrivate::localContent(QXmppCallStream *stream, QString dtlsSetup) const
{
QXmppJingleIq::Content content;
content.setCreator(stream->creator());
Expand All @@ -487,6 +483,13 @@ QXmppJingleIq::Content QXmppCallPrivate::localContent(QXmppCallStream *stream) c
content.setTransportPassword(stream->d->connection->localPassword());
content.setTransportCandidates(stream->d->connection->localCandidates());

// encryption
if (useDtls) {
content.setTransportFingerprint(stream->d->digest);
content.setTransportFingerprintHash("sha-256");
content.setTransportFingerprintSetup(dtlsSetup);
}

return content;
}

Expand Down Expand Up @@ -514,7 +517,7 @@ bool QXmppCallPrivate::sendInvite()
iq.setAction(QXmppJingleIq::SessionInitiate);
iq.setInitiator(ownJid);
iq.setSid(sid);
iq.addContent(localContent(stream));
iq.addContent(localContent(stream, QLatin1String("actpass")));
return sendRequest(iq);
}

Expand Down Expand Up @@ -562,13 +565,14 @@ void QXmppCallPrivate::terminate(QXmppJingleIq::Reason::Type reasonType)
QTimer::singleShot(5000, q, SLOT(terminated()));
}

QXmppCall::QXmppCall(const QString &jid, QXmppCall::Direction direction, QXmppCallManager *parent)
QXmppCall::QXmppCall(const QString &jid, QXmppCall::Direction direction, bool useDtls, QXmppCallManager *parent)
: QXmppLoggable(parent)
{
d = new QXmppCallPrivate(this);
d->direction = direction;
d->jid = jid;
d->ownJid = parent->client()->configuration().jid();
d->useDtls = true;
d->manager = parent;
}

Expand All @@ -593,7 +597,8 @@ void QXmppCall::accept()
iq.setAction(QXmppJingleIq::SessionAccept);
iq.setResponder(d->ownJid);
iq.setSid(d->sid);
iq.addContent(d->localContent(stream));
iq.addContent(d->localContent(stream, QLatin1String("active")));
stream->d->toDtlsClientMode();
d->sendRequest(iq);

// notify user
Expand Down Expand Up @@ -680,7 +685,7 @@ void QXmppCall::localCandidatesChanged()
iq.setType(QXmppIq::Set);
iq.setAction(QXmppJingleIq::TransportInfo);
iq.setSid(d->sid);
iq.addContent(d->localContent(stream));
iq.addContent(d->localContent(stream, QLatin1String()));
d->sendRequest(iq);
}

Expand Down Expand Up @@ -734,6 +739,13 @@ void QXmppCall::addVideo()
iq.setType(QXmppIq::Set);
iq.setAction(QXmppJingleIq::ContentAdd);
iq.setSid(d->sid);
iq.addContent(d->localContent(stream));
iq.addContent(d->localContent(stream, QLatin1String("actpass")));
d->sendRequest(iq);
}

// Returns if the call is encrypted

bool QXmppCall::isEncrypted() const
{
return d->useDtls;
}
4 changes: 3 additions & 1 deletion src/client/QXmppCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class QXMPP_EXPORT QXmppCall : public QXmppLoggable
QXmppCallStream *audioStream() const;
QXmppCallStream *videoStream() const;

bool isEncrypted() const;

signals:
/// \brief This signal is emitted when a call is connected.
///
Expand Down Expand Up @@ -108,7 +110,7 @@ private slots:
void terminated();

private:
QXmppCall(const QString &jid, QXmppCall::Direction direction, QXmppCallManager *parent);
QXmppCall(const QString &jid, QXmppCall::Direction direction, bool useDtls, QXmppCallManager *parent);

QXmppCallPrivate *d;
friend class QXmppCallManager;
Expand Down
45 changes: 40 additions & 5 deletions src/client/QXmppCallManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "QXmppClient.h"
#include "QXmppConstants_p.h"
#include "QXmppJingleIq.h"
#include "QXmppRosterManager.h"
#include "QXmppStun.h"
#include "QXmppUtils.h"

Expand All @@ -43,6 +44,21 @@ QXmppCallManagerPrivate::QXmppCallManagerPrivate(QXmppCallManager *qq)
{
// Initialize GStreamer
gst_init(nullptr, nullptr);

supportsDtls = true;
GstElementFactory *factory;
factory = gst_element_factory_find("dtlssrtpenc");
if (!factory) {
supportsDtls = false;
} else {
g_object_unref(factory);
}
factory = gst_element_factory_find("dtlssrtpdec");
if (!factory) {
supportsDtls = false;
} else {
g_object_unref(factory);
}
}

QXmppCall *QXmppCallManagerPrivate::findCall(const QString &sid) const
Expand Down Expand Up @@ -80,12 +96,17 @@ QXmppCallManager::~QXmppCallManager()
/// \cond
QStringList QXmppCallManager::discoveryFeatures() const
{
return QStringList()
QStringList features;
features
<< ns_jingle // XEP-0166 : Jingle
<< ns_jingle_rtp // XEP-0167 : Jingle RTP Sessions
<< ns_jingle_rtp_audio
<< ns_jingle_rtp_video
<< ns_jingle_ice_udp; // XEP-0176 : Jingle ICE-UDP Transport Method
if (d->supportsDtls) {
features << ns_jingle_dtls; // XEP-0320: Use of DTLS-SRTP in Jingle Sessions
}
return features;
}

bool QXmppCallManager::handleStanza(const QDomElement &element)
Expand Down Expand Up @@ -136,7 +157,12 @@ QXmppCall *QXmppCallManager::call(const QString &jid)
return nullptr;
}

QXmppCall *call = new QXmppCall(jid, QXmppCall::OutgoingDirection, this);
/* Determine support for XEP-0320: Use of DTLS-SRTP in Jingle Sessions */
QXmppRosterManager *rosterManager = client()->findExtension<QXmppRosterManager>();
QXmppPresence presence = rosterManager->getPresence(QXmppUtils::jidToBareJid(jid), QXmppUtils::jidToResource(jid));
bool remoteSupportsDtls = presence.capabilityExt().contains(ns_jingle_dtls);

QXmppCall *call = new QXmppCall(jid, QXmppCall::OutgoingDirection, remoteSupportsDtls && d->supportsDtls, this);
QXmppCallStream *stream = call->d->createStream("audio", "initiator", "microphone");
call->d->streams << stream;
call->d->sid = QXmppUtils::generateStanzaHash();
Expand Down Expand Up @@ -247,14 +273,23 @@ void QXmppCallManager::_q_jingleIqReceived(const QXmppJingleIq &iq)
return;

if (iq.action() == QXmppJingleIq::SessionInitiate) {
const QXmppJingleIq::Content content = iq.contents().isEmpty() ? QXmppJingleIq::Content() : iq.contents().first();

// build call
QXmppCall *call = new QXmppCall(iq.from(), QXmppCall::IncomingDirection, this);
bool useDtls = !content.transportFingerprint().isEmpty();
QXmppCall *call = new QXmppCall(iq.from(), QXmppCall::IncomingDirection, useDtls, this);
call->d->sid = iq.sid();

const QXmppJingleIq::Content content = iq.contents().isEmpty() ? QXmppJingleIq::Content() : iq.contents().first();
if (useDtls && !d->supportsDtls) {
call->d->terminate(QXmppJingleIq::Reason::FailedApplication);
return;
}

QXmppCallStream *stream = call->d->createStream(content.descriptionMedia(), content.creator(), content.name());
if (!stream)
if (!stream) {
call->d->terminate(QXmppJingleIq::Reason::FailedApplication);
return;
}
call->d->streams << stream;

// send ack
Expand Down
2 changes: 2 additions & 0 deletions src/client/QXmppCallManager_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class QXmppCallManagerPrivate
QString turnUser;
QString turnPassword;

bool supportsDtls;

private:
QXmppCallManager *q;
};
Expand Down
Loading