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:
Albert Vaca 2013-09-02 13:26:26 +02:00
parent 36e5d41811
commit 892385f3fc
7 changed files with 57 additions and 64 deletions

View file

@ -152,7 +152,7 @@ void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink*
}
Q_EMIT deviceVisibilityChanged(id, true);
}
void Daemon::onDeviceReachableStatusChanged()

View file

@ -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);
}

View file

@ -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);

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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")

View file

@ -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);