mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
feat: stop using plaintext passwords in the code
This commit is contained in:
parent
0f54e44262
commit
084f3b0626
|
@ -51,7 +51,6 @@ QVector<QString> Profile::profiles;
|
|||
|
||||
Profile::Profile(QString name, const QString& password, bool isNewProfile, const QByteArray& toxsave)
|
||||
: name{name}
|
||||
, password{password}
|
||||
, newProfile{isNewProfile}
|
||||
, isRemoved{false}
|
||||
{
|
||||
|
@ -62,7 +61,9 @@ Profile::Profile(QString name, const QString& password, bool isNewProfile, const
|
|||
coreThread = new QThread();
|
||||
coreThread->setObjectName("qTox Core");
|
||||
core = new Core(coreThread, *this);
|
||||
QObject::connect(core, &Core::idSet, this, &Profile::loadDatabase, Qt::QueuedConnection);
|
||||
QObject::connect(core, &Core::idSet, this,
|
||||
[this, password](const ToxId& id) { loadDatabase(id, password); },
|
||||
Qt::QueuedConnection);
|
||||
core->moveToThread(coreThread);
|
||||
QObject::connect(coreThread, &QThread::started, core, [=]() { core->start(toxsave); });
|
||||
}
|
||||
|
@ -88,7 +89,7 @@ Profile* Profile::loadProfile(QString name, const QString& password)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ToxEncrypt> tmpKey;
|
||||
std::unique_ptr<ToxEncrypt> tmpKey = nullptr;
|
||||
QByteArray data = QByteArray();
|
||||
Profile* p = nullptr;
|
||||
qint64 fileSize = 0;
|
||||
|
@ -140,6 +141,9 @@ Profile* Profile::loadProfile(QString name, const QString& password)
|
|||
saveFile.close();
|
||||
p = new Profile(name, password, false, data);
|
||||
p->passkey = std::move(tmpKey);
|
||||
if (tmpKey) {
|
||||
p->encrypted = true;
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
|
@ -188,6 +192,9 @@ Profile* Profile::createProfile(QString name, QString password)
|
|||
Settings::getInstance().createPersonal(name);
|
||||
Profile* p = new Profile(name, password, true, QByteArray());
|
||||
p->passkey = std::move(tmpKey);
|
||||
if (tmpKey) {
|
||||
p->encrypted = true;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
@ -308,7 +315,7 @@ void Profile::saveToxSave(QByteArray data)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!password.isEmpty()) {
|
||||
if (encrypted) {
|
||||
data = passkey->encrypt(data);
|
||||
if (data.isEmpty()) {
|
||||
qCritical() << "Failed to encrypt, can't save!";
|
||||
|
@ -339,7 +346,7 @@ void Profile::saveToxSave(QByteArray data)
|
|||
*/
|
||||
QString Profile::avatarPath(const QString& ownerId, bool forceUnencrypted)
|
||||
{
|
||||
if (password.isEmpty() || forceUnencrypted)
|
||||
if (!encrypted || forceUnencrypted)
|
||||
return Settings::getInstance().getSettingsDirPath() + "avatars/" + ownerId + ".png";
|
||||
|
||||
QByteArray idData = ownerId.toUtf8();
|
||||
|
@ -379,28 +386,16 @@ QPixmap Profile::loadAvatar(const QString& ownerId)
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Get a contact's avatar from cache
|
||||
* @brief Get a contact's avatar from cache.
|
||||
* @param ownerId Friend ID to load avatar.
|
||||
* @return Avatar as QByteArray.
|
||||
*/
|
||||
QByteArray Profile::loadAvatarData(const QString& ownerId)
|
||||
{
|
||||
return loadAvatarData(ownerId, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a contact's avatar from cache, with a specified profile password.
|
||||
* @param ownerId Friend ID to load avatar.
|
||||
* @param password Profile password to decrypt data.
|
||||
* @return Avatar as QByteArray.
|
||||
*/
|
||||
QByteArray Profile::loadAvatarData(const QString& ownerId, const QString& password)
|
||||
{
|
||||
QString path = avatarPath(ownerId);
|
||||
bool encrypted = !password.isEmpty();
|
||||
|
||||
// If the encrypted avatar isn't found, try loading the unencrypted one for the same ID
|
||||
if (!password.isEmpty() && !QFile::exists(path)) {
|
||||
if (encrypted && !QFile::exists(path)) {
|
||||
encrypted = false;
|
||||
path = avatarPath(ownerId, true);
|
||||
}
|
||||
|
@ -412,14 +407,13 @@ QByteArray Profile::loadAvatarData(const QString& ownerId, const QString& passwo
|
|||
|
||||
QByteArray pic = file.readAll();
|
||||
if (encrypted && !pic.isEmpty()) {
|
||||
// TODO: check if we can use passkey-decrypt(pic) here
|
||||
pic = ToxEncrypt::decryptPass(password, pic);
|
||||
pic = passkey->decrypt(pic);
|
||||
}
|
||||
|
||||
return pic;
|
||||
}
|
||||
|
||||
void Profile::loadDatabase(const ToxId& id)
|
||||
void Profile::loadDatabase(const ToxId& id, QString password)
|
||||
{
|
||||
if (isRemoved) {
|
||||
qDebug() << "Can't load database of removed profile";
|
||||
|
@ -452,7 +446,7 @@ void Profile::loadDatabase(const ToxId& id)
|
|||
*/
|
||||
void Profile::saveAvatar(QByteArray pic, const QString& ownerId)
|
||||
{
|
||||
if (!password.isEmpty() && !pic.isEmpty()) {
|
||||
if (encrypted && !pic.isEmpty()) {
|
||||
pic = passkey->encrypt(pic);
|
||||
}
|
||||
|
||||
|
@ -534,7 +528,7 @@ bool Profile::exists(QString name)
|
|||
*/
|
||||
bool Profile::isEncrypted() const
|
||||
{
|
||||
return !password.isEmpty();
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -640,14 +634,9 @@ bool Profile::rename(QString newName)
|
|||
return true;
|
||||
}
|
||||
|
||||
QString Profile::getPassword() const
|
||||
const ToxEncrypt* Profile::getPasskey() const
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
const ToxEncrypt& Profile::getPasskey() const
|
||||
{
|
||||
return *passkey;
|
||||
return passkey.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -665,37 +654,57 @@ void Profile::restartCore()
|
|||
|
||||
/**
|
||||
* @brief Changes the encryption password and re-saves everything with it
|
||||
* @param newPassword Password for encryption.
|
||||
* @param newPassword Password for encryption, if empty profile will be decrypted.
|
||||
* @param oldPassword Supply previous password if already encrypted or empty QString if not yet
|
||||
* encrypted.
|
||||
* @return Empty QString on success or error message on failure.
|
||||
*/
|
||||
void Profile::setPassword(const QString& newPassword)
|
||||
QString Profile::setPassword(const QString& newPassword)
|
||||
{
|
||||
QByteArray avatar = loadAvatarData(core->getSelfId().getPublicKey().toString());
|
||||
QString oldPassword = password;
|
||||
std::unique_ptr<ToxEncrypt> oldpasskey = std::move(passkey);
|
||||
password = newPassword;
|
||||
passkey = ToxEncrypt::makeToxEncrypt(password);
|
||||
if (!passkey) {
|
||||
qCritical() << "Failed to derive key from password, the profile won't use the new password";
|
||||
password = oldPassword;
|
||||
passkey = std::move(oldpasskey);
|
||||
return;
|
||||
if (newPassword.isEmpty()) {
|
||||
// remove password
|
||||
encrypted = false;
|
||||
} else {
|
||||
std::unique_ptr<ToxEncrypt> newpasskey = ToxEncrypt::makeToxEncrypt(newPassword);
|
||||
if (!newpasskey) {
|
||||
qCritical()
|
||||
<< "Failed to derive key from password, the profile won't use the new password";
|
||||
return tr(
|
||||
"Failed to derive key from password, the profile won't use the new password.");
|
||||
}
|
||||
// apply change
|
||||
passkey = std::move(newpasskey);
|
||||
encrypted = true;
|
||||
}
|
||||
|
||||
// apply new encryption
|
||||
saveToxSave();
|
||||
|
||||
bool dbSuccess = false;
|
||||
|
||||
// TODO: ensure the database and the tox save file use the same password
|
||||
if (database) {
|
||||
database->setPassword(newPassword);
|
||||
dbSuccess = database->setPassword(newPassword);
|
||||
}
|
||||
|
||||
QString error{};
|
||||
if (!dbSuccess) {
|
||||
error = tr("Couldn't change password on the database, it might be corrupted or use the old "
|
||||
"password.");
|
||||
}
|
||||
|
||||
Nexus::getDesktopGUI()->reloadHistory();
|
||||
|
||||
QByteArray avatar = loadAvatarData(core->getSelfId().getPublicKey().toString());
|
||||
saveAvatar(avatar, core->getSelfId().getPublicKey().toString());
|
||||
|
||||
QVector<uint32_t> friendList = core->getFriendList();
|
||||
QVectorIterator<uint32_t> i(friendList);
|
||||
while (i.hasNext()) {
|
||||
QString friendPublicKey = core->getFriendPublicKey(i.next()).toString();
|
||||
saveAvatar(loadAvatarData(friendPublicKey, oldPassword), friendPublicKey);
|
||||
saveAvatar(loadAvatarData(friendPublicKey), friendPublicKey);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,9 +52,8 @@ public:
|
|||
void restartCore();
|
||||
bool isNewProfile();
|
||||
bool isEncrypted() const;
|
||||
QString getPassword() const;
|
||||
void setPassword(const QString& newPassword);
|
||||
const ToxEncrypt& getPasskey() const;
|
||||
QString setPassword(const QString& newPassword);
|
||||
const ToxEncrypt* getPasskey() const;
|
||||
|
||||
void saveToxSave();
|
||||
void saveToxSave(QByteArray data);
|
||||
|
@ -62,7 +61,6 @@ public:
|
|||
QPixmap loadAvatar();
|
||||
QPixmap loadAvatar(const QString& ownerId);
|
||||
QByteArray loadAvatarData(const QString& ownerId);
|
||||
QByteArray loadAvatarData(const QString& ownerId, const QString& password);
|
||||
void saveAvatar(QByteArray pic, const QString& ownerId);
|
||||
QByteArray getAvatarHash(const QString& ownerId);
|
||||
void removeAvatar(const QString& ownerId);
|
||||
|
@ -83,7 +81,7 @@ public:
|
|||
static QString getDbPath(const QString& profileName);
|
||||
|
||||
private slots:
|
||||
void loadDatabase(const ToxId& id);
|
||||
void loadDatabase(const ToxId& id, QString password);
|
||||
|
||||
private:
|
||||
Profile(QString name, const QString& password, bool newProfile, const QByteArray& toxsave);
|
||||
|
@ -93,12 +91,13 @@ private:
|
|||
private:
|
||||
Core* core;
|
||||
QThread* coreThread;
|
||||
QString name, password;
|
||||
std::unique_ptr<ToxEncrypt> passkey;
|
||||
QString name;
|
||||
std::unique_ptr<ToxEncrypt> passkey = nullptr;
|
||||
std::shared_ptr<RawDatabase> database;
|
||||
std::unique_ptr<History> history;
|
||||
bool newProfile;
|
||||
bool isRemoved;
|
||||
bool encrypted = false;
|
||||
static QVector<QString> profiles;
|
||||
};
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ void Settings::loadPersonal(Profile* profile)
|
|||
|
||||
qDebug() << "Loading personal settings from" << filePath;
|
||||
|
||||
SettingsSerializer ps(filePath, profile->getPassword());
|
||||
SettingsSerializer ps(filePath, profile->getPasskey());
|
||||
ps.load();
|
||||
friendLst.clear();
|
||||
|
||||
|
@ -618,15 +618,14 @@ void Settings::savePersonal(Profile* profile)
|
|||
qDebug() << "Could not save personal settings because there is no active profile";
|
||||
return;
|
||||
}
|
||||
savePersonal(profile->getName(), profile->getPassword());
|
||||
}
|
||||
|
||||
void Settings::savePersonal(QString profileName, const QString& password)
|
||||
{
|
||||
if (QThread::currentThread() != settingsThread)
|
||||
return (void)QMetaObject::invokeMethod(&getInstance(), "savePersonal",
|
||||
Q_ARG(QString, profileName), Q_ARG(QString, password));
|
||||
Q_ARG(Profile*, profile));
|
||||
savePersonal(profile->getName(), profile->getPasskey());
|
||||
}
|
||||
|
||||
void Settings::savePersonal(QString profileName, const ToxEncrypt* passkey)
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
if (!loaded)
|
||||
return;
|
||||
|
@ -635,7 +634,7 @@ void Settings::savePersonal(QString profileName, const QString& password)
|
|||
|
||||
qDebug() << "Saving personal settings at " << path;
|
||||
|
||||
SettingsSerializer ps(path, password);
|
||||
SettingsSerializer ps(path, passkey);
|
||||
ps.beginGroup("Friends");
|
||||
{
|
||||
ps.beginWriteArray("Friend", friendLst.size());
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define SETTINGS_HPP
|
||||
|
||||
#include "src/core/corestructs.h"
|
||||
#include "src/core/toxencrypt.h"
|
||||
#include <QDate>
|
||||
#include <QFlags>
|
||||
#include <QFont>
|
||||
|
@ -140,7 +141,6 @@ public:
|
|||
void createPersonal(QString basename);
|
||||
|
||||
void savePersonal();
|
||||
void savePersonal(Profile* profile);
|
||||
|
||||
void loadGlobal();
|
||||
void loadPersonal();
|
||||
|
@ -523,9 +523,10 @@ private:
|
|||
~Settings();
|
||||
Settings(Settings& settings) = delete;
|
||||
Settings& operator=(const Settings&) = delete;
|
||||
void savePersonal(QString profileName, const ToxEncrypt* passkey);
|
||||
|
||||
private slots:
|
||||
void savePersonal(QString profileName, const QString& password);
|
||||
public slots:
|
||||
void savePersonal(Profile* profile);
|
||||
|
||||
private:
|
||||
bool loaded;
|
||||
|
|
|
@ -99,9 +99,9 @@ QDataStream& readStream(QDataStream& dataStream, QByteArray& data)
|
|||
return dataStream;
|
||||
}
|
||||
|
||||
SettingsSerializer::SettingsSerializer(QString filePath, const QString& password)
|
||||
SettingsSerializer::SettingsSerializer(QString filePath, const ToxEncrypt* passKey)
|
||||
: path{filePath}
|
||||
, password{password}
|
||||
, passKey{passKey}
|
||||
, group{-1}
|
||||
, array{-1}
|
||||
, arrayIndex{-1}
|
||||
|
@ -300,9 +300,8 @@ void SettingsSerializer::save()
|
|||
}
|
||||
|
||||
// Encrypt
|
||||
if (!password.isEmpty()) {
|
||||
// TODO: use passkey
|
||||
data = ToxEncrypt::encryptPass(password, data);
|
||||
if (passKey) {
|
||||
data = passKey->encrypt(data);
|
||||
}
|
||||
|
||||
f.write(data);
|
||||
|
@ -327,19 +326,19 @@ void SettingsSerializer::readSerialized()
|
|||
f.close();
|
||||
|
||||
// Decrypt
|
||||
if (tox_is_data_encrypted(reinterpret_cast<uint8_t*>(data.data()))) {
|
||||
if (password.isEmpty()) {
|
||||
qCritical() << "The settings file is encrypted, but we don't have a password!";
|
||||
if (ToxEncrypt::isEncrypted(data)) {
|
||||
if (!passKey) {
|
||||
qCritical() << "The settings file is encrypted, but we don't have a passkey!";
|
||||
return;
|
||||
}
|
||||
|
||||
data = ToxEncrypt::decryptPass(password, data);
|
||||
data = passKey->decrypt(data);
|
||||
if (data.isEmpty()) {
|
||||
qCritical() << "Failed to decrypt the settings file";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!password.isEmpty())
|
||||
if (passKey)
|
||||
qWarning() << "We have a password, but the settings file is not encrypted";
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#ifndef SETTINGSSERIALIZER_H
|
||||
#define SETTINGSSERIALIZER_H
|
||||
|
||||
#include "src/core/toxencrypt.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
|
@ -28,7 +30,7 @@
|
|||
class SettingsSerializer
|
||||
{
|
||||
public:
|
||||
SettingsSerializer(QString filePath, const QString& password = QString());
|
||||
SettingsSerializer(QString filePath, const ToxEncrypt* passKey = nullptr);
|
||||
|
||||
static bool isSerializedFormat(QString filePath);
|
||||
|
||||
|
@ -102,7 +104,7 @@ private:
|
|||
|
||||
private:
|
||||
QString path;
|
||||
QString password;
|
||||
const ToxEncrypt* passKey;
|
||||
int group, array, arrayIndex;
|
||||
QVector<QString> groups;
|
||||
QVector<Array> arrays;
|
||||
|
|
|
@ -433,11 +433,15 @@ void ProfileForm::onDeletePassClicked()
|
|||
"deletion confirmation text")))
|
||||
return;
|
||||
|
||||
Nexus::getProfile()->setPassword(QString());
|
||||
QString errorMsg = pro->setPassword(QString());
|
||||
if (!errorMsg.isEmpty()) {
|
||||
GUI::showInfo(tr("Couldn't change password"), errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileForm::onChangePassClicked()
|
||||
{
|
||||
Profile* p = Nexus::getProfile();
|
||||
SetPasswordDialog* dialog =
|
||||
new SetPasswordDialog(tr("Please enter a new password."), QString(), 0);
|
||||
int r = dialog->exec();
|
||||
|
@ -445,7 +449,10 @@ void ProfileForm::onChangePassClicked()
|
|||
return;
|
||||
|
||||
QString newPass = dialog->getPassword();
|
||||
Nexus::getProfile()->setPassword(newPass);
|
||||
QString errorMsg = p->setPassword(newPass);
|
||||
if (!errorMsg.isEmpty()) {
|
||||
GUI::showInfo(tr("Couldn't change password"), errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileForm::retranslateUi()
|
||||
|
|
Loading…
Reference in New Issue
Block a user