diff --git a/src/persistence/profile.cpp b/src/persistence/profile.cpp index f3c0478e7..f2e6cde01 100644 --- a/src/persistence/profile.cpp +++ b/src/persistence/profile.cpp @@ -51,7 +51,6 @@ QVector 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 tmpKey; + std::unique_ptr 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 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 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 friendList = core->getFriendList(); QVectorIterator i(friendList); while (i.hasNext()) { QString friendPublicKey = core->getFriendPublicKey(i.next()).toString(); - saveAvatar(loadAvatarData(friendPublicKey, oldPassword), friendPublicKey); + saveAvatar(loadAvatarData(friendPublicKey), friendPublicKey); } + return error; } /** diff --git a/src/persistence/profile.h b/src/persistence/profile.h index 2b24a7337..76c6f1579 100644 --- a/src/persistence/profile.h +++ b/src/persistence/profile.h @@ -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 passkey; + QString name; + std::unique_ptr passkey = nullptr; std::shared_ptr database; std::unique_ptr history; bool newProfile; bool isRemoved; + bool encrypted = false; static QVector profiles; }; diff --git a/src/persistence/settings.cpp b/src/persistence/settings.cpp index e4ec43e49..daf188051 100644 --- a/src/persistence/settings.cpp +++ b/src/persistence/settings.cpp @@ -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()); diff --git a/src/persistence/settings.h b/src/persistence/settings.h index 1376afd23..c12b05905 100644 --- a/src/persistence/settings.h +++ b/src/persistence/settings.h @@ -22,6 +22,7 @@ #define SETTINGS_HPP #include "src/core/corestructs.h" +#include "src/core/toxencrypt.h" #include #include #include @@ -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; diff --git a/src/persistence/settingsserializer.cpp b/src/persistence/settingsserializer.cpp index 2a5619287..b5ad8720c 100644 --- a/src/persistence/settingsserializer.cpp +++ b/src/persistence/settingsserializer.cpp @@ -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(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"; } diff --git a/src/persistence/settingsserializer.h b/src/persistence/settingsserializer.h index ee56486be..a03f570ef 100644 --- a/src/persistence/settingsserializer.h +++ b/src/persistence/settingsserializer.h @@ -20,6 +20,8 @@ #ifndef SETTINGSSERIALIZER_H #define SETTINGSSERIALIZER_H +#include "src/core/toxencrypt.h" + #include #include #include @@ -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 groups; QVector arrays; diff --git a/src/widget/form/profileform.cpp b/src/widget/form/profileform.cpp index 17e2ec743..763255af9 100644 --- a/src/widget/form/profileform.cpp +++ b/src/widget/form/profileform.cpp @@ -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()