Skip to content
This repository has been archived by the owner on Feb 12, 2023. It is now read-only.

Commit

Permalink
feat: stop using plaintext passwords in the code
Browse files Browse the repository at this point in the history
  • Loading branch information
sudden6 committed Apr 8, 2017
1 parent 0f54e44 commit 084f3b0
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 77 deletions.
99 changes: 54 additions & 45 deletions src/persistence/profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}
{
Expand All @@ -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); });
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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!";
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
Expand All @@ -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";
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -534,7 +528,7 @@ bool Profile::exists(QString name)
*/
bool Profile::isEncrypted() const
{
return !password.isEmpty();
return encrypted;
}

/**
Expand Down Expand Up @@ -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();
}

/**
Expand All @@ -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;
}

/**
Expand Down
13 changes: 6 additions & 7 deletions src/persistence/profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,15 @@ class Profile : public QObject
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);

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);
Expand All @@ -83,7 +81,7 @@ class Profile : public QObject
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);
Expand All @@ -93,12 +91,13 @@ private slots:
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;
};

Expand Down
15 changes: 7 additions & 8 deletions src/persistence/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand All @@ -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());
Expand Down
7 changes: 4 additions & 3 deletions src/persistence/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define SETTINGS_HPP

#include "src/core/corestructs.h"
#include "src/core/toxencrypt.h"
#include <QDate>
#include <QFlags>
#include <QFont>
Expand Down Expand Up @@ -140,7 +141,6 @@ class Settings : public QObject
void createPersonal(QString basename);

void savePersonal();
void savePersonal(Profile* profile);

void loadGlobal();
void loadPersonal();
Expand Down Expand Up @@ -523,9 +523,10 @@ public slots:
~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;
Expand Down
19 changes: 9 additions & 10 deletions src/persistence/settingsserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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);
Expand All @@ -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";
}

Expand Down
Loading

0 comments on commit 084f3b0

Please sign in to comment.