Add support for DH1080 key exchange 5/head
authorBas Pape <baspape@gmail.com>
Thu, 31 Jan 2013 18:13:12 +0000 (19:13 +0100)
committerBas Pape <baspape@gmail.com>
Thu, 31 Jan 2013 18:19:34 +0000 (19:19 +0100)
Adds a KeyEvent and handlers, as well as a command (keyx) to initiate
and act upon a key exchange.

src/common/CMakeLists.txt
src/common/eventmanager.h
src/common/keyevent.cpp [new file with mode: 0644]
src/common/keyevent.h [new file with mode: 0644]
src/core/coresessioneventprocessor.cpp
src/core/coresessioneventprocessor.h
src/core/coreuserinputhandler.cpp
src/core/coreuserinputhandler.h
src/core/ircparser.cpp

index a21f807..7bdf1c5 100644 (file)
@@ -77,6 +77,11 @@ set(HEADERS ${MOC_HDRS}
     types.h
     util.h)
 
+if (HAVE_QCA2)
+    set(SOURCES ${SOURCES} keyevent.cpp)
+    set(HEADERS ${HEADERS} keyevent.h)
+endif(HAVE_QCA2)
+
 if(APPLE)
   set(SOURCES ${SOURCES} mac_utils.cpp)
   set(HEADERS ${HEADERS} mac_utils.h)
index b346756..1241149 100644 (file)
@@ -113,6 +113,10 @@ public :
 
         CtcpEvent                   = 0x00050000,
         CtcpEventFlush
+
+#ifdef HAVE_QCA2
+        ,KeyEvent                    = 0x00060000
+#endif
     };
 
     EventManager(QObject *parent = 0);
