1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Teach the AutoUpdater to read a flist

We can now read the list of files in an update, along with the install path, file size, and signature

Everything, metadata and files, is signed with ed25519, but not encrypted
This commit is contained in:
Tux3 / Mlkj / !Lev.uXFMLA 2014-11-09 17:55:38 +01:00
parent 747fd33592
commit 965254d151
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
6 changed files with 474 additions and 8 deletions

View File

@ -147,7 +147,8 @@ HEADERS += src/widget/form/addfriendform.h \
src/widget/toxuri.h \
src/toxdns.h \
src/widget/toxsave.h \
src/autoupdate.h
src/autoupdate.h \
src/misc/serialize.h
SOURCES += \
src/widget/form/addfriendform.cpp \
@ -211,4 +212,5 @@ SOURCES += \
src/toxdns.cpp \
src/ipc.cpp \
src/widget/toxsave.cpp \
src/autoupdate.cpp
src/autoupdate.cpp \
src/misc/serialize.cpp

View File

@ -1,10 +1,26 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
This file is part of qTox, a Qt-based graphical interface for Tox.
This program is libre 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 3 of the License, or
(at your option) any later version.
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 COPYING file for more details.
*/
#include "src/autoupdate.h"
#include "src/misc/serialize.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QCoreApplication>
#include <sodium.h>
#ifdef _WIN32
const QString AutoUpdater::platform = "win32";
#else
@ -38,7 +54,6 @@ QString AutoUpdater::getUpdateVersion()
return version;
QNetworkAccessManager *manager = new QNetworkAccessManager;
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(checkURI)));
while (!reply->isFinished())
qApp->processEvents();
@ -46,15 +61,21 @@ QString AutoUpdater::getUpdateVersion()
if (reply->error() != QNetworkReply::NoError)
{
qWarning() << "AutoUpdater: getUpdateVersion: network error: "<<reply->errorString();
reply->deleteLater();
manager->deleteLater();
return version;
}
QByteArray data = reply->readAll();
reply->deleteLater();
manager->deleteLater();
if (data.size() < (int)(1+crypto_sign_BYTES))
return version;
// Check updater protocol version
if ((int)data[0] != '1')
{
qWarning() << "AutoUpdater: getUpdateVersion: Bad protocol version "<<(uint8_t)data[0];
qWarning() << "AutoUpdater: getUpdateVersion: Bad version "<<(uint8_t)data[0];
return version;
}
@ -74,3 +95,109 @@ QString AutoUpdater::getUpdateVersion()
return version;
}
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::genUpdateDiff()
{
QList<UpdateFileMeta> diff;
// Updates only for supported platforms
if (platform.isEmpty())
return diff;
QList<UpdateFileMeta> newFlist = getUpdateFlist();
return diff;
}
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseflist(QByteArray flistData)
{
QList<UpdateFileMeta> flist;
if (flistData.isEmpty())
{
qWarning() << "AutoUpdater::parseflist: Empty data";
return flist;
}
// Check version
if (flistData[0] != '1')
{
qWarning() << "AutoUpdater: parseflist: Bad version "<<(uint8_t)flistData[0];
return flist;
}
flistData = flistData.mid(1);
// Check signature
if (flistData.size() < (int)(crypto_sign_BYTES))
{
qWarning() << "AutoUpdater::parseflist: Truncated data";
return flist;
}
else
{
QByteArray msgData = flistData.mid(crypto_sign_BYTES);
unsigned char* msg = (unsigned char*)msgData.data();
if (crypto_sign_verify_detached((unsigned char*)flistData.data(), msg, msgData.size(), key) != 0)
{
qCritical() << "AutoUpdater: parseflist: FORGED FLIST FILE";
return flist;
}
flistData = flistData.mid(crypto_sign_BYTES);
}
// Parse. We assume no errors handling needed since the signature is valid.
while (!flistData.isEmpty())
{
qDebug() << "Got "<<flistData.size()<<" bytes of data left";
UpdateFileMeta newFile;
memcpy(newFile.sig, flistData.data(), crypto_sign_BYTES);
flistData = flistData.mid(crypto_sign_BYTES);
newFile.id = dataToString(flistData);
flistData = flistData.mid(newFile.id.size() + getVUint32Size(flistData));
newFile.installpath = dataToString(flistData);
flistData = flistData.mid(newFile.installpath.size() + getVUint32Size(flistData));
newFile.size = dataToUint64(flistData);
flistData = flistData.mid(8);
qDebug() << "AutoUpdater::parseflist: New file:";
qDebug() << "- Id: "<<newFile.id;
qDebug() << "- Install path: "<<newFile.installpath;
qDebug() << "- Size: "<<newFile.size<<" bytes";
qDebug() << "- Signature: "<<QByteArray((char*)newFile.sig, crypto_sign_BYTES).toHex();
flist += newFile;
}
qDebug() << "AutoUpdater::parseflist: Done parsing flist";
return flist;
}
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::getUpdateFlist()
{
QList<UpdateFileMeta> flist;
QNetworkAccessManager *manager = new QNetworkAccessManager;
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(flistURI)));
while (!reply->isFinished())
qApp->processEvents();
if (reply->error() != QNetworkReply::NoError)
{
qWarning() << "AutoUpdater: getUpdateFlist: network error: "<<reply->errorString();
reply->deleteLater();
manager->deleteLater();
return flist;
}
QByteArray data = reply->readAll();
reply->deleteLater();
manager->deleteLater();
flist = parseflist(data);
return flist;
}

