diff --git a/src/gui/accountmanager.cpp b/src/gui/accountmanager.cpp index 331db6297cfe6..0474021c97e94 100644 --- a/src/gui/accountmanager.cpp +++ b/src/gui/accountmanager.cpp @@ -26,7 +26,6 @@ #include #include #include "clientsideencryption.h" -#include "ui_mnemonicdialog.h" namespace { constexpr auto urlC = "url"; @@ -434,23 +433,6 @@ AccountPtr AccountManager::createAccount() return acc; } -void AccountManager::displayMnemonic(const QString& mnemonic) -{ - const auto widget = new QDialog; - Ui_Dialog ui; - ui.setupUi(widget); - widget->setWindowTitle(tr("End-to-End encryption mnemonic")); - ui.label->setText(tr("To protect your Cryptographic Identity, we encrypt it with a mnemonic of 12 dictionary words. " - "Please note these down and keep them safe. " - "They will be needed to add other devices to your account (like your mobile phone or laptop).")); - ui.textEdit->setText(mnemonic); - ui.textEdit->focusWidget(); - ui.textEdit->selectAll(); - ui.textEdit->setAlignment(Qt::AlignCenter); - widget->exec(); - widget->resize(widget->sizeHint()); -} - void AccountManager::shutdown() { const auto accountsCopy = _accounts; diff --git a/src/gui/accountmanager.h b/src/gui/accountmanager.h index 04ce8e57b6168..948e1de4a0d4d 100644 --- a/src/gui/accountmanager.h +++ b/src/gui/accountmanager.h @@ -113,9 +113,6 @@ public slots: /// Saves account state data, not including the account void saveAccountState(AccountState *a); - /// Display a Box with the mnemonic so the user can copy it to a safe place. - static void displayMnemonic(const QString& mnemonic); - Q_SIGNALS: void accountAdded(AccountState *account); diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 169162a5c97a1..82520d1c5b694 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -41,6 +41,7 @@ #include "syncresult.h" #include "ignorelisttablewidget.h" #include "wizard/owncloudwizard.h" +#include "ui_mnemonicdialog.h" #include @@ -63,6 +64,9 @@ namespace { constexpr auto propertyFolder = "folder"; constexpr auto propertyPath = "path"; +constexpr auto e2eUiActionIdKey = "id"; +constexpr auto e2EeUiActionEnableEncryptionId = "enable_encryption"; +constexpr auto e2EeUiActionDisplayMnemonicId = "display_mnemonic"; } namespace OCC { @@ -223,20 +227,7 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) _ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));*/ // Connect E2E stuff - connect(this, &AccountSettings::requestMnemonic, _accountState->account()->e2e(), &ClientSideEncryption::slotRequestMnemonic); - connect(_accountState->account()->e2e(), &ClientSideEncryption::showMnemonic, this, &AccountSettings::slotShowMnemonic); - - connect(_accountState->account()->e2e(), &ClientSideEncryption::mnemonicGenerated, this, &AccountSettings::slotNewMnemonicGenerated); - if (_accountState->account()->e2e()->newMnemonicGenerated()) { - slotNewMnemonicGenerated(); - } else { - _ui->encryptionMessage->setText(tr("This account supports End-to-End encryption")); - - auto *mnemonic = new QAction(tr("Display mnemonic"), this); - connect(mnemonic, &QAction::triggered, this, &AccountSettings::requestMnemonic); - _ui->encryptionMessage->addAction(mnemonic); - _ui->encryptionMessage->hide(); - } + initializeE2eEncryption(); _ui->connectLabel->setText(tr("No account configured.")); @@ -249,16 +240,34 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) customizeStyle(); } -void AccountSettings::slotNewMnemonicGenerated() +void AccountSettings::slotE2eEncryptionMnemonicReady() { + auto *const actionDisplayMnemonic = addActionToEncryptionMessage(tr("Display mnemonic"), e2EeUiActionDisplayMnemonicId); + connect(actionDisplayMnemonic, &QAction::triggered, this, [this]() { + displayMnemonic(_accountState->account()->e2e()->_mnemonic); + }); _ui->encryptionMessage->setText(tr("This account supports End-to-End encryption")); + _ui->encryptionMessage->show(); + +} - auto *mnemonic = new QAction(tr("Enable encryption"), this); - connect(mnemonic, &QAction::triggered, this, &AccountSettings::requestMnemonic); - connect(mnemonic, &QAction::triggered, _ui->encryptionMessage, &KMessageWidget::hide); +void AccountSettings::slotE2eEncryptionGenerateKeys() +{ + connect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished); + _accountState->account()->setE2eEncryptionKeysGenerationAllowed(true); + _accountState->account()->e2e()->initialize(_accountState->account()); +} - _ui->encryptionMessage->addAction(mnemonic); - _ui->encryptionMessage->show(); +void AccountSettings::slotE2eEncryptionInitializationFinished(bool isNewMnemonicGenerated) +{ + disconnect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished); + if (!_accountState->account()->e2e()->_mnemonic.isEmpty()) { + removeActionFromEncryptionMessage(e2EeUiActionEnableEncryptionId); + slotE2eEncryptionMnemonicReady(); + if (isNewMnemonicGenerated) { + displayMnemonic(_accountState->account()->e2e()->_mnemonic); + } + } } void AccountSettings::slotEncryptFolderFinished(int status) @@ -310,11 +319,6 @@ void AccountSettings::doExpand() } } -void AccountSettings::slotShowMnemonic(const QString &mnemonic) -{ - AccountManager::instance()->displayMnemonic(mnemonic); -} - bool AccountSettings::canEncryptOrDecrypt (const FolderStatusModel::SubFolderInfo* info) { if (info->_folder->syncResult().status() != SyncResult::Status::Success) { QMessageBox msgBox; @@ -965,6 +969,35 @@ void AccountSettings::slotSetSubFolderAvailability(Folder *folder, const QString folder->scheduleThisFolderSoon(); } +void AccountSettings::displayMnemonic(const QString &mnemonic) +{ + auto widget = QDialog(this); + Ui_Dialog ui; + ui.setupUi(&widget); + widget.setWindowTitle(tr("End-to-End encryption mnemonic")); + ui.label->setText( + tr("To protect your Cryptographic Identity, we encrypt it with a mnemonic of 12 dictionary words. " + "Please note these down and keep them safe. " + "They will be needed to add other devices to your account (like your mobile phone or laptop).")); + QFont monoFont(QStringLiteral("Monospace")); + monoFont.setStyleHint(QFont::TypeWriter); + ui.lineEdit->setFont(monoFont); + ui.lineEdit->setText(mnemonic); + ui.lineEdit->setReadOnly(true); + + ui.lineEdit->setStyleSheet(QStringLiteral("QLineEdit{ color: black; background: lightgrey; border-style: inset;}")); + + ui.lineEdit->focusWidget(); + ui.lineEdit->selectAll(); + ui.lineEdit->setAlignment(Qt::AlignCenter); + + const QFont font(QStringLiteral(""), 0); + QFontMetrics fm(font); + ui.lineEdit->setFixedWidth(fm.horizontalAdvance(mnemonic)); + widget.resize(widget.sizeHint()); + widget.exec(); +} + void AccountSettings::showConnectionLabel(const QString &message, QStringList errors) { const auto errStyle = QLatin1String("color:#ffffff; background-color:#bb4d4d;padding:5px;" @@ -1417,6 +1450,46 @@ void AccountSettings::customizeStyle() _ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name())); } +void AccountSettings::initializeE2eEncryption() +{ + if (!_accountState->account()->e2e()->_mnemonic.isEmpty()) { + slotE2eEncryptionMnemonicReady(); + } else { + _ui->encryptionMessage->setText(tr("This account supports End-to-End encryption")); + _ui->encryptionMessage->hide(); + + auto *const actionEnableE2e = addActionToEncryptionMessage(tr("Enable encryption"), e2EeUiActionEnableEncryptionId); + connect(actionEnableE2e, &QAction::triggered, this, &AccountSettings::slotE2eEncryptionGenerateKeys); + } +} + +void AccountSettings::removeActionFromEncryptionMessage(const QString &actionId) +{ + const auto foundEnableEncryptionActionIt = std::find_if(std::cbegin(_ui->encryptionMessage->actions()), std::cend(_ui->encryptionMessage->actions()), [&actionId](const QAction *action) { + return action->property(e2eUiActionIdKey).toString() == actionId; + }); + if (foundEnableEncryptionActionIt != std::cend(_ui->encryptionMessage->actions())) { + _ui->encryptionMessage->removeAction(*foundEnableEncryptionActionIt); + (*foundEnableEncryptionActionIt)->deleteLater(); + } +} + +QAction *AccountSettings::addActionToEncryptionMessage(const QString &actionTitle, const QString &actionId) +{ + for (const auto &action : _ui->encryptionMessage->actions()) { + if (action->property(e2eUiActionIdKey) == actionId) { + return action; + } + } + + auto *const action = new QAction(actionTitle, this); + if (!actionId.isEmpty()) { + action->setProperty(e2eUiActionIdKey, actionId); + } + _ui->encryptionMessage->addAction(action); + return action; +} + } // namespace OCC #include "accountsettings.moc" diff --git a/src/gui/accountsettings.h b/src/gui/accountsettings.h index 8a2db31efc053..f875be0d17158 100644 --- a/src/gui/accountsettings.h +++ b/src/gui/accountsettings.h @@ -104,14 +104,16 @@ protected slots: void slotLinkActivated(const QString &link); // Encryption Related Stuff. - void slotShowMnemonic(const QString &mnemonic); - void slotNewMnemonicGenerated(); + void slotE2eEncryptionMnemonicReady(); + void slotE2eEncryptionGenerateKeys(); + void slotE2eEncryptionInitializationFinished(bool isNewMnemonicGenerated); void slotEncryptFolderFinished(int status); void slotSelectiveSyncChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); private: + void displayMnemonic(const QString &mnemonic); void showConnectionLabel(const QString &message, QStringList errors = QStringList()); bool event(QEvent *) override; @@ -119,6 +121,10 @@ protected slots: void openIgnoredFilesDialog(const QString & absFolderPath); void customizeStyle(); + void initializeE2eEncryption(); + void removeActionFromEncryptionMessage(const QString &actionId); + QAction *addActionToEncryptionMessage(const QString &actionTitle, const QString &actionId); + /// Returns the alias of the selected folder, empty string if none [[nodiscard]] QString selectedFolderAlias() const; diff --git a/src/gui/mnemonicdialog.ui b/src/gui/mnemonicdialog.ui index a482b0f34e014..7a18b6ce24e63 100644 --- a/src/gui/mnemonicdialog.ui +++ b/src/gui/mnemonicdialog.ui @@ -10,7 +10,7 @@ 0 0 588 - 214 + 131 @@ -31,6 +31,9 @@ + + 7 + QLayout::SetMinimumSize @@ -39,7 +42,7 @@ 0 - 50 + 0 @@ -73,20 +76,13 @@ 20 - 40 + 20 - - - - 16777215 - 80 - - - + diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 7f640416dbf26..6d9f2a4d5f8e1 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -933,4 +933,14 @@ bool Account::trustCertificates() const return _trustCertificates; } +void Account::setE2eEncryptionKeysGenerationAllowed(bool allowed) +{ + _e2eEncryptionKeysGenerationAllowed = allowed; +} + +[[nodiscard]] bool Account::e2eEncryptionKeysGenerationAllowed() const +{ + return _e2eEncryptionKeysGenerationAllowed; +} + } // namespace OCC diff --git a/src/libsync/account.h b/src/libsync/account.h index d96597deeb275..42af77f56ca5c 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -85,6 +85,7 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject Q_PROPERTY(QString displayName MEMBER _displayName) Q_PROPERTY(QString prettyName READ prettyName NOTIFY prettyNameChanged) Q_PROPERTY(QUrl url MEMBER _url) + Q_PROPERTY(bool e2eEncryptionKeysGenerationAllowed MEMBER _e2eEncryptionKeysGenerationAllowed) public: static AccountPtr create(); @@ -300,6 +301,9 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject void setTrustCertificates(bool trustCertificates); [[nodiscard]] bool trustCertificates() const; + void setE2eEncryptionKeysGenerationAllowed(bool allowed); + [[nodiscard]] bool e2eEncryptionKeysGenerationAllowed() const; + public slots: /// Used when forgetting credentials void clearQNAMCache(); @@ -355,6 +359,8 @@ protected Q_SLOTS: bool _trustCertificates = false; + bool _e2eEncryptionKeysGenerationAllowed = false; + QWeakPointer _sharedThis; QString _id; QString _davUser; diff --git a/src/libsync/clientsideencryption.cpp b/src/libsync/clientsideencryption.cpp index 2408b6adec9cd..51ca66a7fbfe1 100644 --- a/src/libsync/clientsideencryption.cpp +++ b/src/libsync/clientsideencryption.cpp @@ -1082,11 +1082,6 @@ void ClientSideEncryption::forgetSensitiveData(const AccountPtr &account) startDeleteJob(user + e2e_mnemonic); } -void ClientSideEncryption::slotRequestMnemonic() -{ - emit showMnemonic(_mnemonic); -} - void ClientSideEncryption::generateKeyPair(const AccountPtr &account) { // AES/GCM/NoPadding, @@ -1223,11 +1218,8 @@ void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account) { QStringList list = WordList::getRandomWords(12); _mnemonic = list.join(' '); - _newMnemonicGenerated = true; qCInfo(lcCse()) << "mnemonic Generated:" << _mnemonic; - emit mnemonicGenerated(_mnemonic); - QString passPhrase = list.join(QString()).toLower(); qCInfo(lcCse()) << "Passphrase Generated:" << passPhrase; @@ -1246,7 +1238,7 @@ void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account) writePrivateKey(account); writeCertificate(account); writeMnemonic(account); - emit initializationFinished(); + emit initializationFinished(true); break; default: qCInfo(lcCse()) << "Store private key failed, return code:" << retCode; @@ -1255,11 +1247,6 @@ void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account) job->start(); } -bool ClientSideEncryption::newMnemonicGenerated() const -{ - return _newMnemonicGenerated; -} - void ClientSideEncryption::decryptPrivateKey(const AccountPtr &account, const QByteArray &key) { QString msg = tr("Please enter your End-to-End encryption passphrase:
" "
" @@ -1349,6 +1336,11 @@ void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account) fetchAndValidatePublicKeyFromServer(account); } else if (retCode == 404) { qCInfo(lcCse()) << "No public key on the server"; + if (!account->e2eEncryptionKeysGenerationAllowed()) { + qCInfo(lcCse()) << "User did not allow E2E keys generation."; + emit initializationFinished(); + return; + } generateKeyPair(account); } else { qCInfo(lcCse()) << "Error while requesting public key: " << retCode; diff --git a/src/libsync/clientsideencryption.h b/src/libsync/clientsideencryption.h index ec51de61b0c91..552282e3864c9 100644 --- a/src/libsync/clientsideencryption.h +++ b/src/libsync/clientsideencryption.h @@ -130,20 +130,13 @@ class OWNCLOUDSYNC_EXPORT ClientSideEncryption : public QObject { public: void forgetSensitiveData(const AccountPtr &account); - [[nodiscard]] bool newMnemonicGenerated() const; - -public slots: - void slotRequestMnemonic(); - private slots: void publicKeyFetched(QKeychain::Job *incoming); void privateKeyFetched(QKeychain::Job *incoming); void mnemonicKeyFetched(QKeychain::Job *incoming); signals: - void initializationFinished(); - void mnemonicGenerated(const QString& mnemonic); - void showMnemonic(const QString& mnemonic); + void initializationFinished(bool isNewMnemonicGenerated = false); private: void getPrivateKeyFromServer(const AccountPtr &account);