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)
: 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;
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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