View File

@ -19,10 +19,27 @@
#define AUTOUPDATE_H
#include <QString>
#include <QList>
#include <sodium.h>
/// Handles checking and applying updates for qTox
class AutoUpdater
{
public:
struct UpdateFileMeta
{
unsigned char sig[crypto_sign_BYTES]; ///< Signature of the file (ed25519)
QString id; ///< Unique id of the file
QString installpath; ///< Local path including the file name. May be relative to qtox-updater or absolute
uint64_t size; ///< Size in bytes of the file
};
struct UpdateFile
{
UpdateFileMeta metadata;
QByteArray data;
};
public:
/// Connects to the qTox update server, returns true if an update is available for download
/// Will call getUpdateVersion, and as such may block and processEvents
@ -30,6 +47,16 @@ public:
/// Fetch the version string of the last update available from the qTox update server
/// Will try to follow qTox's proxy settings, may block and processEvents
static QString getUpdateVersion();
/// Generates a list of files we need to update
/// Will try to follow qTox's proxy settings, may block and processEvents
static QList<UpdateFileMeta> genUpdateDiff();
protected:
/// Parses and validates a flist file. Returns an empty list on error
static QList<UpdateFileMeta> parseflist(QByteArray flistData);
/// Get the update server's flist and parse it. Returns an empty list on error
/// Will try to follow qTox's proxy settings, may block and processEvents
static QList<UpdateFileMeta> getUpdateFlist();
private:
AutoUpdater() = delete;

254
src/misc/serialize.cpp Normal file
View File

@ -0,0 +1,254 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
This file is part of qTox, a Qt-based graphical interface for Tox.
This program is libre 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 3 of the License, or
(at your option) any later version.
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 COPYING file for more details.
*/
#include "src/misc/serialize.h"
QByteArray doubleToData(double num)
{
union
{
char tab[8];
double n;
} castUnion;
//char n[8];
//*((double*) n) = num;
castUnion.n=num;
return QByteArray(castUnion.tab,8);
}
QByteArray floatToData(float num)
{
union
{
char tab[4];
float n;
} castUnion;
castUnion.n=num;
return QByteArray(castUnion.tab,4);
}
float dataToFloat(QByteArray data)
{
union
{
char tab[4];
float n;
} castUnion;
castUnion.tab[0]=data.data()[0];
castUnion.tab[1]=data.data()[1];
castUnion.tab[2]=data.data()[2];
castUnion.tab[3]=data.data()[3];
return castUnion.n;
}
// Converts a string into PNet string data
QByteArray stringToData(QString str)
{
QByteArray data(4,0);
// Write the size in a Uint of variable lenght (8-32 bits)
int i=0;
uint num1 = (uint)str.toUtf8().size();
while (num1 >= 0x80)
{
data[i] = (unsigned char)(num1 | 0x80); i++;
num1 = num1 >> 7;
}
data[i]=num1;
data.resize(i+1);
data+=str.toUtf8();
return data;
}
QString dataToString(QByteArray data)
{
// Variable UInt32
unsigned char num3;
int num = 0;
int num2 = 0;
int i=0;
do
{
num3 = data[i]; i++;
num |= (num3 & 0x7f) << num2;
num2 += 7;
} while ((num3 & 0x80) != 0);
unsigned int strlen = (uint) num;
if (!strlen)
return QString();
data = data.right(data.size()-i); // Remove the strlen
data.truncate(strlen);
return QString(data);
}
float dataToRangedSingle(float min, float max, int numberOfBits, QByteArray data)
{
uint endvalue=0;
uint value=0;
if (numberOfBits <= 8)
{
endvalue = (uchar)data[0];
goto done;
}
value = (uchar)data[0];
numberOfBits -= 8;
if (numberOfBits <= 8)
{
endvalue = (value | ((uint) ((uchar)data[1]) << 8));
goto done;
}
value |= (uint) (((uchar)data[1]) << 8);
numberOfBits -= 8;
if (numberOfBits <= 8)
{
uint num2 = (uint) (((uchar)data[2]) << 0x10);
endvalue = (value | num2);
goto done;
}
value |= (uint) (((uchar)data[2]) << 0x10);
numberOfBits -= 8;
endvalue = (value | ((uint) (((uchar)data[3]) << 0x18)));
goto done;
done:
float num = max - min;
int num2 = (((int) 1) << numberOfBits) - 1;
float num3 = endvalue;
float num4 = num3 / ((float) num2);
return (min + (num4 * num));
}
QByteArray rangedSingleToData(float value, float min, float max, int numberOfBits)
{
QByteArray data;
float num = max - min;
float num2 = (value - min) / num;
int num3 = (((int) 1) << numberOfBits) - 1;
uint source = num3 * num2;
if (numberOfBits <= 8)
{
data += (unsigned char)source;
return data;
}
data += (unsigned char)source;
numberOfBits -= 8;
if (numberOfBits <= 8)
{
data += (unsigned char)source>>8;
return data;
}
data += (unsigned char)source>>8;
numberOfBits -= 8;
if (numberOfBits <= 8)
{
data += (unsigned char)source>>16;
return data;
}
data += (unsigned char)source>>16;
data += (unsigned char)source>>24;
return data;
}
uint8_t dataToUint8(QByteArray data)
{
return (uint8_t)data[0];
}
uint16_t dataToUint16(QByteArray data)
{
return ((uint16_t)(uint8_t)data[0])
+(((uint16_t)(uint8_t)data[1])<<8);
}
uint32_t dataToUint32(QByteArray data)
{
return ((uint32_t)(uint8_t)data[0])
+(((uint32_t)(uint8_t)data[1])<<8)
+(((uint32_t)(uint8_t)data[2])<<16)
+(((uint32_t)(uint8_t)data[3])<<24);
}
uint64_t dataToUint64(QByteArray data)
{
return ((uint64_t)(uint8_t)data[0])
+(((uint64_t)(uint8_t)data[1])<<8)
+(((uint64_t)(uint8_t)data[2])<<16)
+(((uint64_t)(uint8_t)data[3])<<24)
+(((uint64_t)(uint8_t)data[4])<<32)
+(((uint64_t)(uint8_t)data[5])<<40)
+(((uint64_t)(uint8_t)data[6])<<48)
+(((uint64_t)(uint8_t)data[7])<<56);
}
unsigned getVUint32Size(QByteArray data)
{
unsigned lensize=0;
{
unsigned char num3;
do {
num3 = data[lensize];
lensize++;
} while ((num3 & 0x80) != 0);
}
return lensize;
}
QByteArray uint8ToData(uint8_t num)
{
QByteArray data(1,0);
data[0] = (uint8_t)num;
return data;
}
QByteArray uint16ToData(uint16_t num)
{
QByteArray data(2,0);
data[0] = (uint8_t)(num & 0xFF);
data[1] = (uint8_t)((num>>8) & 0xFF);
return data;
}
QByteArray uint32ToData(uint32_t num)
{
QByteArray data(4,0);
data[0] = (uint8_t)(num & 0xFF);
data[1] = (uint8_t)((num>>8) & 0xFF);
data[2] = (uint8_t)((num>>16) & 0xFF);
data[3] = (uint8_t)((num>>24) & 0xFF);
return data;
}
QByteArray uint64ToData(uint64_t num)
{
QByteArray data(8,0);
data[0] = (uint8_t)(num & 0xFF);
data[1] = (uint8_t)((num>>8) & 0xFF);
data[2] = (uint8_t)((num>>16) & 0xFF);
data[3] = (uint8_t)((num>>24) & 0xFF);
data[4] = (uint8_t)((num>>32) & 0xFF);
data[5] = (uint8_t)((num>>40) & 0xFF);
data[6] = (uint8_t)((num>>48) & 0xFF);
data[7] = (uint8_t)((num>>56) & 0xFF);
return data;
}

45
src/misc/serialize.h Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
This file is part of qTox, a Qt-based graphical interface for Tox.
This program is libre 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 3 of the License, or
(at your option) any later version.
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 COPYING file for more details.
*/
#ifndef SERIALIZE_H
#define SERIALIZE_H
#include <cstdint>
#include <QByteArray>
#include <QString>
/// Most of those functions are unsafe unless otherwise specified
/// Do not use them on untrusted data (e.g. check a signature first)
QByteArray doubleToData(double num);
QByteArray floatToData(float num);
float dataToFloat(QByteArray data);
QByteArray stringToData(QString str);
QString dataToString(QByteArray data);
float dataToRangedSingle(float min, float max, int numberOfBits, QByteArray data);
QByteArray rangedSingleToData(float value, float min, float max, int numberOfBits);
uint8_t dataToUint8(QByteArray data);
uint16_t dataToUint16(QByteArray data);
uint32_t dataToUint32(QByteArray data);
uint64_t dataToUint64(QByteArray data);
unsigned getVUint32Size(QByteArray data);
QByteArray uint8ToData(uint8_t num);
QByteArray uint16ToData(uint16_t num);
QByteArray uint32ToData(uint32_t num);
QByteArray uint64ToData(uint64_t num);
#endif // SERIALIZE_H

View File

@ -31,6 +31,9 @@
#include "src/video/camera.h"
#include "form/chatform.h"
#include "maskablepixmapwidget.h"
#include "src/historykeeper.h"
#include "form/inputpassworddialog.h"
#include "src/autoupdate.h"
#include <QMessageBox>
#include <QDebug>
#include <QFile>
@ -45,9 +48,7 @@
#include <QTimer>
#include <QStyleFactory>
#include <QTranslator>
#include "src/historykeeper.h"
#include <tox/tox.h>
#include "form/inputpassworddialog.h"
Widget *Widget::instance{nullptr};
@ -195,6 +196,16 @@ void Widget::init()
addFriendForm = new AddFriendForm;
settingsWidget = new SettingsWidget();
// Check for updates
{
QString newVersion = AutoUpdater::getUpdateVersion();
if (!newVersion.isEmpty() && newVersion != GIT_VERSION)
{
qWarning() << "New update:"<<newVersion;
AutoUpdater::genUpdateDiff();
}
}
connect(core, &Core::connected, this, &Widget::onConnected);
connect(core, &Core::disconnected, this, &Widget::onDisconnected);
connect(core, &Core::failedToStart, this, &Widget::onFailedToStartCore);