1
0
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:
sudden6 2017-03-25 14:46:43 +01:00
parent 0f54e44262
commit 084f3b0626
No known key found for this signature in database
GPG Key ID: 279509B499E032B9
7 changed files with 93 additions and 77 deletions

View File

@ -51,7 +51,6 @@ QVector<QString> Profile::profiles;
Profile::Profile(QString name, const QString& password, bool isNewProfile, const QByteArray& toxsave) Profile::Profile(QString name, const QString& password, bool isNewProfile, const QByteArray& toxsave)
: name{name} : name{name}
, password{password}
, newProfile{isNewProfile} , newProfile{isNewProfile}
, isRemoved{false} , isRemoved{false}
{ {
@ -62,7 +61,9 @@ Profile::Profile(QString name, const QString& password, bool isNewProfile, const
coreThread = new QThread(); coreThread = new QThread();
coreThread->setObjectName("qTox Core"); coreThread->setObjectName("qTox Core");
core = new Core(coreThread, *this); 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); core->moveToThread(coreThread);
QObject::connect(coreThread, &QThread::started, core, [=]() { core->start(toxsave); }); QObject::connect(coreThread, &QThread::started, core, [=]() { core->start(toxsave); });
} }
@ -88,7 +89,7 @@ Profile* Profile::loadProfile(QString name, const QString& password)
return nullptr; return nullptr;
} }
std::unique_ptr<ToxEncrypt> tmpKey; std::unique_ptr<ToxEncrypt> tmpKey = nullptr;
QByteArray data = QByteArray(); QByteArray data = QByteArray();
Profile* p = nullptr; Profile* p = nullptr;
qint64 fileSize = 0; qint64 fileSize = 0;
@ -140,6 +141,9 @@ Profile* Profile::loadProfile(QString name, const QString& password)
saveFile.close(); saveFile.close();
p = new Profile(name, password, false, data); p = new Profile(name, password, false, data);
p->passkey = std::move(tmpKey); p->passkey = std::move(tmpKey);
if (tmpKey) {
p->encrypted = true;
}
return p; return p;
@ -188,6 +192,9 @@ Profile* Profile::createProfile(QString name, QString password)
Settings::getInstance().createPersonal(name); Settings::getInstance().createPersonal(name);
Profile* p = new Profile(name, password, true, QByteArray()); Profile* p = new Profile(name, password, true, QByteArray());
p->passkey = std::move(tmpKey); p->passkey = std::move(tmpKey);
if (tmpKey) {
p->encrypted = true;
}
return p; return p;
} }
@ -308,7 +315,7 @@ void Profile::saveToxSave(QByteArray data)
return; return;
} }
if (!password.isEmpty()) { if (encrypted) {
data = passkey->encrypt(data); data = passkey->encrypt(data);
if (data.isEmpty()) { if (data.isEmpty()) {
qCritical() << "Failed to encrypt, can't save!"; qCritical() << "Failed to encrypt, can't save!";
@ -339,7 +346,7 @@ void Profile::saveToxSave(QByteArray data)
*/ */
QString Profile::avatarPath(const QString& ownerId, bool forceUnencrypted) QString Profile::avatarPath(const QString& ownerId, bool forceUnencrypted)
{ {
if (password.isEmpty() || forceUnencrypted) if (!encrypted || forceUnencrypted)
return Settings::getInstance().getSettingsDirPath() + "avatars/" + ownerId + ".png"; return Settings::getInstance().getSettingsDirPath() + "avatars/" + ownerId + ".png";
QByteArray idData = ownerId.toUtf8(); 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. * @param ownerId Friend ID to load avatar.
* @return Avatar as QByteArray. * @return Avatar as QByteArray.
*/ */
QByteArray Profile::loadAvatarData(const QString& ownerId) 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); 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 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; encrypted = false;
path = avatarPath(ownerId, true); path = avatarPath(ownerId, true);
} }
@ -412,14 +407,13 @@ QByteArray Profile::loadAvatarData(const QString& ownerId, const QString& passwo
QByteArray pic = file.readAll(); QByteArray pic = file.readAll();
if (encrypted && !pic.isEmpty()) { if (encrypted && !pic.isEmpty()) {
// TODO: check if we can use passkey-decrypt(pic) here pic = passkey->decrypt(pic);
pic = ToxEncrypt::decryptPass(password, pic);
} }
return pic; return pic;
} }
void Profile::loadDatabase(const ToxId& id) void Profile::loadDatabase(const ToxId& id, QString password)
{ {
if (isRemoved) { if (isRemoved) {
qDebug() << "Can't load database of removed profile"; 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) void Profile::saveAvatar(QByteArray pic, const QString& ownerId)
{ {
if (!password.isEmpty() && !pic.isEmpty()) { if (encrypted && !pic.isEmpty()) {
pic = passkey->encrypt(pic); pic = passkey->encrypt(pic);
} }
@ -534,7 +528,7 @@ bool Profile::exists(QString name)
*/ */
bool Profile::isEncrypted() const bool Profile::isEncrypted() const
{ {
return !password.isEmpty(); return encrypted;
} }
/** /**
@ -640,14 +634,9 @@ bool Profile::rename(QString newName)
return true; return true;
} }
QString Profile::getPassword() const const ToxEncrypt* Profile::getPasskey() const
{ {
return password; return passkey.get();
}
const ToxEncrypt& Profile::getPasskey() const
{
return *passkey;
} }
/** /**
@ -665,37 +654,57 @@ void Profile::restartCore()
/** /**
* @brief Changes the encryption password and re-saves everything with it * @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()); if (newPassword.isEmpty()) {
QString oldPassword = password; // remove password
std::unique_ptr<ToxEncrypt> oldpasskey = std::move(passkey); encrypted = false;
password = newPassword; } else {
passkey = ToxEncrypt::makeToxEncrypt(password); std::unique_ptr<ToxEncrypt> newpasskey = ToxEncrypt::makeToxEncrypt(newPassword);
if (!passkey) { if (!newpasskey) {
qCritical() << "Failed to derive key from password, the profile won't use the new password"; qCritical()
password = oldPassword; << "Failed to derive key from password, the profile won't use the new password";
passkey = std::move(oldpasskey); return tr(
return; "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(); saveToxSave();
bool dbSuccess = false;
// TODO: ensure the database and the tox save file use the same password // TODO: ensure the database and the tox save file use the same password
if (database) { 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(); Nexus::getDesktopGUI()->reloadHistory();
QByteArray avatar = loadAvatarData(core->getSelfId().getPublicKey().toString());
saveAvatar(avatar, core->getSelfId().getPublicKey().toString()); saveAvatar(avatar, core->getSelfId().getPublicKey().toString());
QVector<uint32_t> friendList = core->getFriendList(); QVector<uint32_t> friendList = core->getFriendList();
QVectorIterator<uint32_t> i(friendList); QVectorIterator<uint32_t> i(friendList);
while (i.hasNext()) { while (i.hasNext()) {
QString friendPublicKey = core->getFriendPublicKey(i.next()).toString(); QString friendPublicKey = core->getFriendPublicKey(i.next()).toString();
saveAvatar(loadAvatarData(friendPublicKey, oldPassword), friendPublicKey); saveAvatar(loadAvatarData(friendPublicKey), friendPublicKey);
} }
return error;
} }
/** /**

View File

@ -52,9 +52,8 @@ public:
void restartCore(); void restartCore();
bool isNewProfile(); bool isNewProfile();
bool isEncrypted() const; bool isEncrypted() const;
QString getPassword() const; QString setPassword(const QString& newPassword);
void setPassword(const QString& newPassword); const ToxEncrypt* getPasskey() const;
const ToxEncrypt& getPasskey() const;
void saveToxSave(); void saveToxSave();
void saveToxSave(QByteArray data); void saveToxSave(QByteArray data);
@ -62,7 +61,6 @@ public:
QPixmap loadAvatar(); QPixmap loadAvatar();
QPixmap loadAvatar(const QString& ownerId); QPixmap loadAvatar(const QString& ownerId);
QByteArray loadAvatarData(const QString& ownerId); QByteArray loadAvatarData(const QString& ownerId);
QByteArray loadAvatarData(const QString& ownerId, const QString& password);
void saveAvatar(QByteArray pic, const QString& ownerId); void saveAvatar(QByteArray pic, const QString& ownerId);
QByteArray getAvatarHash(const QString& ownerId); QByteArray getAvatarHash(const QString& ownerId);
void removeAvatar(const QString& ownerId); void removeAvatar(const QString& ownerId);
@ -83,7 +81,7 @@ public:
static QString getDbPath(const QString& profileName); static QString getDbPath(const QString& profileName);
private slots: private slots:
void loadDatabase(const ToxId& id); void loadDatabase(const ToxId& id, QString password);
private: private:
Profile(QString name, const QString& password, bool newProfile, const QByteArray& toxsave); Profile(QString name, const QString& password, bool newProfile, const QByteArray& toxsave);
@ -93,12 +91,13 @@ private:
private: private:
Core* core; Core* core;
QThread* coreThread; QThread* coreThread;
QString name, password; QString name;
std::unique_ptr<ToxEncrypt> passkey; std::unique_ptr<ToxEncrypt> passkey = nullptr;
std::shared_ptr<RawDatabase> database; std::shared_ptr<RawDatabase> database;
std::unique_ptr<History> history; std::unique_ptr<History> history;
bool newProfile; bool newProfile;
bool isRemoved; bool isRemoved;
bool encrypted = false;
static QVector<QString> profiles; static QVector<QString> profiles;
}; };

View File

@ -341,7 +341,7 @@ void Settings::loadPersonal(Profile* profile)
qDebug() << "Loading personal settings from" << filePath; qDebug() << "Loading personal settings from" << filePath;
SettingsSerializer ps(filePath, profile->getPassword()); SettingsSerializer ps(filePath, profile->getPasskey());
ps.load(); ps.load();
friendLst.clear(); friendLst.clear();
@ -618,15 +618,14 @@ void Settings::savePersonal(Profile* profile)
qDebug() << "Could not save personal settings because there is no active profile"; qDebug() << "Could not save personal settings because there is no active profile";
return; return;
} }
savePersonal(profile->getName(), profile->getPassword());
}
void Settings::savePersonal(QString profileName, const QString& password)
{
if (QThread::currentThread() != settingsThread) if (QThread::currentThread() != settingsThread)
return (void)QMetaObject::invokeMethod(&getInstance(), "savePersonal", 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}; QMutexLocker locker{&bigLock};
if (!loaded) if (!loaded)
return; return;
@ -635,7 +634,7 @@ void Settings::savePersonal(QString profileName, const QString& password)
qDebug() << "Saving personal settings at " << path; qDebug() << "Saving personal settings at " << path;
SettingsSerializer ps(path, password); SettingsSerializer ps(path, passkey);
ps.beginGroup("Friends"); ps.beginGroup("Friends");
{ {
ps.beginWriteArray("Friend", friendLst.size()); ps.beginWriteArray("Friend", friendLst.size());

View File

@ -22,6 +22,7 @@
#define SETTINGS_HPP #define SETTINGS_HPP
#include "src/core/corestructs.h" #include "src/core/corestructs.h"
#include "src/core/toxencrypt.h"
#include <QDate> #include <QDate>
#include <QFlags> #include <QFlags>
#include <QFont> #include <QFont>
@ -140,7 +141,6 @@ public:
void createPersonal(QString basename); void createPersonal(QString basename);
void savePersonal(); void savePersonal();
void savePersonal(Profile* profile);
void loadGlobal(); void loadGlobal();
void loadPersonal(); void loadPersonal();
@ -523,9 +523,10 @@ private:
~Settings(); ~Settings();
Settings(Settings& settings) = delete; Settings(Settings& settings) = delete;
Settings& operator=(const Settings&) = delete; Settings& operator=(const Settings&) = delete;
void savePersonal(QString profileName, const ToxEncrypt* passkey);
private slots: public slots:
void savePersonal(QString profileName, const QString& password); void savePersonal(Profile* profile);
private: private:
bool loaded; bool loaded;

View File

@ -99,9 +99,9 @@ QDataStream& readStream(QDataStream& dataStream, QByteArray& data)
return dataStream; return dataStream;
} }
SettingsSerializer::SettingsSerializer(QString filePath, const QString& password) SettingsSerializer::SettingsSerializer(QString filePath, const ToxEncrypt* passKey)
: path{filePath} : path{filePath}
, password{password} , passKey{passKey}
, group{-1} , group{-1}
, array{-1} , array{-1}
, arrayIndex{-1} , arrayIndex{-1}
@ -300,9 +300,8 @@ void SettingsSerializer::save()
} }
// Encrypt // Encrypt
if (!password.isEmpty()) { if (passKey) {
// TODO: use passkey data = passKey->encrypt(data);
data = ToxEncrypt::encryptPass(password, data);
} }
f.write(data); f.write(data);
@ -327,19 +326,19 @@ void SettingsSerializer::readSerialized()
f.close(); f.close();
// Decrypt // Decrypt
if (tox_is_data_encrypted(reinterpret_cast<uint8_t*>(data.data()))) { if (ToxEncrypt::isEncrypted(data)) {
if (password.isEmpty()) { if (!passKey) {
qCritical() << "The settings file is encrypted, but we don't have a password!"; qCritical() << "The settings file is encrypted, but we don't have a passkey!";
return; return;
} }
data = ToxEncrypt::decryptPass(password, data); data = passKey->decrypt(data);
if (data.isEmpty()) { if (data.isEmpty()) {
qCritical() << "Failed to decrypt the settings file"; qCritical() << "Failed to decrypt the settings file";
return; return;
} }
} else { } else {
if (!password.isEmpty()) if (passKey)
qWarning() << "We have a password, but the settings file is not encrypted"; qWarning() << "We have a password, but the settings file is not encrypted";
} }

View File

@ -20,6 +20,8 @@
#ifndef SETTINGSSERIALIZER_H #ifndef SETTINGSSERIALIZER_H
#define SETTINGSSERIALIZER_H #define SETTINGSSERIALIZER_H
#include "src/core/toxencrypt.h"
#include <QDataStream> #include <QDataStream>
#include <QSettings> #include <QSettings>
#include <QString> #include <QString>
@ -28,7 +30,7 @@
class SettingsSerializer class SettingsSerializer
{ {
public: public:
SettingsSerializer(QString filePath, const QString& password = QString()); SettingsSerializer(QString filePath, const ToxEncrypt* passKey = nullptr);
static bool isSerializedFormat(QString filePath); static bool isSerializedFormat(QString filePath);
@ -102,7 +104,7 @@ private:
private: private:
QString path; QString path;
QString password; const ToxEncrypt* passKey;
int group, array, arrayIndex; int group, array, arrayIndex;
QVector<QString> groups; QVector<QString> groups;
QVector<Array> arrays; QVector<Array> arrays;

View File

@ -433,11 +433,15 @@ void ProfileForm::onDeletePassClicked()
"deletion confirmation text"))) "deletion confirmation text")))
return; return;
Nexus::getProfile()->setPassword(QString()); QString errorMsg = pro->setPassword(QString());
if (!errorMsg.isEmpty()) {
GUI::showInfo(tr("Couldn't change password"), errorMsg);
}
} }
void ProfileForm::onChangePassClicked() void ProfileForm::onChangePassClicked()
{ {
Profile* p = Nexus::getProfile();
SetPasswordDialog* dialog = SetPasswordDialog* dialog =
new SetPasswordDialog(tr("Please enter a new password."), QString(), 0); new SetPasswordDialog(tr("Please enter a new password."), QString(), 0);
int r = dialog->exec(); int r = dialog->exec();
@ -445,7 +449,10 @@ void ProfileForm::onChangePassClicked()
return; return;
QString newPass = dialog->getPassword(); 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() void ProfileForm::retranslateUi()