aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelvin Keskin <melvo@olomono.de>2022-10-03 13:14:32 +0200
committerGitHub <noreply@github.com>2022-10-03 13:14:32 +0200
commitb020af2439342e3f748ecdcad6d4db4d5a8a2880 (patch)
tree7d478009ac9d76cce201d9d6ec6b11ec0f351aa8
parentcdf9984a6543f29b7a307a5a45e3e10d0fb28db4 (diff)
downloadqxmpp-b020af2439342e3f748ecdcad6d4db4d5a8a2880.tar.gz
Implement XEP-0167: Jingle RTP Sessions SRTP negotiation (#487)
-rw-r--r--doc/doap.xml2
-rw-r--r--src/base/QXmppJingleIq.cpp302
-rw-r--r--src/base/QXmppJingleIq.h59
-rw-r--r--tests/qxmppjingleiq/tst_qxmppjingleiq.cpp170
4 files changed, 532 insertions, 1 deletions
diff --git a/doc/doap.xml b/doc/doap.xml
index b3d2c4e0..a21293d4 100644
--- a/doc/doap.xml
+++ b/doc/doap.xml
@@ -285,7 +285,7 @@ SPDX-License-Identifier: CC0-1.0
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.2</xmpp:version>
<xmpp:since>0.2</xmpp:since>
- <xmpp:note>'rtcp-mux' of https://xmpp.org/extensions/xep-0167.html#format, informational messages for session states 'active', 'hold', 'unhold', 'mute', 'unmute' since 1.5; Missing 'encryption' of https://xmpp.org/extensions/xep-0167.html#srtp</xmpp:note>
+ <xmpp:note>Multiplexing, encryption via SRTP, informational messages for session states 'active', 'hold', 'unhold', 'mute', 'unmute' since 1.5</xmpp:note>
</xmpp:SupportedXep>
</implements>
<implements>
diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp
index fc198278..a89f87a6 100644
--- a/src/base/QXmppJingleIq.cpp
+++ b/src/base/QXmppJingleIq.cpp
@@ -240,6 +240,9 @@ public:
QList<QXmppJinglePayloadType> payloadTypes;
QList<QXmppJingleCandidate> transportCandidates;
+ // XEP-0167: Jingle RTP Sessions
+ std::optional<QXmppJingleRtpEncryption> rtpEncryption;
+
// XEP-0293: Jingle RTP Feedback Negotiation
QVector<QXmppJingleRtpFeedbackProperty> rtpFeedbackProperties;
QVector<QXmppJingleRtpFeedbackInterval> rtpFeedbackIntervals;
@@ -418,6 +421,31 @@ void QXmppJingleIq::Content::setRtpMultiplexingSupported(bool isRtpMultiplexingS
d->isRtpMultiplexingSupported = isRtpMultiplexingSupported;
}
+///
+/// Returns the encryption used for SRTP negotiation as specified by
+/// \xep{0167, Jingle RTP Sessions}.
+///
+/// \return the RTP encryption via SRTP
+///
+/// \since QXmpp 1.5
+///
+std::optional<QXmppJingleRtpEncryption> QXmppJingleIq::Content::rtpEncryption() const
+{
+ return d->rtpEncryption;
+}
+
+///
+/// Sets the encryption used for SRTP negotiation as specified by \xep{0167, Jingle RTP Sessions}.
+///
+/// \param rtpEncryption RTP encryption via SRTP
+///
+/// \since QXmpp 1.5
+///
+void QXmppJingleIq::Content::setRtpEncryption(const std::optional<QXmppJingleRtpEncryption> &rtpEncryption)
+{
+ d->rtpEncryption = rtpEncryption;
+}
+
void QXmppJingleIq::Content::addPayloadType(const QXmppJinglePayloadType &payload)
{
d->descriptionType = ns_jingle_rtp;
@@ -664,6 +692,17 @@ void QXmppJingleIq::Content::parse(const QDomElement &element)
d->descriptionSsrc = descriptionElement.attribute(QStringLiteral("ssrc")).toULong();
d->isRtpMultiplexingSupported = !descriptionElement.firstChildElement(QStringLiteral("rtcp-mux")).isNull();
+ for (auto childElement = descriptionElement.firstChildElement();
+ !childElement.isNull();
+ childElement = childElement.nextSiblingElement()) {
+ if (QXmppJingleRtpEncryption::isJingleRtpEncryption(childElement)) {
+ QXmppJingleRtpEncryption encryption;
+ encryption.parse(childElement);
+ d->rtpEncryption = encryption;
+ break;
+ }
+ }
+
parseJingleRtpFeedbackNegotiationElements(descriptionElement, d->rtpFeedbackProperties, d->rtpFeedbackIntervals);
parseJingleRtpHeaderExtensionsNegotiationElements(descriptionElement, d->rtpHeaderExtensionProperties, d->isRtpHeaderExtensionMixingAllowed);
@@ -723,6 +762,10 @@ void QXmppJingleIq::Content::toXml(QXmlStreamWriter *writer) const
writer->writeEmptyElement(QStringLiteral("rtcp-mux"));
}
+ if (d->rtpEncryption) {
+ d->rtpEncryption->toXml(writer);
+ }
+
jingleRtpFeedbackNegotiationElementsToXml(writer, d->rtpFeedbackProperties, d->rtpFeedbackIntervals);
jingleRtpHeaderExtensionsNegotiationElementsToXml(writer, d->rtpHeaderExtensionProperties, d->isRtpHeaderExtensionMixingAllowed);
@@ -2115,6 +2158,265 @@ bool QXmppSdpParameter::isSdpParameter(const QDomElement &element)
return element.tagName() == QStringLiteral("parameter");
}
+class QXmppJingleRtpCryptoElementPrivate : public QSharedData
+{
+public:
+ uint32_t tag = 0;
+ QString cryptoSuite;
+ QString keyParams;
+ QString sessionParams;
+};
+
+///
+/// \class QXmppJingleRtpCryptoElement
+///
+/// \brief The QXmppJingleRtpCryptoElement class represents the \xep{0167: Jingle RTP Sessions}
+/// "crypto" element used for SRTP negotiation.
+///
+/// \since QXmpp 1.5
+///
+
+///
+/// Constructs a Jingle RTP crypto element.
+///
+QXmppJingleRtpCryptoElement::QXmppJingleRtpCryptoElement()
+ : d(new QXmppJingleRtpCryptoElementPrivate())
+{
+}
+
+QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppJingleRtpCryptoElement)
+
+///
+/// Returns the tag used as an identifier for the crypto element.
+///
+/// \return the identifying tag
+///
+uint32_t QXmppJingleRtpCryptoElement::tag() const
+{
+ return d->tag;
+}
+
+///
+/// Sets the tag used as an identifier for the crypto element.
+///
+/// \param tag identifying tag
+///
+void QXmppJingleRtpCryptoElement::setTag(uint32_t tag)
+{
+ d->tag = tag;
+}
+
+///
+/// Returns the crypto suite used as an identifier for describing the encryption and authentication
+/// algorithms.
+///
+/// \return the identifying crypto suite
+///
+QString QXmppJingleRtpCryptoElement::cryptoSuite() const
+{
+ return d->cryptoSuite;
+}
+
+///
+/// Sets the crypto suite used as an identifier for describing the encryption and authentication
+/// algorithms.
+///
+/// \param cryptoSuite identifying crypto suite
+///
+void QXmppJingleRtpCryptoElement::setCryptoSuite(const QString &cryptoSuite)
+{
+ d->cryptoSuite = cryptoSuite;
+}
+
+///
+/// Returns the key parameters providing one or more sets of keying material for the crypto suite.
+///
+/// \return the key parameters providing one or more sets of keying material
+///
+QString QXmppJingleRtpCryptoElement::keyParams() const
+{
+ return d->keyParams;
+}
+
+///
+/// Sets the key parameters providing one or more sets of keying material for the crypto suite.
+///
+/// \param keyParams key parameters providing one or more sets of keying material
+///
+void QXmppJingleRtpCryptoElement::setKeyParams(const QString &keyParams)
+{
+ d->keyParams = keyParams;
+}
+
+///
+/// Returns the session parameters providing transport-specific data.
+///
+/// \return the session parameters providing transport-specific data
+///
+QString QXmppJingleRtpCryptoElement::sessionParams() const
+{
+ return d->sessionParams;
+}
+
+///
+/// Sets the session parameters providing transport-specific data.
+///
+/// \param sessionParams session parameters providing transport-specific data
+///
+void QXmppJingleRtpCryptoElement::setSessionParams(const QString &sessionParams)
+{
+ d->sessionParams = sessionParams;
+}
+
+/// \cond
+void QXmppJingleRtpCryptoElement::parse(const QDomElement &element)
+{
+ d->tag = element.attribute(QStringLiteral("tag")).toUInt();
+ d->cryptoSuite = element.attribute(QStringLiteral("crypto-suite"));
+ d->keyParams = element.attribute(QStringLiteral("key-params"));
+ d->sessionParams = element.attribute(QStringLiteral("session-params"));
+}
+
+void QXmppJingleRtpCryptoElement::toXml(QXmlStreamWriter *writer) const
+{
+ if (!d->cryptoSuite.isEmpty() && !d->keyParams.isEmpty()) {
+ writer->writeStartElement(QStringLiteral("crypto"));
+ writer->writeAttribute(QStringLiteral("tag"), QString::number(d->tag));
+ writer->writeAttribute(QStringLiteral("crypto-suite"), d->cryptoSuite);
+ writer->writeAttribute(QStringLiteral("key-params"), d->keyParams);
+ helperToXmlAddAttribute(writer, QStringLiteral("session-params"), d->sessionParams);
+ writer->writeEndElement();
+ }
+}
+/// \endcond
+
+///
+/// Determines whether the given DOM element is an RTP crypto element.
+///
+/// \param element DOM element being checked
+///
+/// \return whether element is an RTP crypto element
+///
+bool QXmppJingleRtpCryptoElement::isJingleRtpCryptoElement(const QDomElement &element)
+{
+ return element.tagName() == QStringLiteral("crypto");
+}
+
+class QXmppJingleRtpEncryptionPrivate : public QSharedData
+{
+public:
+ bool isRequired = false;
+ QVector<QXmppJingleRtpCryptoElement> cryptoElements;
+};
+
+///
+/// \class QXmppJingleRtpEncryption
+///
+/// \brief The QXmppJingleRtpEncryption class represents the \xep{0167: Jingle RTP Sessions}
+/// "encryption" element used for SRTP negotiation.
+///
+/// \since QXmpp 1.5
+///
+
+///
+/// Constructs a Jingle RTP encryption.
+///
+QXmppJingleRtpEncryption::QXmppJingleRtpEncryption()
+ : d(new QXmppJingleRtpEncryptionPrivate())
+{
+}
+
+QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppJingleRtpEncryption)
+
+///
+/// Returns whether encryption via SRTP is required.
+///
+/// \return whether encryption is required
+///
+bool QXmppJingleRtpEncryption::isRequired() const
+{
+ return d->isRequired;
+}
+
+///
+/// Sets whether encryption via SRTP is required.
+///
+/// \param isRequired whether encryption is required
+///
+void QXmppJingleRtpEncryption::setRequired(bool isRequired)
+{
+ d->isRequired = isRequired;
+}
+
+///
+/// Returns the crypto elements used for encryption via SRTP.
+///
+/// \return the crypto elements
+///
+QVector<QXmppJingleRtpCryptoElement> QXmppJingleRtpEncryption::cryptoElements() const
+{
+ return d->cryptoElements;
+}
+
+///
+/// Sets the crypto elements used for encryption via SRTP.
+///
+/// \param cryptoElements the crypto elements
+///
+void QXmppJingleRtpEncryption::setCryptoElements(const QVector<QXmppJingleRtpCryptoElement> &cryptoElements)
+{
+ d->cryptoElements = cryptoElements;
+}
+
+/// \cond
+void QXmppJingleRtpEncryption::parse(const QDomElement &element)
+{
+ d->isRequired = element.attribute(QStringLiteral("required")) == QStringLiteral("true") ||
+ element.attribute(QStringLiteral("required")) == QStringLiteral("1");
+
+ for (auto childElement = element.firstChildElement();
+ !childElement.isNull();
+ childElement = childElement.nextSiblingElement()) {
+ if (QXmppJingleRtpCryptoElement::isJingleRtpCryptoElement(childElement)) {
+ QXmppJingleRtpCryptoElement cryptoElement;
+ cryptoElement.parse(childElement);
+ d->cryptoElements.append(std::move(cryptoElement));
+ }
+ }
+}
+
+void QXmppJingleRtpEncryption::toXml(QXmlStreamWriter *writer) const
+{
+ if (!d->cryptoElements.isEmpty()) {
+ writer->writeStartElement(QStringLiteral("encryption"));
+ writer->writeDefaultNamespace(ns_jingle_rtp);
+
+ if (d->isRequired) {
+ writer->writeAttribute(QStringLiteral("required"), QStringLiteral("1"));
+ }
+
+ for (const auto &cryptoElement : std::as_const(d->cryptoElements)) {
+ cryptoElement.toXml(writer);
+ }
+
+ writer->writeEndElement();
+ }
+}
+/// \endcond
+
+///
+/// Determines whether the given DOM element is an RTP encryption element.
+///
+/// \param element DOM element being checked
+///
+/// \return whether element is an RTP encryption element
+///
+bool QXmppJingleRtpEncryption::isJingleRtpEncryption(const QDomElement &element)
+{
+ return element.tagName() == QStringLiteral("encryption") &&
+ element.namespaceURI() == ns_jingle_rtp;
+}
+
class QXmppJingleRtpFeedbackPropertyPrivate : public QSharedData
{
public:
diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h
index 5d8de1f8..f5da136c 100644
--- a/src/base/QXmppJingleIq.h
+++ b/src/base/QXmppJingleIq.h
@@ -16,6 +16,8 @@ class QXmppJingleCandidatePrivate;
class QXmppJingleIqContentPrivate;
class QXmppJingleIqPrivate;
class QXmppJinglePayloadTypePrivate;
+class QXmppJingleRtpCryptoElementPrivate;
+class QXmppJingleRtpEncryptionPrivate;
class QXmppJingleRtpFeedbackPropertyPrivate;
class QXmppJingleRtpHeaderExtensionPropertyPrivate;
class QXmppSdpParameterPrivate;
@@ -44,6 +46,60 @@ private:
QSharedDataPointer<QXmppSdpParameterPrivate> d;
};
+class QXMPP_EXPORT QXmppJingleRtpCryptoElement
+{
+public:
+ QXmppJingleRtpCryptoElement();
+
+ QXMPP_PRIVATE_DECLARE_RULE_OF_SIX(QXmppJingleRtpCryptoElement)
+
+ uint32_t tag() const;
+ void setTag(uint32_t tag);
+
+ QString cryptoSuite() const;
+ void setCryptoSuite(const QString &cryptoSuite);
+
+ QString keyParams() const;
+ void setKeyParams(const QString &keyParams);
+
+ QString sessionParams() const;
+ void setSessionParams(const QString &sessionParams);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ static bool isJingleRtpCryptoElement(const QDomElement &element);
+
+private:
+ QSharedDataPointer<QXmppJingleRtpCryptoElementPrivate> d;
+};
+
+class QXMPP_EXPORT QXmppJingleRtpEncryption
+{
+public:
+ QXmppJingleRtpEncryption();
+
+ QXMPP_PRIVATE_DECLARE_RULE_OF_SIX(QXmppJingleRtpEncryption)
+
+ bool isRequired() const;
+ void setRequired(bool isRequired);
+
+ QVector<QXmppJingleRtpCryptoElement> cryptoElements() const;
+ void setCryptoElements(const QVector<QXmppJingleRtpCryptoElement> &cryptoElements);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ static bool isJingleRtpEncryption(const QDomElement &element);
+
+private:
+ QSharedDataPointer<QXmppJingleRtpEncryptionPrivate> d;
+};
+
class QXMPP_EXPORT QXmppJingleRtpFeedbackProperty
{
public:
@@ -350,6 +406,9 @@ public:
bool isRtpMultiplexingSupported() const;
void setRtpMultiplexingSupported(bool isRtpMultiplexingSupported);
+ std::optional<QXmppJingleRtpEncryption> rtpEncryption() const;
+ void setRtpEncryption(const std::optional<QXmppJingleRtpEncryption> &rtpEncryption);
+
void addPayloadType(const QXmppJinglePayloadType &payload);
QList<QXmppJinglePayloadType> payloadTypes() const;
void setPayloadTypes(const QList<QXmppJinglePayloadType> &payloadTypes);
diff --git a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp
index b2d74c83..c291bbde 100644
--- a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp
+++ b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp
@@ -17,6 +17,10 @@ private slots:
void testIsSdpParameter();
void testSdpParameter();
void testSdpParameterWithoutValue();
+ void testIsRtpEncryption_data();
+ void testIsRtpEncryption();
+ void testRtpEncryption_data();
+ void testRtpEncryption();
void testIsRtpFeedbackProperty_data();
void testIsRtpFeedbackProperty();
void testRtpFeedbackProperty();
@@ -109,6 +113,156 @@ void tst_QXmppJingleIq::testSdpParameterWithoutValue()
serializePacket(parameter2, xml);
}
+void tst_QXmppJingleIq::testIsRtpEncryption_data()
+{
+ QTest::addColumn<QByteArray>("xml");
+ QTest::addColumn<bool>("isValid");
+
+ QTest::newRow("valid")
+ << QByteArrayLiteral("<encryption xmlns=\"urn:xmpp:jingle:apps:rtp:1\"/>")
+ << true;
+ QTest::newRow("invalidTag")
+ << QByteArrayLiteral("<invalid xmlns=\"urn:xmpp:jingle:apps:rtp:1\"/>")
+ << false;
+ QTest::newRow("invalidNamespace")
+ << QByteArrayLiteral("<encryption xmlns=\"invalid\"/>")
+ << false;
+}
+
+void tst_QXmppJingleIq::testIsRtpEncryption()
+{
+ QFETCH(QByteArray, xml);
+ QFETCH(bool, isValid);
+
+ QCOMPARE(QXmppJingleRtpEncryption::isJingleRtpEncryption(xmlToDom(xml)), isValid);
+}
+
+void tst_QXmppJingleIq::testRtpEncryption_data()
+{
+ QTest::addColumn<QByteArray>("xml");
+ QTest::addColumn<bool>("isRequired");
+ QTest::addColumn<bool>("hasSessionParams");
+ QTest::addColumn<int>("cryptoElementCount");
+
+ QTest::newRow("required")
+ << QByteArrayLiteral("<encryption xmlns=\"urn:xmpp:jingle:apps:rtp:1\" required=\"1\">"
+ "<crypto"
+ " tag=\"1\""
+ " crypto-suite=\"AES_CM_128_HMAC_SHA1_80\""
+ " key-params=\"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32\"/>"
+ "</encryption>")
+ << true
+ << false
+ << 1;
+ QTest::newRow("optional")
+ << QByteArrayLiteral("<encryption xmlns=\"urn:xmpp:jingle:apps:rtp:1\">"
+ "<crypto"
+ " tag=\"1\""
+ " crypto-suite=\"AES_CM_128_HMAC_SHA1_80\""
+ " key-params=\"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32\"/>"
+ "</encryption>")
+ << false
+ << false
+ << 1;
+ QTest::newRow("optionalWithSessionParams")
+ << QByteArrayLiteral("<encryption xmlns=\"urn:xmpp:jingle:apps:rtp:1\">"
+ "<crypto"
+ " tag=\"1\""
+ " crypto-suite=\"AES_CM_128_HMAC_SHA1_80\""
+ " key-params=\"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32\""
+ " session-params=\"KDR=1 UNENCRYPTED_SRTCP\"/>"
+ "</encryption>")
+ << false
+ << true
+ << 1;
+ QTest::newRow("optionalWithMultipleCryptoElements")
+ << QByteArrayLiteral("<encryption xmlns=\"urn:xmpp:jingle:apps:rtp:1\">"
+ "<crypto"
+ " tag=\"1\""
+ " crypto-suite=\"AES_CM_128_HMAC_SHA1_80\""
+ " key-params=\"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32\"/>"
+ "<crypto"
+ " tag=\"2\""
+ " crypto-suite=\"AES_CM_128_HMAC_SHA1_80\""
+ " key-params=\"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32\"/>"
+ "</encryption>")
+ << false
+ << false
+ << 2;
+}
+
+void tst_QXmppJingleIq::testRtpEncryption()
+{
+ QFETCH(QByteArray, xml);
+ QFETCH(bool, isRequired);
+ QFETCH(bool, hasSessionParams);
+ QFETCH(int, cryptoElementCount);
+
+ QXmppJingleRtpEncryption rtpEncryption1;
+ QVERIFY(!rtpEncryption1.isRequired());
+ QVERIFY(rtpEncryption1.cryptoElements().isEmpty());
+ parsePacket(rtpEncryption1, xml);
+
+ QCOMPARE(rtpEncryption1.isRequired(), isRequired);
+ QCOMPARE(rtpEncryption1.cryptoElements().size(), cryptoElementCount);
+
+ const auto rtpCryptoElement1 = rtpEncryption1.cryptoElements().at(0);
+ QCOMPARE(rtpCryptoElement1.tag(), 1);
+ QCOMPARE(rtpCryptoElement1.cryptoSuite(), QStringLiteral("AES_CM_128_HMAC_SHA1_80"));
+ QCOMPARE(rtpCryptoElement1.keyParams(), QStringLiteral("inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32"));
+ if (hasSessionParams) {
+ QCOMPARE(rtpCryptoElement1.sessionParams(), QStringLiteral("KDR=1 UNENCRYPTED_SRTCP"));
+ } else {
+ QVERIFY(rtpCryptoElement1.sessionParams().isEmpty());
+ }
+
+ if (cryptoElementCount == 2) {
+ QCOMPARE(rtpEncryption1.cryptoElements().at(1).tag(), 2);
+ }
+
+ serializePacket(rtpEncryption1, xml);
+
+ QXmppJingleRtpCryptoElement rtpCryptoElement2;
+ rtpCryptoElement2.setTag(1);
+ rtpCryptoElement2.setCryptoSuite(QStringLiteral("AES_CM_128_HMAC_SHA1_80"));
+ rtpCryptoElement2.setKeyParams(QStringLiteral("inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32"));
+
+ if (hasSessionParams) {
+ rtpCryptoElement2.setSessionParams(QStringLiteral("KDR=1 UNENCRYPTED_SRTCP"));
+ }
+
+ QXmppJingleRtpEncryption rtpEncryption2;
+ rtpEncryption2.setRequired(isRequired);
+
+ if (cryptoElementCount == 2) {
+ auto rtpCryptoElement3 = rtpCryptoElement2;
+ rtpCryptoElement3.setTag(2);
+
+ rtpEncryption2.setCryptoElements({ rtpCryptoElement2, rtpCryptoElement3 });
+ } else {
+ rtpEncryption2.setCryptoElements({ rtpCryptoElement2 });
+ }
+
+ QCOMPARE(rtpEncryption2.isRequired(), isRequired);
+ QCOMPARE(rtpEncryption2.cryptoElements().size(), cryptoElementCount);
+
+ const auto rtpCryptoElement4 = rtpEncryption2.cryptoElements().at(0);
+ QCOMPARE(rtpCryptoElement4.tag(), 1);
+ QCOMPARE(rtpCryptoElement4.cryptoSuite(), QStringLiteral("AES_CM_128_HMAC_SHA1_80"));
+ QCOMPARE(rtpCryptoElement4.keyParams(), QStringLiteral("inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32"));
+ if (hasSessionParams) {
+ QCOMPARE(rtpCryptoElement4.sessionParams(), QStringLiteral("KDR=1 UNENCRYPTED_SRTCP"));
+ } else {
+ QVERIFY(rtpCryptoElement4.sessionParams().isEmpty());
+ }
+
+ if (cryptoElementCount == 2) {
+ QCOMPARE(rtpEncryption2.cryptoElements().at(1).tag(), 2);
+ }
+
+ serializePacket(rtpEncryption2, xml);
+}
+
void tst_QXmppJingleIq::testIsRtpFeedbackProperty_data()
{
QTest::addColumn<QByteArray>("xml");
@@ -380,6 +534,12 @@ void tst_QXmppJingleIq::testContent()
"<content creator=\"initiator\" name=\"voice\">"
"<description xmlns=\"urn:xmpp:jingle:apps:rtp:1\" media=\"audio\">"
"<rtcp-mux/>"
+ "<encryption xmlns=\"urn:xmpp:jingle:apps:rtp:1\">"
+ "<crypto"
+ " tag=\"1\""
+ " crypto-suite=\"AES_CM_128_HMAC_SHA1_80\""
+ " key-params=\"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32\"/>"
+ "</encryption>"
"<payload-type id=\"96\"/>"
"<payload-type id=\"97\"/>"
"</description>"
@@ -409,6 +569,7 @@ void tst_QXmppJingleIq::testContent()
QVERIFY(content1.descriptionMedia().isEmpty());
QCOMPARE(content1.descriptionSsrc(), quint32(0));
QVERIFY(!content1.isRtpMultiplexingSupported());
+ QVERIFY(!content1.rtpEncryption());
QCOMPARE(content1.payloadTypes().size(), 0);
QVERIFY(content1.transportUser().isEmpty());
QVERIFY(content1.transportPassword().isEmpty());
@@ -420,6 +581,7 @@ void tst_QXmppJingleIq::testContent()
QCOMPARE(content1.descriptionMedia(), QStringLiteral("audio"));
QCOMPARE(content1.descriptionSsrc(), quint32(0));
QVERIFY(content1.isRtpMultiplexingSupported());
+ QVERIFY(content1.rtpEncryption());
QCOMPARE(content1.payloadTypes().size(), 2);
QCOMPARE(content1.payloadTypes().at(0).id(), quint8(96));
QCOMPARE(content1.payloadTypes().at(1).id(), quint8(97));
@@ -436,6 +598,13 @@ void tst_QXmppJingleIq::testContent()
content2.setDescriptionMedia(QStringLiteral("audio"));
content2.setDescriptionSsrc(quint32(0));
content2.setRtpMultiplexingSupported(true);
+ QXmppJingleRtpCryptoElement rtpCryptoElement;
+ rtpCryptoElement.setTag(1);
+ rtpCryptoElement.setCryptoSuite(QStringLiteral("AES_CM_128_HMAC_SHA1_80"));
+ rtpCryptoElement.setKeyParams(QStringLiteral("inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:32"));
+ QXmppJingleRtpEncryption rtpEncryption;
+ rtpEncryption.setCryptoElements({ rtpCryptoElement });
+ content2.setRtpEncryption(rtpEncryption);
QXmppJinglePayloadType payloadType1;
payloadType1.setId(quint8(96));
content2.setPayloadTypes({ payloadType1 });
@@ -456,6 +625,7 @@ void tst_QXmppJingleIq::testContent()
QCOMPARE(content2.descriptionMedia(), QStringLiteral("audio"));
QCOMPARE(content2.descriptionSsrc(), quint32(0));
QVERIFY(content2.isRtpMultiplexingSupported());
+ QVERIFY(content2.rtpEncryption());
QCOMPARE(content2.payloadTypes().size(), 2);
QCOMPARE(content2.payloadTypes().at(0).id(), quint8(96));
QCOMPARE(content2.payloadTypes().at(1).id(), quint8(97));