From: Bas Pape Date: Thu, 31 Jan 2013 18:13:12 +0000 (+0100) Subject: Add support for DH1080 key exchange X-Git-Tag: 0.9-beta1~18^2 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=4f2d0000935e5f040940aca85a44b905bd916147 Add support for DH1080 key exchange Adds a KeyEvent and handlers, as well as a command (keyx) to initiate and act upon a key exchange. --- diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a21f8075..7bdf1c55 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -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) diff --git a/src/common/eventmanager.h b/src/common/eventmanager.h index b346756c..12411495 100644 --- a/src/common/eventmanager.h +++ b/src/common/eventmanager.h @@ -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 index 00000000..afeca219 --- /dev/null +++ b/src/common/keyevent.cpp @@ -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(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 index 00000000..0e7b3b87 --- /dev/null +++ b/src/common/keyevent.h @@ -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 ×tamp = 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 diff --git a/src/core/coresessioneventprocessor.cpp b/src/core/coresessioneventprocessor.cpp index d0c75971..1e9bf050 100644 --- a/src/core/coresessioneventprocessor.cpp +++ b/src/core/coresessioneventprocessor.cpp @@ -30,6 +30,10 @@ #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(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 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) { diff --git a/src/core/coresessioneventprocessor.h b/src/core/coresessioneventprocessor.h index 1d255d8e..07bb7d85 100644 --- a/src/core/coresessioneventprocessor.h +++ b/src/core/coresessioneventprocessor.h @@ -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 diff --git a/src/core/coreuserinputhandler.cpp b/src/core/coreuserinputhandler.cpp index 03264363..bb677c38 100644 --- a/src/core/coreuserinputhandler.cpp +++ b/src/core/coreuserinputhandler.cpp @@ -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 [] 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 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); diff --git a/src/core/coreuserinputhandler.h b/src/core/coreuserinputhandler.h index 6f559c59..288e2e86 100644 --- a/src/core/coreuserinputhandler.h +++ b/src/core/coreuserinputhandler.h @@ -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); diff --git a/src/core/ircparser.cpp b/src/core/ircparser.cpp index f71f53c1..44e6cd19 100644 --- a/src/core/ircparser.cpp +++ b/src/core/ircparser.cpp @@ -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;