Simplified NetworkPackage format
Now protocol version is only sent once (in the identity package) Removed "isEncrypted" bit, using type "kdeconnect.encrypted" instead. NetworkPackage's decrypt and unserialize return false when find problems Updated tests.
This commit is contained in:
parent
36e5d41811
commit
892385f3fc
7 changed files with 57 additions and 64 deletions
|
@ -152,7 +152,7 @@ void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink*
|
|||
}
|
||||
|
||||
Q_EMIT deviceVisibilityChanged(id, true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Daemon::onDeviceReachableStatusChanged()
|
||||
|
|
|
@ -44,6 +44,12 @@ Device::Device(const NetworkPackage& identityPackage, DeviceLink* dl)
|
|||
m_deviceId = identityPackage.get<QString>("deviceId");
|
||||
m_deviceName = identityPackage.get<QString>("deviceName");
|
||||
|
||||
int protocolVersion = identityPackage.get<int>("protocolVersion");
|
||||
if (protocolVersion != NetworkPackage::ProtocolVersion) {
|
||||
qDebug() << "WARNING: Device uses a different protocol version" << protocolVersion << "expected" << NetworkPackage::ProtocolVersion;
|
||||
//TODO: Do something
|
||||
}
|
||||
|
||||
addLink(dl);
|
||||
|
||||
m_pairStatus = Device::Device::NotPaired;
|
||||
|
@ -320,14 +326,7 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
|
|||
|
||||
} else {
|
||||
|
||||
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);
|
||||
|
||||
} else {
|
||||
if (np.type() == PACKAGE_TYPE_ENCRYPTED) {
|
||||
|
||||
//TODO: Do not read the key every time
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
|
@ -338,6 +337,15 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
|
|||
NetworkPackage decryptedNp("");
|
||||
np.decrypt(privateKey, &decryptedNp);
|
||||
Q_EMIT receivedPackage(decryptedNp);
|
||||
|
||||
} else {
|
||||
|
||||
//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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -76,9 +76,9 @@ void LanLinkProvider::newUdpConnection()
|
|||
mUdpServer->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
|
||||
|
||||
NetworkPackage* np = new NetworkPackage("");
|
||||
NetworkPackage::unserialize(datagram,np);
|
||||
bool success = NetworkPackage::unserialize(datagram,np);
|
||||
|
||||
if (np->version() > 0 && np->type() == PACKAGE_TYPE_IDENTITY) {
|
||||
if (success && np->type() == PACKAGE_TYPE_IDENTITY) {
|
||||
|
||||
NetworkPackage np2("");
|
||||
NetworkPackage::createIdentityPackage(&np2);
|
||||
|
@ -206,9 +206,9 @@ void LanLinkProvider::dataReceived()
|
|||
qDebug() << "LanLinkProvider received reply:" << data;
|
||||
|
||||
NetworkPackage np("");
|
||||
NetworkPackage::unserialize(data,&np);
|
||||
bool success = NetworkPackage::unserialize(data,&np);
|
||||
|
||||
if (np.version() > 0 && np.type() == PACKAGE_TYPE_IDENTITY) {
|
||||
if (success && np.type() == PACKAGE_TYPE_IDENTITY) {
|
||||
|
||||
const QString& id = np.get<QString>("deviceId");
|
||||
LanDeviceLink* dl = new LanDeviceLink(id, this, socket);
|
||||
|
|
|
@ -32,15 +32,11 @@
|
|||
#include <qjson/serializer.h>
|
||||
#include <qjson/qobjecthelper.h>
|
||||
|
||||
const static int CURRENT_PACKAGE_VERSION = 2;
|
||||
|
||||
NetworkPackage::NetworkPackage(const QString& type)
|
||||
{
|
||||
mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||
mType = type;
|
||||
mBody = QVariantMap();
|
||||
mVersion = CURRENT_PACKAGE_VERSION;
|
||||
mEncrypted = false;
|
||||
}
|
||||
|
||||
QByteArray NetworkPackage::serialize() const
|
||||
|
@ -66,7 +62,7 @@ QByteArray NetworkPackage::serialize() const
|
|||
return json;
|
||||
}
|
||||
|
||||
void NetworkPackage::unserialize(const QByteArray& a, NetworkPackage* np)
|
||||
bool NetworkPackage::unserialize(const QByteArray& a, NetworkPackage* np)
|
||||
{
|
||||
qDebug() << "Unserialize: " << a;
|
||||
|
||||
|
@ -76,15 +72,13 @@ void NetworkPackage::unserialize(const QByteArray& a, NetworkPackage* np)
|
|||
QVariantMap variant = parser.parse(a, &ok).toMap();
|
||||
if (!ok) {
|
||||
qDebug() << "Unserialization error:" << parser.errorLine() << parser.errorString();
|
||||
np->setVersion(-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
//QVariant -> Object
|
||||
QJson::QObjectHelper::qvariant2qobject(variant,np);
|
||||
|
||||
if (np->version() > CURRENT_PACKAGE_VERSION) {
|
||||
qDebug() << "Warning: package version" << np->version() << "is greater than supported version" << CURRENT_PACKAGE_VERSION;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
@ -93,28 +87,26 @@ void NetworkPackage::encrypt (QCA::PublicKey& key)
|
|||
|
||||
QByteArray serialized = serialize();
|
||||
|
||||
int chunkSize = key.maximumEncryptSize(QCA::EME_PKCS1v15);
|
||||
int chunkSize = key.maximumEncryptSize(NetworkPackage::EncryptionAlgorithm);
|
||||
|
||||
QStringList chunks;
|
||||
while (!serialized.isEmpty()) {
|
||||
QByteArray chunk = serialized.left(chunkSize);
|
||||
serialized = serialized.mid(chunkSize);
|
||||
QByteArray encryptedChunk = key.encrypt(chunk, QCA::EME_PKCS1v15).toByteArray();
|
||||
QByteArray encryptedChunk = key.encrypt(chunk, NetworkPackage::EncryptionAlgorithm).toByteArray();
|
||||
chunks.append( encryptedChunk.toBase64() );
|
||||
}
|
||||
|
||||
qDebug() << chunks.size() << "chunks";
|
||||
|
||||
mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||
mType = "kdeconnect.encrypted";
|
||||
mType = PACKAGE_TYPE_ENCRYPTED;
|
||||
mBody = QVariantMap();
|
||||
mBody["data"] = chunks;
|
||||
mVersion = CURRENT_PACKAGE_VERSION;
|
||||
mEncrypted = true;
|
||||
|
||||
}
|
||||
|
||||
void NetworkPackage::decrypt (QCA::PrivateKey& key, NetworkPackage* out) const
|
||||
bool NetworkPackage::decrypt (QCA::PrivateKey& key, NetworkPackage* out) const
|
||||
{
|
||||
const QStringList& chunks = mBody["data"].toStringList();
|
||||
|
||||
|
@ -122,12 +114,16 @@ void NetworkPackage::decrypt (QCA::PrivateKey& key, NetworkPackage* out) const
|
|||
Q_FOREACH(const QString& chunk, chunks) {
|
||||
QByteArray encryptedChunk = QByteArray::fromBase64(chunk.toAscii());
|
||||
QCA::SecureArray decryptedChunk;
|
||||
key.decrypt(encryptedChunk, &decryptedChunk, QCA::EME_PKCS1v15);
|
||||
bool success = key.decrypt(encryptedChunk, &decryptedChunk, NetworkPackage::EncryptionAlgorithm);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
decryptedJson.append(decryptedChunk.toByteArray());
|
||||
|
||||
}
|
||||
|
||||
unserialize(decryptedJson, out);
|
||||
return unserialize(decryptedJson, out);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,11 +132,10 @@ void NetworkPackage::createIdentityPackage(NetworkPackage* np)
|
|||
{
|
||||
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
||||
QString id = config->group("myself").readEntry<QString>("id","");
|
||||
np->mId = time(NULL);
|
||||
np->mType = PACKAGE_TYPE_IDENTITY;
|
||||
np->mVersion = CURRENT_PACKAGE_VERSION;
|
||||
np->mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||
np->set("deviceId", id);
|
||||
np->set("deviceName", QHostInfo::localHostName());
|
||||
np->set("protocolVersion", NetworkPackage::ProtocolVersion);
|
||||
|
||||
//qDebug() << "createIdentityPackage" << np->serialize();
|
||||
}
|
||||
|
|
|
@ -41,47 +41,41 @@ class NetworkPackage : public QObject
|
|||
Q_PROPERTY( QString id READ id WRITE setId )
|
||||
Q_PROPERTY( QString type READ type WRITE setType )
|
||||
Q_PROPERTY( QVariantMap body READ body WRITE setBody )
|
||||
Q_PROPERTY( int version READ version WRITE setVersion )
|
||||
Q_PROPERTY( bool isEncrypted READ isEncrypted WRITE setEncrypted )
|
||||
|
||||
public:
|
||||
|
||||
const static QCA::EncryptionAlgorithm EncryptionAlgorithm = QCA::EME_PKCS1v15;
|
||||
const static int ProtocolVersion = 3;
|
||||
|
||||
NetworkPackage(const QString& type);
|
||||
|
||||
static void unserialize(const QByteArray& json, NetworkPackage* out);
|
||||
QByteArray serialize() const;
|
||||
|
||||
void encrypt(QCA::PublicKey& key);
|
||||
void decrypt(QCA::PrivateKey& key, NetworkPackage* out) const;
|
||||
|
||||
static void createIdentityPackage(NetworkPackage*);
|
||||
|
||||
QString id() const { return mId; }
|
||||
QByteArray serialize() const;
|
||||
static bool unserialize(const QByteArray& json, NetworkPackage* out);
|
||||
|
||||
void encrypt(QCA::PublicKey& key);
|
||||
bool decrypt(QCA::PrivateKey& key, NetworkPackage* out) const;
|
||||
|
||||
const QString& id() const { return mId; }
|
||||
const QString& type() const { return mType; }
|
||||
QVariantMap& body() { return mBody; }
|
||||
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 and type can not be accessed through these.
|
||||
template<typename T> T get(const QString& key, const T& defaultValue = default_arg<T>::get()) const {
|
||||
return mBody.value(key,defaultValue).template value<T>(); //Important note: Awesome template syntax is awesome
|
||||
}
|
||||
template<typename T> void set(const QString& key, const T& value) { mBody[key] = QVariant(value); }
|
||||
|
||||
bool has(const QString& key) const { return mBody.contains(key); }
|
||||
|
||||
private:
|
||||
void setId(QString id) { mId = id; }
|
||||
void setId(const QString& id) { mId = id; }
|
||||
void setType(const QString& t) { mType = t; }
|
||||
void setBody(const QVariantMap& b) { mBody = b; }
|
||||
void setVersion(int v) { mVersion = v; }
|
||||
void setEncrypted(bool b) { mEncrypted = b; }
|
||||
|
||||
QString mId;
|
||||
QString mType;
|
||||
bool mEncrypted;
|
||||
QVariantMap mBody; //json in the Android side
|
||||
int mVersion;
|
||||
QVariantMap mBody;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define PACKAGE_TYPE_IDENTITY QString("kdeconnect.identity")
|
||||
#define PACKAGE_TYPE_PAIR QString("kdeconnect.pair")
|
||||
#define PACKAGE_TYPE_PING QString("kdeconnect.ping")
|
||||
#define PACKAGE_TYPE_ENCRYPTED QString("kdeconnect.encrypted")
|
||||
#define PACKAGE_TYPE_NOTIFICATION QString("kdeconnect.notification")
|
||||
#define PACKAGE_TYPE_BATTERY QString("kdeconnect.battery")
|
||||
#define PACKAGE_TYPE_TELEPHONY QString("kdeconnect.telephony")
|
||||
|
|
|
@ -63,14 +63,12 @@ void NetworkPackageTests::networkPackageTest()
|
|||
|
||||
QCOMPARE( np.id(), np2.id() );
|
||||
QCOMPARE( np.type(), np2.type() );
|
||||
QCOMPARE( np.version(), np2.version() );
|
||||
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 } }");
|
||||
//qDebug() << json;
|
||||
NetworkPackage::unserialize(json,&np2);
|
||||
QCOMPARE( np2.id(), QString("123") );
|
||||
QCOMPARE( np2.version(), 3 );
|
||||
QCOMPARE( (np2.get<bool>("testing")), true );
|
||||
QCOMPARE( (np2.get<bool>("not_testing")), false );
|
||||
QCOMPARE( (np2.get<bool>("not_testing",true)), true );
|
||||
|
@ -78,8 +76,6 @@ void NetworkPackageTests::networkPackageTest()
|
|||
//NetworkPackage::unserialize("this is not json",&np2);
|
||||
//QtTest::ignoreMessage(QtSystemMsg, "json_parser - syntax error found, forcing abort, Line 1 Column 0");
|
||||
//QtTest::ignoreMessage(QtDebugMsg, "Unserialization error: 1 \"syntax error, unexpected string\"");
|
||||
//QCOMPARE( np2.version(), -1 );
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -100,27 +96,26 @@ void NetworkPackageTests::networkPackageEncryptionTest()
|
|||
|
||||
|
||||
//Encrypt and decrypt np
|
||||
QCOMPARE( original.isEncrypted(), false );
|
||||
QCOMPARE( original.type(), QString("com.test") );
|
||||
original.encrypt(publicKey);
|
||||
QCOMPARE( original.isEncrypted(), true );
|
||||
QCOMPARE( original.type(), PACKAGE_TYPE_ENCRYPTED );
|
||||
original.decrypt(privateKey, &decrypted);
|
||||
QCOMPARE( original.isEncrypted(), true );
|
||||
QCOMPARE( decrypted.isEncrypted(), false );
|
||||
QCOMPARE( original.type(), PACKAGE_TYPE_ENCRYPTED );
|
||||
QCOMPARE( decrypted.type(), QString("com.test") );
|
||||
|
||||
//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() );
|
||||
|
||||
|
||||
|
||||
//Test for long package encryption that need multi-chunk encryption
|
||||
|
||||
QByteArray json = "{ \"body\" : { \"nowPlaying\" : \"A really long song name - A really long artist name\", \"player\" : \"A really long player name\" }, \"id\" : \"A really long package id\", \"isEncrypted\" : false, \"type\" : \"kdeconnect.a_really_long_package_type\", \"version\" : 2 }\n";
|
||||
QByteArray json = "{ \"body\" : { \"nowPlaying\" : \"A really long song name - A really long artist name\", \"player\" : \"A really long player name\", \"the_meaning_of_life_the_universe_and_everything\" : \"42\" }, \"id\" : \"A really long package id\", \"type\" : \"kdeconnect.a_really_really_long_package_type\" }\n";
|
||||
qDebug() << "EME_PKCS1_OAEP maximumEncryptSize" << publicKey.maximumEncryptSize(QCA::EME_PKCS1_OAEP);
|
||||
qDebug() << "EME_PKCS1v15 maximumEncryptSize" << publicKey.maximumEncryptSize(QCA::EME_PKCS1v15);
|
||||
QCOMPARE( json.size() > publicKey.maximumEncryptSize(QCA::EME_PKCS1v15), true );
|
||||
QCOMPARE( json.size() > publicKey.maximumEncryptSize(NetworkPackage::EncryptionAlgorithm), true );
|
||||
|
||||
NetworkPackage::unserialize(json, &original);
|
||||
original.encrypt(publicKey);
|
||||
|
|
Loading…
Reference in a new issue