diff options
| author | Melvin Keskin <melvo@olomono.de> | 2022-10-03 13:14:32 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-03 13:14:32 +0200 |
| commit | b020af2439342e3f748ecdcad6d4db4d5a8a2880 (patch) | |
| tree | 7d478009ac9d76cce201d9d6ec6b11ec0f351aa8 | |
| parent | cdf9984a6543f29b7a307a5a45e3e10d0fb28db4 (diff) | |
| download | qxmpp-b020af2439342e3f748ecdcad6d4db4d5a8a2880.tar.gz | |
Implement XEP-0167: Jingle RTP Sessions SRTP negotiation (#487)
| -rw-r--r-- | doc/doap.xml | 2 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.cpp | 302 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.h | 59 | ||||
| -rw-r--r-- | tests/qxmppjingleiq/tst_qxmppjingleiq.cpp | 170 |
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)); |
