Implemented encryption in NetworkPackage using QCA2
Fixed some bugs in the pairing process state machine Keys are now stored in base64 in KConfig (was storing non-allowed chars) Updated NetworkPackage tests to include encryption Increased networkpackage version 1 -> 2
This commit is contained in:
parent
13589dfc0f
commit
deeeb595b3
26 changed files with 240 additions and 119 deletions
|
@ -18,4 +18,4 @@ add_subdirectory(kcm)
|
||||||
#add_subdirectory(kioslave)
|
#add_subdirectory(kioslave)
|
||||||
add_subdirectory(plasmoid)
|
add_subdirectory(plasmoid)
|
||||||
|
|
||||||
add_subdirectory(test)
|
add_subdirectory(tests)
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
add_subdirectory(plugins)
|
||||||
|
|
||||||
|
find_package (QJSON REQUIRED)
|
||||||
|
find_package (QCA2 REQUIRED)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${QJSON_INCLUDE_DIR}
|
${QJSON_INCLUDE_DIR}
|
||||||
${QCA2_INCLUDE_DIR}
|
${QCA2_INCLUDE_DIR}
|
||||||
|
@ -21,13 +26,8 @@ set(kded_kdeconnect_SRCS
|
||||||
device.cpp
|
device.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(plugins)
|
|
||||||
|
|
||||||
kde4_add_plugin(kded_kdeconnect ${kded_kdeconnect_SRCS})
|
kde4_add_plugin(kded_kdeconnect ${kded_kdeconnect_SRCS})
|
||||||
|
|
||||||
find_package (QJSON REQUIRED)
|
|
||||||
find_package (QCA2 REQUIRED)
|
|
||||||
|
|
||||||
target_link_libraries(kded_kdeconnect
|
target_link_libraries(kded_kdeconnect
|
||||||
${KDE4_KDECORE_LIBS}
|
${KDE4_KDECORE_LIBS}
|
||||||
${KDE4_KDEUI_LIBS}
|
${KDE4_KDEUI_LIBS}
|
||||||
|
|
|
@ -57,10 +57,10 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
|
||||||
|
|
||||||
//http://delta.affinix.com/docs/qca/rsatest_8cpp-example.html
|
//http://delta.affinix.com/docs/qca/rsatest_8cpp-example.html
|
||||||
QCA::PrivateKey privateKey = QCA::KeyGenerator().createRSA(1024);
|
QCA::PrivateKey privateKey = QCA::KeyGenerator().createRSA(1024);
|
||||||
config->group("myself").writeEntry("privateKey", privateKey.toDER());
|
config->group("myself").writeEntry("privateKey", privateKey.toDER().toByteArray().toBase64());
|
||||||
|
|
||||||
QCA::PublicKey publicKey = privateKey.toPublicKey();
|
QCA::PublicKey publicKey = privateKey.toPublicKey();
|
||||||
config->group("myself").writeEntry("publicKey", publicKey.toDER());
|
config->group("myself").writeEntry("publicKey", publicKey.toDER().toBase64());
|
||||||
//TODO: Store key in a PEM file instead (KStandardDirs::locate("appdata", "private.pem"))
|
//TODO: Store key in a PEM file instead (KStandardDirs::locate("appdata", "private.pem"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ Device::Device(const QString& id)
|
||||||
m_deviceName = name;
|
m_deviceName = name;
|
||||||
|
|
||||||
const QByteArray& key = data.readEntry<QByteArray>("publicKey",QByteArray());
|
const QByteArray& key = data.readEntry<QByteArray>("publicKey",QByteArray());
|
||||||
m_publicKey = QCA::RSAPublicKey::fromDER(key);
|
m_publicKey = QCA::RSAPublicKey::fromDER(QByteArray::fromBase64(key));
|
||||||
|
|
||||||
m_pairStatus = Device::Paired;
|
m_pairStatus = Device::Paired;
|
||||||
|
|
||||||
|
@ -57,12 +57,12 @@ Device::~Device()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::hasPlugin(const QString& name)
|
bool Device::hasPlugin(const QString& name) const
|
||||||
{
|
{
|
||||||
return m_plugins.contains(name);
|
return m_plugins.contains(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Device::loadedPlugins()
|
QStringList Device::loadedPlugins() const
|
||||||
{
|
{
|
||||||
return m_plugins.keys();
|
return m_plugins.keys();
|
||||||
}
|
}
|
||||||
|
@ -94,8 +94,8 @@ void Device::reloadPlugins()
|
||||||
} else {
|
} else {
|
||||||
KdeConnectPlugin* plugin = loader->instantiatePluginForDevice(pluginName, this);
|
KdeConnectPlugin* plugin = loader->instantiatePluginForDevice(pluginName, this);
|
||||||
|
|
||||||
connect(this, SIGNAL(receivedPackage(const NetworkPackage&)),
|
connect(this, SIGNAL(receivedPackage(NetworkPackage)),
|
||||||
plugin, SLOT(receivePackage(const NetworkPackage&)));
|
plugin, SLOT(receivePackage(NetworkPackage)));
|
||||||
|
|
||||||
newPluginMap[pluginName] = plugin;
|
newPluginMap[pluginName] = plugin;
|
||||||
}
|
}
|
||||||
|
@ -118,25 +118,16 @@ void Device::reloadPlugins()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
|
||||||
QSslKey myPrivateKey() {
|
|
||||||
|
|
||||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
|
||||||
const QString& key = config->group("myself").readEntry<QString>("privateKey",QString());
|
|
||||||
|
|
||||||
QSslKey privateKey(key.toAscii(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
|
||||||
qDebug() << "Valid public key:" << !privateKey.isNull();
|
|
||||||
|
|
||||||
return privateKey;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::requestPair()
|
void Device::requestPair()
|
||||||
{
|
{
|
||||||
if (m_pairStatus != NotPaired) {
|
if (m_pairStatus == Paired) {
|
||||||
Q_EMIT pairingFailed(i18n("Already paired"));
|
Q_EMIT pairingFailed(i18n("Already paired"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (m_pairStatus == Device::PairRequested) {
|
||||||
|
Q_EMIT pairingFailed(i18n("Pairing already requested for this device"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!isReachable()) {
|
if (!isReachable()) {
|
||||||
Q_EMIT pairingFailed(i18n("Device not reachable"));
|
Q_EMIT pairingFailed(i18n("Device not reachable"));
|
||||||
return;
|
return;
|
||||||
|
@ -206,7 +197,8 @@ void Device::addLink(DeviceLink* link)
|
||||||
//the old one before this is called), so we do not have to worry about destroying old links.
|
//the old one before this is called), so we do not have to worry about destroying old links.
|
||||||
//Actually, we should not destroy them or the provider will store an invalid ref!
|
//Actually, we should not destroy them or the provider will store an invalid ref!
|
||||||
|
|
||||||
connect(link, SIGNAL(receivedPackage(NetworkPackage)), this, SLOT(privateReceivedPackage(NetworkPackage)));
|
connect(link, SIGNAL(receivedPackage(NetworkPackage)),
|
||||||
|
this, SLOT(privateReceivedPackage(NetworkPackage)));
|
||||||
|
|
||||||
qSort(m_deviceLinks.begin(), m_deviceLinks.end(), lessThan);
|
qSort(m_deviceLinks.begin(), m_deviceLinks.end(), lessThan);
|
||||||
|
|
||||||
|
@ -237,12 +229,12 @@ void Device::removeLink(DeviceLink* link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::sendPackage(const NetworkPackage& np) const
|
bool Device::sendPackage(NetworkPackage& np)
|
||||||
{
|
{
|
||||||
//Maybe we could block here any package that is not an identity or a pairing package
|
|
||||||
|
|
||||||
if (isPaired()) {
|
if (isPaired()) {
|
||||||
|
np.encrypt(m_publicKey);
|
||||||
|
} else {
|
||||||
|
//Maybe we could block here any package that is not an identity or a pairing package to prevent sending non encrypted data
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_FOREACH(DeviceLink* dl, m_deviceLinks) {
|
Q_FOREACH(DeviceLink* dl, m_deviceLinks) {
|
||||||
|
@ -256,21 +248,27 @@ bool Device::sendPackage(const NetworkPackage& np) const
|
||||||
|
|
||||||
void Device::privateReceivedPackage(const NetworkPackage& np)
|
void Device::privateReceivedPackage(const NetworkPackage& np)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (np.type() == PACKAGE_TYPE_PAIR) {
|
if (np.type() == PACKAGE_TYPE_PAIR) {
|
||||||
|
|
||||||
qDebug() << "Pair package";
|
qDebug() << "Pair package";
|
||||||
|
|
||||||
bool wantsPair = np.get<bool>("pair");
|
bool wantsPair = np.get<bool>("pair");
|
||||||
|
|
||||||
if (wantsPair == isPaired()) {
|
if (wantsPair == isPaired()) {
|
||||||
qDebug() << "Already" << (wantsPair? "paired":"unpaired");
|
qDebug() << "Already" << (wantsPair? "paired":"unpaired");
|
||||||
|
if (m_pairStatus == Device::PairRequested) {
|
||||||
|
m_pairStatus = Device::NotPaired;
|
||||||
|
pairingTimer.stop();
|
||||||
|
Q_EMIT pairingFailed(i18n("Canceled by other peer"));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
|
||||||
|
|
||||||
if (wantsPair) {
|
if (wantsPair) {
|
||||||
|
|
||||||
|
const QByteArray& key = np.get<QByteArray>("publicKey");
|
||||||
|
m_publicKey = QCA::RSAPublicKey::fromDER(QByteArray::fromBase64(key));
|
||||||
|
|
||||||
if (m_pairStatus == Device::PairRequested) { //We started pairing
|
if (m_pairStatus == Device::PairRequested) { //We started pairing
|
||||||
|
|
||||||
qDebug() << "Pair answer";
|
qDebug() << "Pair answer";
|
||||||
|
@ -279,10 +277,10 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
|
||||||
pairingTimer.stop();
|
pairingTimer.stop();
|
||||||
|
|
||||||
//Store as trusted device
|
//Store as trusted device
|
||||||
const QByteArray& key = np.get<QByteArray>("publicKey");
|
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||||
config->group("devices").group(id()).writeEntry("publicKey",key);
|
config->group("devices").group(id()).writeEntry("publicKey",key);
|
||||||
config->group("devices").group(id()).writeEntry("name",name());
|
config->group("devices").group(id()).writeEntry("name",name());
|
||||||
m_publicKey = QCA::RSAPublicKey::fromDER(key);
|
m_publicKey = QCA::RSAPublicKey::fromDER(QByteArray::fromBase64(key));
|
||||||
|
|
||||||
Q_EMIT pairingSuccesful();
|
Q_EMIT pairingSuccesful();
|
||||||
|
|
||||||
|
@ -290,9 +288,6 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
|
||||||
|
|
||||||
qDebug() << "Pair request";
|
qDebug() << "Pair request";
|
||||||
|
|
||||||
const QByteArray& key = np.get<QByteArray>("publicKey");
|
|
||||||
m_publicKey = QCA::RSAPublicKey::fromDER(key);
|
|
||||||
|
|
||||||
KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent
|
KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent
|
||||||
notification->setPixmap(KIcon("dialog-information").pixmap(48, 48));
|
notification->setPixmap(KIcon("dialog-information").pixmap(48, 48));
|
||||||
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
|
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
|
||||||
|
@ -309,10 +304,12 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
|
||||||
|
|
||||||
qDebug() << "Unpair request";
|
qDebug() << "Unpair request";
|
||||||
if (m_pairStatus == PairRequested) {
|
if (m_pairStatus == PairRequested) {
|
||||||
|
m_pairStatus = Device::NotPaired;
|
||||||
pairingTimer.stop();
|
pairingTimer.stop();
|
||||||
Q_EMIT pairingFailed(i18n("Canceled by other peer"));
|
Q_EMIT pairingFailed(i18n("Canceled by other peer"));
|
||||||
}
|
} else if (m_pairStatus == Paired) {
|
||||||
unpair();
|
unpair();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,9 +320,27 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
//Forward signal
|
if (!np.isEncrypted()) {
|
||||||
|
//TODO: The other side doesn't know that we are already paired
|
||||||
|
qDebug() << "Warning: A paired device is sending an unencrypted package";
|
||||||
|
|
||||||
|
//Forward package
|
||||||
Q_EMIT receivedPackage(np);
|
Q_EMIT receivedPackage(np);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//TODO: Do not read the key every time
|
||||||
|
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||||
|
const QByteArray& key = config->group("myself").readEntry<QByteArray>("privateKey",QByteArray());
|
||||||
|
QCA::PrivateKey privateKey = QCA::PrivateKey::fromDER(QByteArray::fromBase64(key));
|
||||||
|
|
||||||
|
//Emit decrypted package
|
||||||
|
NetworkPackage decryptedNp("");
|
||||||
|
np.decrypt(privateKey, &decryptedNp);
|
||||||
|
Q_EMIT receivedPackage(decryptedNp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -348,7 +363,7 @@ void Device::acceptPairing()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Store as trusted device
|
//Store as trusted device
|
||||||
config->group("devices").group(id()).writeEntry("publicKey", m_publicKey.toDER());
|
config->group("devices").group(id()).writeEntry("publicKey", m_publicKey.toDER().toBase64());
|
||||||
config->group("devices").group(id()).writeEntry("name", name());
|
config->group("devices").group(id()).writeEntry("name", name());
|
||||||
|
|
||||||
reloadPlugins(); //This will load plugins
|
reloadPlugins(); //This will load plugins
|
||||||
|
|
|
@ -71,14 +71,14 @@ public:
|
||||||
Q_SCRIPTABLE QStringList availableLinks() const;
|
Q_SCRIPTABLE QStringList availableLinks() const;
|
||||||
Q_SCRIPTABLE bool isReachable() const { return !m_deviceLinks.empty(); }
|
Q_SCRIPTABLE bool isReachable() const { return !m_deviceLinks.empty(); }
|
||||||
|
|
||||||
Q_SCRIPTABLE QStringList loadedPlugins();
|
Q_SCRIPTABLE QStringList loadedPlugins() const;
|
||||||
Q_SCRIPTABLE bool hasPlugin(const QString& name);
|
Q_SCRIPTABLE bool hasPlugin(const QString& name) const;
|
||||||
|
|
||||||
//Send and receive
|
//Send and receive
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void receivedPackage(const NetworkPackage& np);
|
void receivedPackage(const NetworkPackage& np) const;
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
virtual bool sendPackage(const NetworkPackage& np) const;
|
virtual bool sendPackage(NetworkPackage& np);
|
||||||
|
|
||||||
//Dbus operations
|
//Dbus operations
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
|
@ -110,6 +110,7 @@ private:
|
||||||
QMap<QString, KdeConnectPlugin*> m_plugins;
|
QMap<QString, KdeConnectPlugin*> m_plugins;
|
||||||
|
|
||||||
QTimer pairingTimer;
|
QTimer pairingTimer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Device*)
|
Q_DECLARE_METATYPE(Device*)
|
||||||
|
|
|
@ -39,10 +39,10 @@ public:
|
||||||
const QString& deviceId() { return mDeviceId; }
|
const QString& deviceId() { return mDeviceId; }
|
||||||
LinkProvider* provider() { return mLinkProvider; }
|
LinkProvider* provider() { return mLinkProvider; }
|
||||||
|
|
||||||
virtual bool sendPackage(const NetworkPackage& np) const = 0;
|
virtual bool sendPackage(const NetworkPackage& np) = 0;
|
||||||
|
|
||||||
signals:
|
Q_SIGNALS:
|
||||||
void receivedPackage(const NetworkPackage& np) const;
|
void receivedPackage(const NetworkPackage& np);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString mDeviceId;
|
QString mDeviceId;
|
||||||
|
|
|
@ -51,7 +51,7 @@ LanDeviceLink::LanDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* sock
|
||||||
this, SLOT(dataReceived()));
|
this, SLOT(dataReceived()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LanDeviceLink::sendPackage(const NetworkPackage& np) const
|
bool LanDeviceLink::sendPackage(const NetworkPackage& np)
|
||||||
{
|
{
|
||||||
int written = mSocket->write(np.serialize());
|
int written = mSocket->write(np.serialize());
|
||||||
return written != -1;
|
return written != -1;
|
||||||
|
|
|
@ -38,7 +38,7 @@ class LanDeviceLink
|
||||||
public:
|
public:
|
||||||
LanDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* socket);
|
LanDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* socket);
|
||||||
|
|
||||||
bool sendPackage(const NetworkPackage& np) const;
|
bool sendPackage(const NetworkPackage& np);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void dataReceived();
|
void dataReceived();
|
||||||
|
|
|
@ -28,7 +28,7 @@ LoopbackDeviceLink::LoopbackDeviceLink(const QString& deviceId, LoopbackLinkProv
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoopbackDeviceLink::sendPackage(const NetworkPackage& toSend) const
|
bool LoopbackDeviceLink::sendPackage(const NetworkPackage& toSend)
|
||||||
{
|
{
|
||||||
NetworkPackage toReceive("");
|
NetworkPackage toReceive("");
|
||||||
NetworkPackage::unserialize(toSend.serialize(), &toReceive);
|
NetworkPackage::unserialize(toSend.serialize(), &toReceive);
|
||||||
|
|
|
@ -18,8 +18,9 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ECHODEVICELINK_H
|
#ifndef LOOPBACKDEVICELINK_H
|
||||||
#define ECHODEVICELINK_H
|
#define LOOPBACKDEVICELINK_H
|
||||||
|
|
||||||
#include "devicelink.h"
|
#include "devicelink.h"
|
||||||
|
|
||||||
class LoopbackLinkProvider;
|
class LoopbackLinkProvider;
|
||||||
|
@ -31,8 +32,8 @@ class LoopbackDeviceLink
|
||||||
public:
|
public:
|
||||||
LoopbackDeviceLink(const QString& d, LoopbackLinkProvider* a);
|
LoopbackDeviceLink(const QString& d, LoopbackLinkProvider* a);
|
||||||
|
|
||||||
bool sendPackage(const NetworkPackage& np) const;
|
bool sendPackage(const NetworkPackage& np);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ECHODEVICELINK_H
|
#endif
|
||||||
|
|
|
@ -55,7 +55,7 @@ Q_SIGNALS:
|
||||||
//NOTE: The provider will to destroy the DeviceLink when it's no longer accessible,
|
//NOTE: The provider will to destroy the DeviceLink when it's no longer accessible,
|
||||||
// and every user should listen to the destroyed signal to remove its references.
|
// and every user should listen to the destroyed signal to remove its references.
|
||||||
// That's the reason because there is no "onConnectionLost".
|
// That's the reason because there is no "onConnectionLost".
|
||||||
void onConnectionReceived(const NetworkPackage& identityPackage, DeviceLink*);
|
void onConnectionReceived(const NetworkPackage& identityPackage, DeviceLink*) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,15 +32,15 @@
|
||||||
#include <qjson/serializer.h>
|
#include <qjson/serializer.h>
|
||||||
#include <qjson/qobjecthelper.h>
|
#include <qjson/qobjecthelper.h>
|
||||||
|
|
||||||
#include "encryptednetworkpackage.h"
|
const static int CURRENT_PACKAGE_VERSION = 2;
|
||||||
|
|
||||||
const static int CURRENT_PACKAGE_VERSION = 1;
|
|
||||||
|
|
||||||
NetworkPackage::NetworkPackage(const QString& type)
|
NetworkPackage::NetworkPackage(const QString& type)
|
||||||
{
|
{
|
||||||
mId = QDateTime::currentMSecsSinceEpoch();
|
mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||||
mType = type;
|
mType = type;
|
||||||
|
mBody = QVariantMap();
|
||||||
mVersion = CURRENT_PACKAGE_VERSION;
|
mVersion = CURRENT_PACKAGE_VERSION;
|
||||||
|
mEncrypted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray NetworkPackage::serialize() const
|
QByteArray NetworkPackage::serialize() const
|
||||||
|
@ -80,11 +80,6 @@ void NetworkPackage::unserialize(const QByteArray& a, NetworkPackage* np)
|
||||||
}
|
}
|
||||||
|
|
||||||
//QVariant -> Object
|
//QVariant -> Object
|
||||||
//NetworkPackage np;
|
|
||||||
//QJSon json(a);
|
|
||||||
//np.mId = json["id"];
|
|
||||||
//np.mType = json["type"];
|
|
||||||
//np.mBody = json["body"];
|
|
||||||
QJson::QObjectHelper::qvariant2qobject(variant,np);
|
QJson::QObjectHelper::qvariant2qobject(variant,np);
|
||||||
|
|
||||||
if (np->version() > CURRENT_PACKAGE_VERSION) {
|
if (np->version() > CURRENT_PACKAGE_VERSION) {
|
||||||
|
@ -93,6 +88,34 @@ void NetworkPackage::unserialize(const QByteArray& a, NetworkPackage* np)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkPackage::encrypt (QCA::PublicKey& key)
|
||||||
|
{
|
||||||
|
qDebug() << key.toDER() << key.canEncrypt();
|
||||||
|
|
||||||
|
QByteArray serialized = serialize();
|
||||||
|
QByteArray data = key.encrypt(serialized, QCA::EME_PKCS1v15).toByteArray();
|
||||||
|
|
||||||
|
mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||||
|
mType = "kdeconnect.encrypted";
|
||||||
|
mBody = QVariantMap();
|
||||||
|
mBody["data"] = data.toBase64();
|
||||||
|
mVersion = CURRENT_PACKAGE_VERSION;
|
||||||
|
mEncrypted = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkPackage::decrypt (QCA::PrivateKey& key, NetworkPackage* out) const
|
||||||
|
{
|
||||||
|
QByteArray encryptedJson = QByteArray::fromBase64(get<QByteArray>("data"));
|
||||||
|
|
||||||
|
QCA::SecureArray decryptedJson;
|
||||||
|
key.decrypt(encryptedJson, &decryptedJson, QCA::EME_PKCS1v15);
|
||||||
|
|
||||||
|
unserialize(decryptedJson.toByteArray(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void NetworkPackage::createIdentityPackage(NetworkPackage* np)
|
void NetworkPackage::createIdentityPackage(NetworkPackage* np)
|
||||||
{
|
{
|
||||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||||
|
@ -106,11 +129,4 @@ void NetworkPackage::createIdentityPackage(NetworkPackage* np)
|
||||||
//qDebug() << "createIdentityPackage" << np->serialize();
|
//qDebug() << "createIdentityPackage" << np->serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptedNetworkPackage NetworkPackage::encrypt ( const QSslKey& key ) const
|
|
||||||
{
|
|
||||||
QByteArray serialized = serialize();
|
|
||||||
return EncryptedNetworkPackage();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QSslKey>
|
#include <QtCrypto>
|
||||||
|
|
||||||
#include <qjson/parser.h>
|
#include <qjson/parser.h>
|
||||||
|
|
||||||
|
@ -38,24 +38,29 @@ class EncryptedNetworkPackage;
|
||||||
class NetworkPackage : public QObject
|
class NetworkPackage : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY( long id READ id WRITE setId )
|
Q_PROPERTY( QString id READ id WRITE setId )
|
||||||
Q_PROPERTY( QString type READ type WRITE setType )
|
Q_PROPERTY( QString type READ type WRITE setType )
|
||||||
Q_PROPERTY( QVariantMap body READ body WRITE setBody )
|
Q_PROPERTY( QVariantMap body READ body WRITE setBody )
|
||||||
Q_PROPERTY( int version READ version WRITE setVersion )
|
Q_PROPERTY( int version READ version WRITE setVersion )
|
||||||
|
Q_PROPERTY( bool isEncrypted READ isEncrypted WRITE setEncrypted )
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
NetworkPackage(const QString& type);
|
NetworkPackage(const QString& type);
|
||||||
|
|
||||||
static void unserialize(const QByteArray&, NetworkPackage*);
|
static void unserialize(const QByteArray& json, NetworkPackage* out);
|
||||||
QByteArray serialize() const;
|
QByteArray serialize() const;
|
||||||
|
|
||||||
|
void encrypt(QCA::PublicKey& key);
|
||||||
|
void decrypt(QCA::PrivateKey& key, NetworkPackage* out) const;
|
||||||
|
|
||||||
static void createIdentityPackage(NetworkPackage*);
|
static void createIdentityPackage(NetworkPackage*);
|
||||||
|
|
||||||
long id() const { return mId; }
|
QString id() const { return mId; }
|
||||||
const QString& type() const { return mType; }
|
const QString& type() const { return mType; }
|
||||||
QVariantMap& body() { return mBody; }
|
QVariantMap& body() { return mBody; }
|
||||||
int version() const { return mVersion; }
|
int version() const { return mVersion; }
|
||||||
|
bool isEncrypted() const { return mEncrypted; }
|
||||||
|
|
||||||
//Get and set info from body. Note that id, type and version can not be accessed through these.
|
//Get and set info from body. Note that id, type and version can not be accessed through these.
|
||||||
template<typename T> T get(const QString& key, const T& defaultValue = default_arg<T>::get()) const {
|
template<typename T> T get(const QString& key, const T& defaultValue = default_arg<T>::get()) const {
|
||||||
|
@ -65,16 +70,16 @@ public:
|
||||||
|
|
||||||
bool has(const QString& key) const { return mBody.contains(key); }
|
bool has(const QString& key) const { return mBody.contains(key); }
|
||||||
|
|
||||||
EncryptedNetworkPackage encrypt(const QSslKey& key) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setId(long id) { mId = id; }
|
void setId(QString id) { mId = id; }
|
||||||
void setType(const QString& t) { mType = t; }
|
void setType(const QString& t) { mType = t; }
|
||||||
void setBody(const QVariantMap& b) { mBody = b; }
|
void setBody(const QVariantMap& b) { mBody = b; }
|
||||||
void setVersion(int v) { mVersion = v; }
|
void setVersion(int v) { mVersion = v; }
|
||||||
|
void setEncrypted(bool b) { mEncrypted = b; }
|
||||||
|
|
||||||
long mId;
|
QString mId;
|
||||||
QString mType;
|
QString mType;
|
||||||
|
bool mEncrypted;
|
||||||
QVariantMap mBody; //json in the Android side
|
QVariantMap mBody; //json in the Android side
|
||||||
int mVersion;
|
int mVersion;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,10 @@ public Q_SLOTS:
|
||||||
private:
|
private:
|
||||||
Device* mDevice;
|
Device* mDevice;
|
||||||
|
|
||||||
|
// The Initializer object sets things up, and also does cleanup when it goes out of scope
|
||||||
|
// Since the plugins use their own memory, they need their own initializer in order to send encrypted packages
|
||||||
|
QCA::Initializer init;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@ find_package(KDE4 REQUIRED)
|
||||||
find_package(QJSON REQUIRED)
|
find_package(QJSON REQUIRED)
|
||||||
find_package(QCA2 REQUIRED)
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include(KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include_directories(${KDE4_INCLUDES})
|
include_directories(${KDE4_INCLUDES})
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
set(kded_kdeconnect_tests_SRCS
|
|
||||||
../daemon/networkpackage.cpp
|
|
||||||
backendtests.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
kde4_add_unit_test(kded_kdeconnect_tests ${kded_kdeconnect_tests_SRCS})
|
|
||||||
|
|
||||||
target_link_libraries(kded_kdeconnect_tests
|
|
||||||
${KDE4_KDECORE_LIBS}
|
|
||||||
${KDE4_KDEUI_LIBS}
|
|
||||||
kdnssd
|
|
||||||
qjson
|
|
||||||
${QT_QTTEST_LIBRARY}
|
|
||||||
${QT_QTNETWORK_LIBRARY}
|
|
||||||
)
|
|
||||||
|
|
||||||
add_test(kded_kdeconnect_tests ${CMAKE_CURRENT_BINARY_DIR}/kded_kdeconnect_tests)
|
|
||||||
|
|
25
tests/CMakeLists.txt
Normal file
25
tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
|
||||||
|
|
||||||
|
find_package(QJSON REQUIRED)
|
||||||
|
find_package(QCA2 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QJSON_INCLUDE_DIR}
|
||||||
|
${QCA2_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(kdeconnect_tests_SRCS
|
||||||
|
../daemon/networkpackage.cpp
|
||||||
|
networkpackagetests.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
kde4_add_unit_test(kdeconnect_tests ${kdeconnect_tests_SRCS})
|
||||||
|
|
||||||
|
target_link_libraries(kdeconnect_tests
|
||||||
|
${KDE4_KDECORE_LIBS}
|
||||||
|
${KDE4_KDEUI_LIBS}
|
||||||
|
${QJSON_LIBRARIES}
|
||||||
|
${QCA2_LIBRARIES}
|
||||||
|
${QT_QTTEST_LIBRARY}
|
||||||
|
${QT_QTNETWORK_LIBRARY}
|
||||||
|
)
|
|
@ -18,21 +18,21 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "backendtests.h"
|
#include "networkpackagetests.h"
|
||||||
|
|
||||||
#include "../daemon/networkpackage.h"
|
#include "../daemon/networkpackage.h"
|
||||||
|
|
||||||
#include <qtest_kde.h>
|
#include <qtest_kde.h>
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
QTEST_KDEMAIN(BackendTests, NoGUI);
|
QTEST_KDEMAIN(NetworkPackageTests, NoGUI);
|
||||||
|
|
||||||
void BackendTests::initTestCase()
|
void NetworkPackageTests::initTestCase()
|
||||||
{
|
{
|
||||||
// Called before the first testfunction is executed
|
// Called before the first testfunction is executed
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTests::dummyTest()
|
void NetworkPackageTests::dummyTest()
|
||||||
{
|
{
|
||||||
QDate date;
|
QDate date;
|
||||||
date.setYMD( 1967, 3, 11 );
|
date.setYMD( 1967, 3, 11 );
|
||||||
|
@ -41,7 +41,7 @@ void BackendTests::dummyTest()
|
||||||
QCOMPARE( QDate::longMonthName(date.month()), QString("March") );
|
QCOMPARE( QDate::longMonthName(date.month()), QString("March") );
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTests::networkPackageTest()
|
void NetworkPackageTests::networkPackageTest()
|
||||||
{
|
{
|
||||||
NetworkPackage np("com.test");
|
NetworkPackage np("com.test");
|
||||||
|
|
||||||
|
@ -64,10 +64,10 @@ void BackendTests::networkPackageTest()
|
||||||
QCOMPARE( np.version(), np2.version() );
|
QCOMPARE( np.version(), np2.version() );
|
||||||
QCOMPARE( np.body(), np2.body() );
|
QCOMPARE( np.body(), np2.body() );
|
||||||
|
|
||||||
QByteArray json("{ \"id\": 123, \"type\": \"test\", \"body\": { \"testing\": true }, \"version\": 3 }");
|
QByteArray json("{ \"id\": \"123\", \"type\": \"test\", \"body\": { \"testing\": true }, \"version\": 3 }");
|
||||||
//qDebug() << json;
|
//qDebug() << json;
|
||||||
NetworkPackage::unserialize(json,&np2);
|
NetworkPackage::unserialize(json,&np2);
|
||||||
QCOMPARE( np2.id(), long(123) );
|
QCOMPARE( np2.id(), QString("123") );
|
||||||
QCOMPARE( np2.version(), 3 );
|
QCOMPARE( np2.version(), 3 );
|
||||||
QCOMPARE( (np2.get<bool>("testing")), true );
|
QCOMPARE( (np2.get<bool>("testing")), true );
|
||||||
QCOMPARE( (np2.get<bool>("not_testing")), false );
|
QCOMPARE( (np2.get<bool>("not_testing")), false );
|
||||||
|
@ -78,22 +78,53 @@ void BackendTests::networkPackageTest()
|
||||||
//QtTest::ignoreMessage(QtDebugMsg, "Unserialization error: 1 \"syntax error, unexpected string\"");
|
//QtTest::ignoreMessage(QtDebugMsg, "Unserialization error: 1 \"syntax error, unexpected string\"");
|
||||||
//QCOMPARE( np2.version(), -1 );
|
//QCOMPARE( np2.version(), -1 );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkPackageTests::networkPackageEncryptionTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
NetworkPackage original("com.test");
|
||||||
|
original.set("hello","hola");
|
||||||
|
|
||||||
|
NetworkPackage copy("");
|
||||||
|
NetworkPackage::unserialize(original.serialize(), ©);
|
||||||
|
|
||||||
|
NetworkPackage decrypted("");
|
||||||
|
|
||||||
|
QCA::Initializer init;
|
||||||
|
QCA::PrivateKey privateKey = QCA::KeyGenerator().createRSA(1024);
|
||||||
|
QCA::PublicKey publicKey = privateKey.toPublicKey();
|
||||||
|
|
||||||
|
//Encrypt and decrypt np
|
||||||
|
QCOMPARE( original.isEncrypted(), false );
|
||||||
|
original.encrypt(publicKey);
|
||||||
|
QCOMPARE( original.isEncrypted(), true );
|
||||||
|
original.decrypt(privateKey, &decrypted);
|
||||||
|
QCOMPARE( original.isEncrypted(), true );
|
||||||
|
QCOMPARE( decrypted.isEncrypted(), false );
|
||||||
|
|
||||||
|
//np should be equal top np2
|
||||||
|
QCOMPARE( decrypted.id(), copy.id() );
|
||||||
|
QCOMPARE( decrypted.type(), copy.type() );
|
||||||
|
QCOMPARE( decrypted.version(), copy.version() );
|
||||||
|
QCOMPARE( decrypted.body(), copy.body() );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BackendTests::cleanupTestCase()
|
void NetworkPackageTests::cleanupTestCase()
|
||||||
{
|
{
|
||||||
// Called after the last testfunction was executed
|
// Called after the last testfunction was executed
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTests::init()
|
void NetworkPackageTests::init()
|
||||||
{
|
{
|
||||||
// Called before each testfunction is executed
|
// Called before each testfunction is executed
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTests::cleanup()
|
void NetworkPackageTests::cleanup()
|
||||||
{
|
{
|
||||||
// Called after every testfunction
|
// Called after every testfunction
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "backendtests.moc"
|
|
|
@ -18,12 +18,12 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BACKENDTESTS_H
|
#ifndef NETWORKPACKAGETESTS_H
|
||||||
#define BACKENDTESTS_H
|
#define NETWORKPACKAGETESTS_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class BackendTests : public QObject
|
class NetworkPackageTests : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ private Q_SLOTS:
|
||||||
|
|
||||||
void dummyTest();
|
void dummyTest();
|
||||||
void networkPackageTest();
|
void networkPackageTest();
|
||||||
|
void networkPackageEncryptionTest();
|
||||||
|
|
||||||
void cleanupTestCase();
|
void cleanupTestCase();
|
||||||
|
|
||||||
|
@ -40,4 +41,4 @@ private Q_SLOTS:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BACKENDTESTS_H
|
#endif
|
Loading…
Reference in a new issue