aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Jahn <lnj@kaidan.im>2019-04-28 22:11:25 +0200
committerLNJ <lnj@kaidan.im>2019-09-06 21:18:51 +0200
commite7394afc6730b16673f4173fcbc55d54a810a80b (patch)
tree854b917a079c385e780a72f9465403b70d53dc57
parenta0925867901052d97fe84c2fcf31c720e0443546 (diff)
downloadqxmpp-e7394afc6730b16673f4173fcbc55d54a810a80b.tar.gz
Implement XEP-0380: Explicit Message Encryption
This adds parsing and serialization for XEP-0380: Explicit Message Encryption in version 0.3.0. https://xmpp.org/extensions/xep-0380.html
-rw-r--r--CHANGELOG.md1
-rw-r--r--doc/xep.doc1
-rw-r--r--src/base/QXmppConstants.cpp10
-rw-r--r--src/base/QXmppConstants_p.h10
-rw-r--r--src/base/QXmppMessage.cpp105
-rw-r--r--src/base/QXmppMessage.h21
-rw-r--r--tests/qxmppmessage/tst_qxmppmessage.cpp46
7 files changed, 193 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b9b7a18..c8c97ace 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@ QXmpp 1.0.1 (UNRELEASED)
New features:
- Add support for SCRAM-SHA-1 and SCRAM-SHA-256 (#183, @jlaine)
- Add XEP-0367: Message Attaching (v0.3.0) (#196, @lnjX)
+ - Add XEP-0380: Explicit Message Encryption (v0.3.0) (#199, @lnjX)
- Add XEP-0382: Spoiler messages (v0.2.0) (#195, @lnjX)
QXmpp 1.0.0 (Jan 8, 2019)
diff --git a/doc/xep.doc b/doc/xep.doc
index 3daf0936..130436fc 100644
--- a/doc/xep.doc
+++ b/doc/xep.doc
@@ -42,6 +42,7 @@ Complete:
- XEP-0319: Last User Interaction in Presence
- XEP-0352: Client State Indication
- XEP-0367: Message Attaching (v0.3.0)
+- XEP-0380: Explicit Message Encryption (v0.3.0)
- XEP-0382: Spoiler messages (v0.2.0)
Ongoing:
diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp
index b6fab47d..80c97b56 100644
--- a/src/base/QXmppConstants.cpp
+++ b/src/base/QXmppConstants.cpp
@@ -37,6 +37,8 @@ const char* ns_stanza = "urn:ietf:params:xml:ns:xmpp-stanzas";
const char* ns_rpc = "jabber:iq:rpc";
// XEP-0020: Feature Negotiation
const char* ns_feature_negotiation = "http://jabber.org/protocol/feature-neg";
+// XEP-0027: Current Jabber OpenPGP Usage
+const char* ns_legacy_openpgp = "jabber:x:encrypted";
// XEP-0030: Service Discovery
const char* ns_disco_info = "http://jabber.org/protocol/disco#info";
const char* ns_disco_items = "http://jabber.org/protocol/disco#items";
@@ -135,6 +137,8 @@ const char* ns_chat_markers = "urn:xmpp:chat-markers:0";
const char* ns_csi = "urn:xmpp:csi:0";
// XEP-0363: HTTP File Upload
const char* ns_http_upload = "urn:xmpp:http:upload:0";
+// XEP-0364: Current Off-the-Record Messaging Usage
+const char* ns_otr = "urn:xmpp:otr:0";
// XEP-0367: Message Attaching
const char* ns_message_attaching = "urn:xmpp:message-attaching:1";
// XEP-0369: Mediated Information eXchange (MIX)
@@ -146,8 +150,14 @@ const char* ns_mix_node_participants = "urn:xmpp:mix:nodes:participants";
const char* ns_mix_node_presence = "urn:xmpp:mix:nodes:presence";
const char* ns_mix_node_config = "urn:xmpp:mix:nodes:config";
const char* ns_mix_node_info = "urn:xmpp:mix:nodes:info";
+// XEP-0373: OpenPGP for XMPP
+const char* ns_ox = "urn:xmpp:openpgp:0";
+// XEP-0380: Explicit Message Encryption
+const char* ns_eme = "urn:xmpp:eme:0";
// XEP-0382: Spoiler messages
const char* ns_spoiler = "urn:xmpp:spoiler:0";
+// XEP-0384: OMEMO Encryption
+const char* ns_omemo = "eu.siacs.conversations.axolotl";
// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
const char* ns_mix_pam = "urn:xmpp:mix:pam:1";
const char* ns_mix_roster = "urn:xmpp:mix:roster:0";
diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h
index 5304ada4..86003970 100644
--- a/src/base/QXmppConstants_p.h
+++ b/src/base/QXmppConstants_p.h
@@ -49,6 +49,8 @@ extern const char* ns_stanza;
extern const char* ns_rpc;
// XEP-0020: Feature Negotiation
extern const char* ns_feature_negotiation;
+// XEP-0027: Current Jabber OpenPGP Usage
+extern const char* ns_legacy_openpgp;
// XEP-0030: Service Discovery
extern const char* ns_disco_info;
extern const char* ns_disco_items;
@@ -147,6 +149,8 @@ extern const char* ns_chat_markers;
extern const char* ns_csi;
// XEP-0363: HTTP File Upload
extern const char* ns_http_upload;
+// XEP-0364: Current Off-the-Record Messaging Usage
+extern const char* ns_otr;
// XEP-0367: Message Attaching
extern const char* ns_message_attaching;
// XEP-0369: Mediated Information eXchange (MIX)
@@ -158,8 +162,14 @@ extern const char* ns_mix_node_participants;
extern const char* ns_mix_node_presence;
extern const char* ns_mix_node_config;
extern const char* ns_mix_node_info;
+// XEP-0373: OpenPGP for XMPP
+extern const char* ns_ox;
+// XEP-0380: Explicit Message Encryption
+extern const char* ns_eme;
// XEP-0382: Spoiler messages
extern const char* ns_spoiler;
+// XEP-0384: OMEMO Encryption
+extern const char* ns_omemo;
// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
extern const char* ns_mix_pam;
extern const char* ns_mix_roster;
diff --git a/src/base/QXmppMessage.cpp b/src/base/QXmppMessage.cpp
index af4b2a6f..54ef4cd6 100644
--- a/src/base/QXmppMessage.cpp
+++ b/src/base/QXmppMessage.cpp
@@ -55,6 +55,24 @@ static const char* marker_types[] = {
"acknowledged"
};
+static const QStringList ENCRYPTION_NAMESPACES = {
+ QString(),
+ QString(),
+ ns_otr,
+ ns_legacy_openpgp,
+ ns_ox,
+ ns_omemo
+};
+
+static const QStringList ENCRYPTION_NAMES = {
+ QString(),
+ QString(),
+ QStringLiteral("OTR"),
+ QStringLiteral("Legacy OpenPGP"),
+ QStringLiteral("OpenPGP for XMPP (OX)"),
+ QStringLiteral("OMEMO")
+};
+
static const char *ns_xhtml = "http://www.w3.org/1999/xhtml";
enum StampType
@@ -110,6 +128,10 @@ public:
QString mixUserJid;
QString mixUserNick;
+ // XEP-0380: Explicit Message Encryption
+ QString encryptionMethod;
+ QString encryptionName;
+
// XEP-0382: Spoiler messages
bool isSpoiler = false;
QString spoilerHint;
@@ -123,7 +145,7 @@ public:
/// \param thread
QXmppMessage::QXmppMessage(const QString& from, const QString& to, const
- QString& body, const QString& thread)
+ QString& body, const QString& thread)
: QXmppStanza(from, to)
, d(new QXmppMessagePrivate)
{
@@ -571,6 +593,74 @@ void QXmppMessage::setMixUserNick(const QString& mixUserNick)
d->mixUserNick = mixUserNick;
}
+/// Returns the encryption method this message is advertised to be encrypted
+/// with.
+///
+/// \note QXmppMessage::NoEncryption does not necesserily mean that the message
+/// is not encrypted; it may also be that the author of the message does not
+/// support XEP-0380: Explicit Message Encryption.
+///
+/// \note If this returns QXmppMessage::UnknownEncryption, you can still get
+/// the namespace of the encryption with \c encryptionMethodNs() and possibly
+/// also a name with \c encryptionName().
+
+QXmppMessage::EncryptionMethod QXmppMessage::encryptionMethod() const
+{
+ if (d->encryptionMethod.isEmpty())
+ return QXmppMessage::NoEncryption;
+
+ int index = ENCRYPTION_NAMESPACES.indexOf(d->encryptionMethod);
+ if (index < 0)
+ return QXmppMessage::UnknownEncryption;
+ return static_cast<QXmppMessage::EncryptionMethod>(index);
+}
+
+/// Advertises that this message is encrypted with the given encryption method.
+/// See XEP-0380: Explicit Message Encryption for details.
+
+void QXmppMessage::setEncryptionMethod(QXmppMessage::EncryptionMethod method)
+{
+ d->encryptionMethod = ENCRYPTION_NAMESPACES.at(int(method));
+}
+
+/// Returns the namespace of the advertised encryption method via. XEP-0380:
+/// Explicit Message Encryption.
+
+QString QXmppMessage::encryptionMethodNs() const
+{
+ return d->encryptionMethod;
+}
+
+/// Sets the namespace of the encryption method this message advertises to be
+/// encrypted with. See XEP-0380: Explicit Message Encryption for details.
+
+void QXmppMessage::setEncryptionMethodNs(const QString &encryptionMethod)
+{
+ d->encryptionMethod = encryptionMethod;
+}
+
+/// Returns the associated name of the encryption method this message
+/// advertises to be encrypted with. See XEP-0380: Explicit Message Encryption
+/// for details.
+
+QString QXmppMessage::encryptionName() const
+{
+ if (!d->encryptionName.isEmpty())
+ return d->encryptionName;
+ return ENCRYPTION_NAMES.at(int(encryptionMethod()));
+}
+
+/// Sets the name of the encryption method for XEP-0380: Explicit Message
+/// Encryption.
+///
+/// \note This should only be used, if the encryption method is custom and is
+/// not one of the methods listed in the XEP.
+
+void QXmppMessage::setEncryptionName(const QString &encryptionName)
+{
+ d->encryptionName = encryptionName;
+}
+
/// Returns true, if this is a spoiler message according to XEP-0382: Spoiler
/// messages. The spoiler hint however can still be empty.
///
@@ -765,6 +855,10 @@ void QXmppMessage::parse(const QDomElement &element)
} else if (xElement.tagName() == "mix" && xElement.namespaceURI() == ns_mix) {
d->mixUserJid = xElement.firstChildElement("jid").text();
d->mixUserNick = xElement.firstChildElement("nick").text();
+ // XEP-0380: Explicit Message Encryption
+ } else if (xElement.tagName() == "encryption" && xElement.namespaceURI() == ns_eme) {
+ d->encryptionMethod = xElement.attribute("namespace");
+ d->encryptionName = xElement.attribute("name");
// XEP-0382: Spoiler messages
} else if (xElement.tagName() == "spoiler" && xElement.namespaceURI() == ns_spoiler) {
d->isSpoiler = true;
@@ -923,6 +1017,15 @@ void QXmppMessage::toXml(QXmlStreamWriter *xmlWriter) const
xmlWriter->writeEndElement();
}
+ // XEP-0380: Explicit Message Encryption
+ if (!d->encryptionMethod.isEmpty()) {
+ xmlWriter->writeStartElement("encryption");
+ xmlWriter->writeAttribute("xmlns", ns_eme);
+ xmlWriter->writeAttribute("namespace", d->encryptionMethod);
+ helperToXmlAddAttribute(xmlWriter, "name", d->encryptionName);
+ xmlWriter->writeEndElement();
+ }
+
// XEP-0382: Spoiler messages
if (d->isSpoiler) {
xmlWriter->writeStartElement("spoiler");
diff --git a/src/base/QXmppMessage.h b/src/base/QXmppMessage.h
index 330c70fe..69e2d451 100644
--- a/src/base/QXmppMessage.h
+++ b/src/base/QXmppMessage.h
@@ -66,6 +66,18 @@ public:
Acknowledged
};
+ /// This enum describes different end-to-end encryption methods. These can
+ /// be used to mark a message explicitly as encrypted with a specific
+ /// algothim. See XEP-0380: Explicit Message Encryption for details.
+ enum EncryptionMethod {
+ NoEncryption, ///< No encryption
+ UnknownEncryption, ///< Unknown encryption
+ OTR, ///< XEP-0364: Current Off-the-Record Messaging Usage
+ LegacyOpenPGP, ///< XEP-0027: Current Jabber OpenPGP Usage
+ OX, ///< XEP-0373: OpenPGP for XMPP
+ OMEMO ///< XEP-0384: OMEMO Encryption
+ };
+
QXmppMessage(const QString& from = QString(), const QString& to = QString(),
const QString& body = QString(), const QString& thread = QString());
@@ -151,6 +163,15 @@ public:
QString mixUserNick() const;
void setMixUserNick(const QString&);
+ // XEP-0380: Explicit Message Encryption
+ EncryptionMethod encryptionMethod() const;
+ void setEncryptionMethod(EncryptionMethod);
+ QString encryptionMethodNs() const;
+ void setEncryptionMethodNs(const QString&);
+
+ QString encryptionName() const;
+ void setEncryptionName(const QString&);
+
// XEP-0382: Spoiler messages
bool isSpoiler() const;
void setIsSpoiler(bool);
diff --git a/tests/qxmppmessage/tst_qxmppmessage.cpp b/tests/qxmppmessage/tst_qxmppmessage.cpp
index 9441acef..5fe4b964 100644
--- a/tests/qxmppmessage/tst_qxmppmessage.cpp
+++ b/tests/qxmppmessage/tst_qxmppmessage.cpp
@@ -50,6 +50,7 @@ private slots:
void testMessageCorrect();
void testMessageAttaching();
void testMix();
+ void testEme();
void testSpoiler();
};
@@ -114,6 +115,7 @@ void tst_QXmppMessage::testBasic()
QCOMPARE(message.isReceiptRequested(), false);
QCOMPARE(message.receiptId(), QString());
QCOMPARE(message.xhtml(), QString());
+ QCOMPARE(message.encryptionMethod(), QXmppMessage::NoEncryption);
QVERIFY(!message.isSpoiler());
serializePacket(message, xml);
}
@@ -663,6 +665,50 @@ void tst_QXmppMessage::testMix()
QCOMPARE(message.mixUserNick(), QString("erik"));
}
+void tst_QXmppMessage::testEme()
+{
+ // test standard encryption: OMEMO
+ const QByteArray xmlOmemo(
+ "<message to=\"foo@example.com/QXmpp\" from=\"bar@example.com/QXmpp\" type=\"normal\">"
+ "<body>This message is encrypted with OMEMO, but your client doesn't seem to support that.</body>"
+ "<encryption xmlns=\"urn:xmpp:eme:0\" namespace=\"eu.siacs.conversations.axolotl\"/>"
+ "</message>");
+
+ QXmppMessage messageOmemo;
+ parsePacket(messageOmemo, xmlOmemo);
+ QCOMPARE(messageOmemo.encryptionMethodNs(), QString("eu.siacs.conversations.axolotl"));
+ QCOMPARE(messageOmemo.encryptionMethod(), QXmppMessage::OMEMO);
+ QCOMPARE(messageOmemo.encryptionName(), QString("OMEMO"));
+ serializePacket(messageOmemo, xmlOmemo);
+
+ // test custom encryption
+ const QByteArray xmlCustom(
+ "<message to=\"foo@example.com/QXmpp\" from=\"bar@example.com/QXmpp\" type=\"normal\">"
+ "<body>This message is encrypted with CustomCrypt, but your client doesn't seem to support that.</body>"
+ "<encryption xmlns=\"urn:xmpp:eme:0\" namespace=\"im:example:customcrypt:1\" name=\"CustomCrypt\"/>"
+ "</message>");
+
+ QXmppMessage messageCustom;
+ parsePacket(messageCustom, xmlCustom);
+ QCOMPARE(messageCustom.encryptionMethodNs(), QString("im:example:customcrypt:1"));
+ QCOMPARE(messageCustom.encryptionMethod(), QXmppMessage::UnknownEncryption);
+ QCOMPARE(messageCustom.encryptionName(), QString("CustomCrypt"));
+ serializePacket(messageCustom, xmlCustom);
+
+ // test setters/getters
+ QXmppMessage message;
+ message.setEncryptionMethod(QXmppMessage::LegacyOpenPGP);
+ QCOMPARE(message.encryptionMethod(), QXmppMessage::LegacyOpenPGP);
+ QCOMPARE(message.encryptionMethodNs(), QString("jabber:x:encrypted"));
+ QCOMPARE(message.encryptionName(), QString("Legacy OpenPGP"));
+
+ message.setEncryptionMethodNs("fancyorg:encryption:fancycrypt:0");
+ message.setEncryptionName("FancyCrypt");
+ QCOMPARE(message.encryptionMethod(), QXmppMessage::UnknownEncryption);
+ QCOMPARE(message.encryptionMethodNs(), QString("fancyorg:encryption:fancycrypt:0"));
+ QCOMPARE(message.encryptionName(), QString("FancyCrypt"));
+}
+
void tst_QXmppMessage::testSpoiler()
{
// test parsing with hint