diff --git a/src/common/keyevent.cpp b/src/common/keyevent.cpp
new file mode 100644 (file)
index 0000000..afeca21
--- /dev/null
@@ -0,0 +1,47 @@
+/***************************************************************************
+ *   Copyright (C) 2013 by the Quassel Project                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#include "keyevent.h"
+
+Event *KeyEvent::create(EventManager::EventType type, QVariantMap &map, Network *network)
+{
+    if (type == EventManager::KeyEvent)
+        return new KeyEvent(type, map, network);
+
+    return 0;
+}
+
+
+KeyEvent::KeyEvent(EventManager::EventType type, QVariantMap &map, Network *network)
+    : IrcEvent(type, map, network)
+{
+    _exchangeType = static_cast<ExchangeType>(map.take("exchangeType").toInt());
+    _target = map.take("target").toString();
+    _key = map.take("key").toByteArray();
+}
+
+
+void KeyEvent::toVariantMap(QVariantMap &map) const
+{
+    IrcEvent::toVariantMap(map);
+    map["exchangeType"] = exchangeType();
+    map["target"] = target();
+    map["key"] = key();
+}
diff --git a/src/common/keyevent.h b/src/common/keyevent.h
new file mode 100644 (file)
index 0000000..0e7b3b8
--- /dev/null
@@ -0,0 +1,79 @@
+/***************************************************************************
+ *   Copyright (C) 2013 by the Quassel Project                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#ifndef KEYEVENT_H
+#define KEYEVENT_H
+
+#include "ircevent.h"
+
+class KeyEvent : public IrcEvent
+{
+public:
+    enum ExchangeType {
+        Init,
+        Finish
+    };
+
+    explicit KeyEvent(EventManager::EventType type, Network *network, const QString &prefix, const QString &target,
+        ExchangeType exchangeType, const QByteArray &key,
+        const QDateTime &timestamp = QDateTime())
+        : IrcEvent(type, network, prefix),
+        _exchangeType(exchangeType),
+        _target(target),
+        _key(key)
+    {
+        setTimestamp(timestamp);
+    }
+
+
+    inline ExchangeType exchangeType() const { return _exchangeType; }
+    inline void setExchangeType(ExchangeType type) { _exchangeType = type; }
+
+    inline QString target() const { return _target; }
+    inline void setTarget(const QString &target) { _target = target; }
+
+    inline QByteArray key() const { return _key; }
+    inline void setKey(const QByteArray &key) { _key = key; }
+
+    static Event *create(EventManager::EventType type, QVariantMap &map, Network *network);
+
+protected:
+    explicit KeyEvent(EventManager::EventType type, QVariantMap &map, Network *network);
+    void toVariantMap(QVariantMap &map) const;
+
+    virtual inline QString className() const { return "KeyEvent"; }
+    virtual inline void debugInfo(QDebug &dbg) const
+    {
+        NetworkEvent::debugInfo(dbg);
+        dbg << ", prefix = " << qPrintable(prefix())
+            << ", target = " << qPrintable(target())
+            << ", exchangetype = " << (exchangeType() == Init ? "init" : "finish")
+            << ", key = " << qPrintable(key());
+    }
+
+
+private:
+    ExchangeType _exchangeType;
+    QString _target;
+    QByteArray _key;
+};
+
+
+#endif
index d0c7597..1e9bf05 100644 (file)
 #include "netsplit.h"
 #include "quassel.h"
 
+#ifdef HAVE_QCA2
+#  include "keyevent.h"
+#endif
+
 CoreSessionEventProcessor::CoreSessionEventProcessor(CoreSession *session)
     : BasicHandler("handleCtcp", session),
     _coreSession(session)
@@ -418,6 +422,42 @@ void CoreSessionEventProcessor::processIrcEventTopic(IrcEvent *e)
 }
 
 
+#ifdef HAVE_QCA2
+void CoreSessionEventProcessor::processKeyEvent(KeyEvent *e)
+{
+    if (!Cipher::neededFeaturesAvailable()) {
+        emit newEvent(new MessageEvent(Message::Error, e->network(), tr("Unable to perform key exchange."), e->prefix(), e->target(), Message::None, e->timestamp()));
+        return;
+    }
+    CoreNetwork *net = qobject_cast<CoreNetwork*>(e->network());
+    Cipher *c = net->cipher(e->target());
+    if (!c) // happens when there is no CoreIrcChannel for the target (i.e. never?)
+        return;
+
+    if (e->exchangeType() == KeyEvent::Init) {
+        QByteArray pubKey = c->parseInitKeyX(e->key());
+        if (pubKey.isEmpty()) {
+            emit newEvent(new MessageEvent(Message::Error, e->network(), tr("Unable to parse the DH1080_INIT. Key exchange failed."), e->prefix(), e->target(), Message::None, e->timestamp()));
+            return;
+        } else {
+            net->setCipherKey(e->target(), c->key());
+            emit newEvent(new MessageEvent(Message::Info, e->network(), tr("Your key is set and messages will be encrypted."), e->prefix(), e->target(), Message::None, e->timestamp()));
+            QList<QByteArray> p;
+            p << net->serverEncode(e->target()) << net->serverEncode("DH1080_FINISH ")+pubKey;
+            net->putCmd("NOTICE", p);
+        }
+    } else {
+        if (c->parseFinishKeyX(e->key())) {
+            net->setCipherKey(e->target(), c->key());
+            emit newEvent(new MessageEvent(Message::Info, e->network(), tr("Your key is set and messages will be encrypted."), e->prefix(), e->target(), Message::None, e->timestamp()));
+        } else {
+            emit newEvent(new MessageEvent(Message::Info, e->network(), tr("Failed to parse DH1080_FINISH. Key exchange failed."), e->prefix(), e->target(), Message::None, e->timestamp()));
+        }
+    }
+}
+#endif
+
+
 /* RPL_WELCOME */
 void CoreSessionEventProcessor::processIrcEvent001(IrcEvent *e)
 {
index 1d255d8..07bb7d8 100644 (file)
@@ -31,6 +31,10 @@ class IrcEvent;
 class IrcEventNumeric;
 class Netsplit;
 
+#ifdef HAVE_QCA2
+class KeyEvent;
+#endif
+
 class CoreSessionEventProcessor : public BasicHandler
 {
     Q_OBJECT
@@ -55,6 +59,9 @@ public:
     Q_INVOKABLE void processIrcEventQuit(IrcEvent *event);
     Q_INVOKABLE void lateProcessIrcEventQuit(IrcEvent *event);
     Q_INVOKABLE void processIrcEventTopic(IrcEvent *event);
+#ifdef HAVE_QCA2
+    Q_INVOKABLE void processKeyEvent(KeyEvent *event);
+#endif
 
     Q_INVOKABLE void processIrcEvent001(IrcEvent *event);          // RPL_WELCOME
     Q_INVOKABLE void processIrcEvent005(IrcEvent *event);          // RPL_ISUPPORT
index 0326436..bb677c3 100644 (file)
@@ -319,6 +319,50 @@ void CoreUserInputHandler::handleJoin(const BufferInfo &bufferInfo, const QStrin
 }
 
 
+void CoreUserInputHandler::handleKeyx(const BufferInfo &bufferInfo, const QString &msg)
+{
+#ifdef HAVE_QCA2
+    if (!bufferInfo.isValid())
+        return;
+
+    if (!Cipher::neededFeaturesAvailable())
+        return;
+
+    QStringList parms = msg.split(' ', QString::SkipEmptyParts);
+
+    if (parms.count() == 0 && !bufferInfo.bufferName().isEmpty())
+        parms.prepend(bufferInfo.bufferName());
+    else if (parms.count() != 1) {
+        emit displayMsg(Message::Info, bufferInfo.bufferName(),
+            tr("[usage] /keyx [<nick|channel>] Initiates a DH1080 key exchange with the target."));
+        return;
+    }
+
+    QString target = parms.at(0);
+
+    Cipher *cipher = network()->cipher(target);
+    if (!cipher) // happens when there is no CoreIrcChannel for the target
+        return;
+
+    QByteArray pubKey = cipher->initKeyExchange();
+    if (pubKey.isEmpty())
+        emit displayMsg(Message::Error, bufferInfo.bufferName(), tr("Failed to initiate key exchange with %1.").arg(target));
+    else {
+        QList<QByteArray> params;
+        params << serverEncode(target) << serverEncode("DH1080_INIT ") + pubKey;
+        emit putCmd("NOTICE", params);
+        emit displayMsg(Message::Info, bufferInfo.bufferName(), tr("Initiated key exchange with %1.").arg(target));
+    }
+#else
+    Q_UNUSED(msg)
+    emit displayMsg(Message::Error, bufferInfo.bufferName(), tr("Error: Setting an encryption key requires Quassel to have been built "
+                                                                "with support for the Qt Cryptographic Architecture (QCA) library. "
+                                                                "Contact your distributor about a Quassel package with QCA "
+                                                                "support, or rebuild Quassel with QCA present."));
+#endif
+}
+
+
 void CoreUserInputHandler::handleKick(const BufferInfo &bufferInfo, const QString &msg)
 {
     QString nick = msg.section(' ', 0, 0, QString::SectionSkipEmpty);
index 6f559c5..288e2e8 100644 (file)
@@ -48,6 +48,7 @@ public slots:
     void handleDevoice(const BufferInfo &bufferInfo, const QString &text);
     void handleInvite(const BufferInfo &bufferInfo, const QString &text);
     void handleJoin(const BufferInfo &bufferInfo, const QString &text);
+    void handleKeyx(const BufferInfo &bufferInfo, const QString &text);
     void handleKick(const BufferInfo &bufferInfo, const QString &text);
     void handleKill(const BufferInfo &bufferInfo, const QString &text);
     void handleList(const BufferInfo &bufferInfo, const QString &text);
index f71f53c..44e6cd1 100644 (file)
@@ -28,6 +28,7 @@
 
 #ifdef HAVE_QCA2
 #  include "cipher.h"
+#  include "keyevent.h"
 #endif
 
 IrcParser::IrcParser(CoreSession *session) :
@@ -228,7 +229,16 @@ void IrcParser::processNetworkIncoming(NetworkDataEvent *e)
                     if (!net->isChannelName(target))
                         target = nickFromMask(prefix);
                 }
-                events << new IrcEventRawMessage(EventManager::IrcEventRawNotice, net, params[1], prefix, target, e->timestamp());
+
+#ifdef HAVE_QCA2
+                // Handle DH1080 key exchange
+                if (params[1].startsWith("DH1080_INIT")) {
+                    events << new KeyEvent(EventManager::KeyEvent, net, prefix, target, KeyEvent::Init, params[1].mid(12));
+                } else if (params[1].startsWith("DH1080_FINISH")) {
+                    events << new KeyEvent(EventManager::KeyEvent, net, prefix, target, KeyEvent::Finish, params[1].mid(14));
+                } else
+#endif
+                    events << new IrcEventRawMessage(EventManager::IrcEventRawNotice, net, params[1], prefix, target, e->timestamp());
             }
         }
         break;