mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Fix raciness of encryption API
And make it saner by not having one global password that has to be set before encrypting/decrypting, which is as racy and poorly designed as it gets Fixes #1917 's immediate symtoms, which some potential for other regressions due to the mess that is encrypted persistence currently
This commit is contained in:
parent
636faac4db
commit
645b1e5566
|
@ -67,7 +67,6 @@ Core::Core(QThread *CoreThread, Profile& profile) :
|
||||||
Audio::getInstance();
|
Audio::getInstance();
|
||||||
|
|
||||||
videobuf = nullptr;
|
videobuf = nullptr;
|
||||||
encryptionKey = nullptr;
|
|
||||||
|
|
||||||
toxTimer = new QTimer(this);
|
toxTimer = new QTimer(this);
|
||||||
toxTimer->setSingleShot(true);
|
toxTimer->setSingleShot(true);
|
||||||
|
|
|
@ -81,10 +81,15 @@ public:
|
||||||
ToxId getSelfId() const; ///< Returns our Tox ID
|
ToxId getSelfId() const; ///< Returns our Tox ID
|
||||||
QPair<QByteArray, QByteArray> getKeypair() const; ///< Returns our public and private keys
|
QPair<QByteArray, QByteArray> getKeypair() const; ///< Returns our public and private keys
|
||||||
|
|
||||||
|
static std::unique_ptr<TOX_PASS_KEY> createPasskey(const QString &password, uint8_t* salt = nullptr);
|
||||||
|
static QByteArray encryptData(const QByteArray& data, const TOX_PASS_KEY& encryptionKey);
|
||||||
|
static QByteArray encryptData(const QByteArray& data); ///< Uses the default profile's key
|
||||||
|
static QByteArray decryptData(const QByteArray& data, const TOX_PASS_KEY &encryptionKey);
|
||||||
|
static QByteArray decryptData(const QByteArray& data); ///< Uses the default profile's key
|
||||||
|
|
||||||
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
||||||
|
|
||||||
static bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
|
static bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
|
||||||
bool isPasswordSet();
|
|
||||||
bool isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first
|
bool isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first
|
||||||
|
|
||||||
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
||||||
|
@ -148,11 +153,6 @@ public slots:
|
||||||
static bool isGroupCallMicEnabled(int groupId);
|
static bool isGroupCallMicEnabled(int groupId);
|
||||||
static bool isGroupCallVolEnabled(int groupId);
|
static bool isGroupCallVolEnabled(int groupId);
|
||||||
|
|
||||||
void setPassword(const QString &password, uint8_t* salt = nullptr);
|
|
||||||
void clearPassword();
|
|
||||||
QByteArray encryptData(const QByteArray& data);
|
|
||||||
QByteArray decryptData(const QByteArray& data);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void connected();
|
void connected();
|
||||||
void disconnected();
|
void disconnected();
|
||||||
|
@ -303,8 +303,6 @@ private:
|
||||||
QMutex messageSendMutex;
|
QMutex messageSendMutex;
|
||||||
bool ready;
|
bool ready;
|
||||||
|
|
||||||
static TOX_PASS_KEY* encryptionKey; // use the pw's hash as the "pw"
|
|
||||||
|
|
||||||
static const int videobufsize;
|
static const int videobufsize;
|
||||||
static uint8_t* videobuf;
|
static uint8_t* videobuf;
|
||||||
|
|
||||||
|
|
|
@ -38,40 +38,29 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
TOX_PASS_KEY* Core::encryptionKey = nullptr;
|
std::unique_ptr<TOX_PASS_KEY> Core::createPasskey(const QString& password, uint8_t* salt)
|
||||||
|
|
||||||
void Core::setPassword(const QString& password, uint8_t* salt)
|
|
||||||
{
|
{
|
||||||
clearPassword();
|
std::unique_ptr<TOX_PASS_KEY> encryptionKey(new TOX_PASS_KEY);
|
||||||
if (password.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
encryptionKey = new TOX_PASS_KEY;
|
|
||||||
|
|
||||||
CString str(password);
|
CString str(password);
|
||||||
if (salt)
|
if (salt)
|
||||||
tox_derive_key_with_salt(str.data(), str.size(), salt, encryptionKey, nullptr);
|
tox_derive_key_with_salt(str.data(), str.size(), salt, encryptionKey.get(), nullptr);
|
||||||
else
|
else
|
||||||
tox_derive_key_from_pass(str.data(), str.size(), encryptionKey, nullptr);
|
tox_derive_key_from_pass(str.data(), str.size(), encryptionKey.get(), nullptr);
|
||||||
|
|
||||||
|
return encryptionKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::clearPassword()
|
QByteArray Core::encryptData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
delete encryptionKey;
|
return encryptData(data, Nexus::getProfile()->getPasskey());
|
||||||
encryptionKey = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Core::encryptData(const QByteArray& data)
|
QByteArray Core::encryptData(const QByteArray& data, const TOX_PASS_KEY& encryptionKey)
|
||||||
{
|
{
|
||||||
if (!encryptionKey)
|
|
||||||
{
|
|
||||||
qWarning() << "No encryption key set";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t encrypted[data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
|
uint8_t encrypted[data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
|
||||||
if (!tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
if (!tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
||||||
encryptionKey, encrypted, nullptr))
|
&encryptionKey, encrypted, nullptr))
|
||||||
{
|
{
|
||||||
qWarning() << "Encryption failed";
|
qWarning() << "Encryption failed";
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
@ -79,18 +68,17 @@ QByteArray Core::encryptData(const QByteArray& data)
|
||||||
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
|
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Core::decryptData(const QByteArray& data)
|
QByteArray Core::decryptData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
if (!encryptionKey)
|
return decryptData(data, Nexus::getProfile()->getPasskey());
|
||||||
{
|
}
|
||||||
qWarning() << "No encryption key set";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
QByteArray Core::decryptData(const QByteArray& data, const TOX_PASS_KEY& encryptionKey)
|
||||||
|
{
|
||||||
int sz = data.size() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
int sz = data.size() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||||
uint8_t decrypted[sz];
|
uint8_t decrypted[sz];
|
||||||
if (!tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
if (!tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
||||||
encryptionKey, decrypted, nullptr))
|
&encryptionKey, decrypted, nullptr))
|
||||||
{
|
{
|
||||||
qWarning() << "Decryption failed";
|
qWarning() << "Decryption failed";
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
@ -98,11 +86,6 @@ QByteArray Core::decryptData(const QByteArray& data)
|
||||||
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
|
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::isPasswordSet()
|
|
||||||
{
|
|
||||||
return static_cast<bool>(encryptionKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray Core::getSaltFromFile(QString filename)
|
QByteArray Core::getSaltFromFile(QString filename)
|
||||||
{
|
{
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
|
@ -139,7 +122,7 @@ void Core::checkEncryptedHistory()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPassword(Nexus::getProfile()->getPassword(), reinterpret_cast<uint8_t*>(salt.data()));
|
auto passkey = createPasskey(Nexus::getProfile()->getPassword(), reinterpret_cast<uint8_t*>(salt.data()));
|
||||||
|
|
||||||
QString a(tr("Please enter the password for the chat history for the profile \"%1\".", "used in load() when no hist pw set").arg(Nexus::getProfile()->getName()));
|
QString a(tr("Please enter the password for the chat history for the profile \"%1\".", "used in load() when no hist pw set").arg(Nexus::getProfile()->getName()));
|
||||||
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
|
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
|
||||||
|
@ -147,7 +130,7 @@ void Core::checkEncryptedHistory()
|
||||||
QString dialogtxt;
|
QString dialogtxt;
|
||||||
|
|
||||||
|
|
||||||
if (!exists || HistoryKeeper::checkPassword())
|
if (!exists || HistoryKeeper::checkPassword(*passkey))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dialogtxt = tr("The chat history password failed. Please try another?", "used only when pw set before load() doesn't work");
|
dialogtxt = tr("The chat history password failed. Please try another?", "used only when pw set before load() doesn't work");
|
||||||
|
@ -160,17 +143,16 @@ void Core::checkEncryptedHistory()
|
||||||
|
|
||||||
if (pw.isEmpty())
|
if (pw.isEmpty())
|
||||||
{
|
{
|
||||||
clearPassword();
|
|
||||||
Settings::getInstance().setEnableLogging(false);
|
Settings::getInstance().setEnableLogging(false);
|
||||||
HistoryKeeper::resetInstance();
|
HistoryKeeper::resetInstance();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setPassword(pw, reinterpret_cast<uint8_t*>(salt.data()));
|
passkey = createPasskey(pw, reinterpret_cast<uint8_t*>(salt.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
error = exists && !HistoryKeeper::checkPassword();
|
error = exists && !HistoryKeeper::checkPassword(*passkey);
|
||||||
dialogtxt = a + "\n" + c + "\n" + b;
|
dialogtxt = a + "\n" + c + "\n" + b;
|
||||||
} while (error);
|
} while (error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include "encrypteddb.h"
|
#include "encrypteddb.h"
|
||||||
#include "src/persistence/settings.h"
|
#include "src/persistence/settings.h"
|
||||||
#include "src/core/core.h"
|
#include "src/core/core.h"
|
||||||
|
#include "src/nexus.h"
|
||||||
|
#include "src/persistence/profile.h"
|
||||||
|
|
||||||
#include <tox/toxencryptsave.h>
|
#include <tox/toxencryptsave.h>
|
||||||
|
|
||||||
|
@ -30,6 +32,8 @@
|
||||||
qint64 EncryptedDb::encryptedChunkSize = 4096;
|
qint64 EncryptedDb::encryptedChunkSize = 4096;
|
||||||
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||||
|
|
||||||
|
TOX_PASS_KEY EncryptedDb::decryptionKey;
|
||||||
|
|
||||||
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
|
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
|
||||||
PlainDb(":memory:", initList), fileName(fname)
|
PlainDb(":memory:", initList), fileName(fname)
|
||||||
{
|
{
|
||||||
|
@ -90,7 +94,7 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf)
|
||||||
while (!dbFile.atEnd())
|
while (!dbFile.atEnd())
|
||||||
{
|
{
|
||||||
QByteArray encrChunk = dbFile.read(encryptedChunkSize);
|
QByteArray encrChunk = dbFile.read(encryptedChunkSize);
|
||||||
buf = Core::getInstance()->decryptData(encrChunk);
|
buf = Core::getInstance()->decryptData(encrChunk, decryptionKey);
|
||||||
if (buf.size() > 0)
|
if (buf.size() > 0)
|
||||||
{
|
{
|
||||||
fileContent += buf;
|
fileContent += buf;
|
||||||
|
@ -154,7 +158,7 @@ void EncryptedDb::appendToEncrypted(const QString &sql)
|
||||||
encrFile.flush();
|
encrFile.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncryptedDb::check(const QString &fname)
|
bool EncryptedDb::check(const TOX_PASS_KEY &passkey, const QString &fname)
|
||||||
{
|
{
|
||||||
QFile file(fname);
|
QFile file(fname);
|
||||||
file.open(QIODevice::ReadOnly);
|
file.open(QIODevice::ReadOnly);
|
||||||
|
@ -163,9 +167,11 @@ bool EncryptedDb::check(const QString &fname)
|
||||||
if (file.size() > 0)
|
if (file.size() > 0)
|
||||||
{
|
{
|
||||||
QByteArray encrChunk = file.read(encryptedChunkSize);
|
QByteArray encrChunk = file.read(encryptedChunkSize);
|
||||||
QByteArray buf = Core::getInstance()->decryptData(encrChunk);
|
QByteArray buf = Core::getInstance()->decryptData(encrChunk, passkey);
|
||||||
if (buf.size() == 0)
|
if (buf.size() == 0)
|
||||||
state = false;
|
state = false;
|
||||||
|
else
|
||||||
|
decryptionKey = passkey;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define ENCRYPTEDDB_H
|
#define ENCRYPTEDDB_H
|
||||||
|
|
||||||
#include "plaindb.h"
|
#include "plaindb.h"
|
||||||
|
#include <tox/toxencryptsave.h>
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
@ -32,7 +33,7 @@ public:
|
||||||
virtual ~EncryptedDb();
|
virtual ~EncryptedDb();
|
||||||
|
|
||||||
virtual QSqlQuery exec(const QString &query);
|
virtual QSqlQuery exec(const QString &query);
|
||||||
static bool check(const QString &fname);
|
static bool check(const TOX_PASS_KEY &passkey, const QString &fname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool pullFileContent(const QString& fname, QByteArray &buf);
|
bool pullFileContent(const QString& fname, QByteArray &buf);
|
||||||
|
@ -45,6 +46,8 @@ private:
|
||||||
static qint64 plainChunkSize;
|
static qint64 plainChunkSize;
|
||||||
static qint64 encryptedChunkSize;
|
static qint64 encryptedChunkSize;
|
||||||
|
|
||||||
|
static TOX_PASS_KEY decryptionKey; ///< When importing, the decryption key may not be the same as the profile key
|
||||||
|
|
||||||
qint64 chunkPosition;
|
qint64 chunkPosition;
|
||||||
QByteArray buffer;
|
QByteArray buffer;
|
||||||
};
|
};
|
||||||
|
|
|
@ -73,13 +73,13 @@ HistoryKeeper *HistoryKeeper::getInstance()
|
||||||
return historyInstance;
|
return historyInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryKeeper::checkPassword(int encrypted)
|
bool HistoryKeeper::checkPassword(const TOX_PASS_KEY &passkey, int encrypted)
|
||||||
{
|
{
|
||||||
if (!Settings::getInstance().getEnableLogging() && (encrypted == -1))
|
if (!Settings::getInstance().getEnableLogging() && (encrypted == -1))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if ((encrypted == 1) || (encrypted == -1 && Nexus::getProfile()->isEncrypted()))
|
if ((encrypted == 1) || (encrypted == -1 && Nexus::getProfile()->isEncrypted()))
|
||||||
return EncryptedDb::check(getHistoryPath(Nexus::getProfile()->getName(), encrypted));
|
return EncryptedDb::check(passkey, getHistoryPath(Nexus::getProfile()->getName(), encrypted));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <tox/toxencryptsave.h>
|
||||||
|
|
||||||
class GenericDdInterface;
|
class GenericDdInterface;
|
||||||
namespace Db { enum class syncType; }
|
namespace Db { enum class syncType; }
|
||||||
|
@ -51,7 +52,7 @@ public:
|
||||||
static void resetInstance();
|
static void resetInstance();
|
||||||
|
|
||||||
static QString getHistoryPath(QString currentProfile = QString(), int encrypted = -1); // -1 defaults to checking settings, 0 or 1 to specify
|
static QString getHistoryPath(QString currentProfile = QString(), int encrypted = -1); // -1 defaults to checking settings, 0 or 1 to specify
|
||||||
static bool checkPassword(int encrypted = -1);
|
static bool checkPassword(const TOX_PASS_KEY& passkey, int encrypted = -1);
|
||||||
static bool isFileExist();
|
static bool isFileExist();
|
||||||
static void renameHistory(QString from, QString to);
|
static void renameHistory(QString from, QString to);
|
||||||
static bool removeHistory(int encrypted = -1);
|
static bool removeHistory(int encrypted = -1);
|
||||||
|
|
|
@ -40,6 +40,8 @@ Profile::Profile(QString name, QString password, bool isNewProfile)
|
||||||
: name{name}, password{password},
|
: name{name}, password{password},
|
||||||
newProfile{isNewProfile}, isRemoved{false}
|
newProfile{isNewProfile}, isRemoved{false}
|
||||||
{
|
{
|
||||||
|
passkey = *core->createPasskey(password);
|
||||||
|
|
||||||
Settings& s = Settings::getInstance();
|
Settings& s = Settings::getInstance();
|
||||||
s.setCurrentProfile(name);
|
s.setCurrentProfile(name);
|
||||||
s.saveGlobal();
|
s.saveGlobal();
|
||||||
|
@ -66,6 +68,63 @@ Profile* Profile::loadProfile(QString name, QString password)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check password
|
||||||
|
{
|
||||||
|
QString path = Settings::getInstance().getSettingsDirPath() + name + ".tox";
|
||||||
|
QFile saveFile(path);
|
||||||
|
qDebug() << "Loading tox save "<<path;
|
||||||
|
|
||||||
|
if (!saveFile.exists())
|
||||||
|
{
|
||||||
|
qWarning() << "The tox save file "<<path<<" was not found";
|
||||||
|
ProfileLocker::unlock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!saveFile.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
qCritical() << "The tox save file " << path << " couldn't' be opened";
|
||||||
|
ProfileLocker::unlock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 fileSize = saveFile.size();
|
||||||
|
if (fileSize <= 0)
|
||||||
|
{
|
||||||
|
qWarning() << "The tox save file"<<path<<" is empty!";
|
||||||
|
ProfileLocker::unlock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data = saveFile.readAll();
|
||||||
|
if (tox_is_data_encrypted((uint8_t*)data.data()))
|
||||||
|
{
|
||||||
|
if (password.isEmpty())
|
||||||
|
{
|
||||||
|
qCritical() << "The tox save file is encrypted, but we don't have a password!";
|
||||||
|
ProfileLocker::unlock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t salt[TOX_PASS_SALT_LENGTH];
|
||||||
|
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||||
|
auto tmpkey = *Core::createPasskey(password, salt);
|
||||||
|
|
||||||
|
data = Core::decryptData(data, tmpkey);
|
||||||
|
if (data.isEmpty())
|
||||||
|
{
|
||||||
|
qCritical() << "Failed to decrypt the tox save file";
|
||||||
|
ProfileLocker::unlock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!password.isEmpty())
|
||||||
|
qWarning() << "We have a password, but the tox save file is not encrypted";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new Profile(name, password, false);
|
return new Profile(name, password, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,9 +264,9 @@ QByteArray Profile::loadToxSave()
|
||||||
|
|
||||||
uint8_t salt[TOX_PASS_SALT_LENGTH];
|
uint8_t salt[TOX_PASS_SALT_LENGTH];
|
||||||
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||||
core->setPassword(password, salt);
|
passkey = *core->createPasskey(password, salt);
|
||||||
|
|
||||||
data = core->decryptData(data);
|
data = core->decryptData(data, passkey);
|
||||||
if (data.isEmpty())
|
if (data.isEmpty())
|
||||||
qCritical() << "Failed to decrypt the tox save file";
|
qCritical() << "Failed to decrypt the tox save file";
|
||||||
}
|
}
|
||||||
|
@ -247,8 +306,8 @@ void Profile::saveToxSave(QByteArray data)
|
||||||
|
|
||||||
if (!password.isEmpty())
|
if (!password.isEmpty())
|
||||||
{
|
{
|
||||||
core->setPassword(password);
|
passkey = *core->createPasskey(password);
|
||||||
data = core->encryptData(data);
|
data = core->encryptData(data, passkey);
|
||||||
if (data.isEmpty())
|
if (data.isEmpty())
|
||||||
{
|
{
|
||||||
qCritical() << "Failed to encrypt, can't save!";
|
qCritical() << "Failed to encrypt, can't save!";
|
||||||
|
@ -350,6 +409,11 @@ QString Profile::getPassword()
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TOX_PASS_KEY& Profile::getPasskey()
|
||||||
|
{
|
||||||
|
return passkey;
|
||||||
|
}
|
||||||
|
|
||||||
void Profile::restartCore()
|
void Profile::restartCore()
|
||||||
{
|
{
|
||||||
GUI::setEnabled(false); // Core::reset re-enables it
|
GUI::setEnabled(false); // Core::reset re-enables it
|
||||||
|
@ -363,7 +427,7 @@ void Profile::setPassword(QString newPassword)
|
||||||
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile();
|
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile();
|
||||||
|
|
||||||
password = newPassword;
|
password = newPassword;
|
||||||
core->setPassword(password);
|
passkey = *core->createPasskey(password);
|
||||||
saveToxSave();
|
saveToxSave();
|
||||||
|
|
||||||
HistoryKeeper::getInstance()->importMessages(oldMessages);
|
HistoryKeeper::getInstance()->importMessages(oldMessages);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
#include <tox/toxencryptsave.h>
|
||||||
|
|
||||||
class Core;
|
class Core;
|
||||||
class QThread;
|
class QThread;
|
||||||
|
@ -51,6 +52,7 @@ public:
|
||||||
bool checkPassword(); ///< Checks whether the password is valid
|
bool checkPassword(); ///< Checks whether the password is valid
|
||||||
QString getPassword();
|
QString getPassword();
|
||||||
void setPassword(QString newPassword); ///< Changes the encryption password and re-saves everything with it
|
void setPassword(QString newPassword); ///< Changes the encryption password and re-saves everything with it
|
||||||
|
const TOX_PASS_KEY& getPasskey();
|
||||||
|
|
||||||
QByteArray loadToxSave(); ///< Loads the profile's .tox save from file, unencrypted
|
QByteArray loadToxSave(); ///< Loads the profile's .tox save from file, unencrypted
|
||||||
void saveToxSave(); ///< Saves the profile's .tox save, encrypted if needed. Invalid on deleted profiles.
|
void saveToxSave(); ///< Saves the profile's .tox save, encrypted if needed. Invalid on deleted profiles.
|
||||||
|
@ -85,6 +87,7 @@ private:
|
||||||
Core* core;
|
Core* core;
|
||||||
QThread* coreThread;
|
QThread* coreThread;
|
||||||
QString name, password;
|
QString name, password;
|
||||||
|
TOX_PASS_KEY passkey;
|
||||||
bool newProfile; ///< True if this is a newly created profile, with no .tox save file yet.
|
bool newProfile; ///< True if this is a newly created profile, with no .tox save file yet.
|
||||||
bool isRemoved; ///< True if the profile has been removed by remove()
|
bool isRemoved; ///< True if the profile has been removed by remove()
|
||||||
static QVector<QString> profiles;
|
static QVector<QString> profiles;
|
||||||
|
|
|
@ -287,8 +287,8 @@ void SettingsSerializer::save()
|
||||||
if (!password.isEmpty())
|
if (!password.isEmpty())
|
||||||
{
|
{
|
||||||
Core* core = Nexus::getCore();
|
Core* core = Nexus::getCore();
|
||||||
core->setPassword(password);
|
auto passkey = core->createPasskey(password);
|
||||||
data = core->encryptData(data);
|
data = core->encryptData(data, *passkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
f.write(data);
|
f.write(data);
|
||||||
|
@ -319,11 +319,14 @@ void SettingsSerializer::readSerialized()
|
||||||
|
|
||||||
uint8_t salt[TOX_PASS_SALT_LENGTH];
|
uint8_t salt[TOX_PASS_SALT_LENGTH];
|
||||||
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||||
core->setPassword(password, salt);
|
auto passkey = core->createPasskey(password, salt);
|
||||||
|
|
||||||
data = core->decryptData(data);
|
data = core->decryptData(data, *passkey);
|
||||||
if (data.isEmpty())
|
if (data.isEmpty())
|
||||||
|
{
|
||||||
qCritical() << "Failed to decrypt the settings file";
|
qCritical() << "Failed to decrypt the settings file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -179,16 +179,17 @@ void LoginScreen::onLogin()
|
||||||
Profile* profile = Profile::loadProfile(name, pass);
|
Profile* profile = Profile::loadProfile(name, pass);
|
||||||
if (!profile)
|
if (!profile)
|
||||||
{
|
{
|
||||||
// Unknown error
|
if (!ProfileLocker::isLockable(name))
|
||||||
QMessageBox::critical(this, tr("Couldn't load this profile"), tr("Couldn't load this profile."));
|
{
|
||||||
|
QMessageBox::critical(this, tr("Couldn't load this profile"), tr("Profile already in use. Close other clients."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!profile->checkPassword())
|
else
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Couldn't load this profile"), tr("Wrong password."));
|
QMessageBox::critical(this, tr("Couldn't load this profile"), tr("Wrong password."));
|
||||||
delete profile;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Nexus& nexus = Nexus::getInstance();
|
Nexus& nexus = Nexus::getInstance();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user