Просмотр исходного кода

Merge pull request #5067 from nextcloud/bugfix/e2e-do-not-generate-keys-without-request

E2EE. Do not generate keypair without user request.
allexzander 3 лет назад
Родитель
Сommit
3755914a95

+ 0 - 18
src/gui/accountmanager.cpp

@@ -26,7 +26,6 @@
 #include <QNetworkAccessManager>
 #include <QMessageBox>
 #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;

+ 0 - 3
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);

+ 98 - 25
src/gui/accountsettings.cpp

@@ -41,6 +41,7 @@
 #include "syncresult.h"
 #include "ignorelisttablewidget.h"
 #include "wizard/owncloudwizard.h"
+#include "ui_mnemonicdialog.h"
 
 #include <cmath>
 
@@ -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"

+ 8 - 2
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<int> &roles);
 
 private:
+    void displayMnemonic(const QString &mnemonic);
     void showConnectionLabel(const QString &message,
         QStringList errors = QStringList());
     bool event(QEvent *) override;
@@ -119,6 +121,10 @@ private:
     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;
 

+ 7 - 11
src/gui/mnemonicdialog.ui

@@ -10,7 +10,7 @@
     <x>0</x>
     <y>0</y>
     <width>588</width>
-    <height>214</height>
+    <height>131</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -31,6 +31,9 @@
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <layout class="QVBoxLayout" name="verticalLayout">
+     <property name="spacing">
+      <number>7</number>
+     </property>
      <property name="sizeConstraint">
       <enum>QLayout::SetMinimumSize</enum>
      </property>
@@ -39,7 +42,7 @@
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
          <horstretch>0</horstretch>
-         <verstretch>50</verstretch>
+         <verstretch>0</verstretch>
         </sizepolicy>
        </property>
        <property name="minimumSize">
@@ -73,20 +76,13 @@
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
-         <height>40</height>
+         <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
-      <widget class="QTextEdit" name="textEdit">
-       <property name="maximumSize">
-        <size>
-         <width>16777215</width>
-         <height>80</height>
-        </size>
-       </property>
-      </widget>
+      <widget class="QLineEdit" name="lineEdit"/>
      </item>
      <item>
       <widget class="QDialogButtonBox" name="buttonBox">

+ 10 - 0
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

+ 6 - 0
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 @@ public:
     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 @@ private:
 
     bool _trustCertificates = false;
 
+    bool _e2eEncryptionKeysGenerationAllowed = false;
+
     QWeakPointer<Account> _sharedThis;
     QString _id;
     QString _davUser;

+ 6 - 14
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:<br>"
                      "<br>"
@@ -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;

+ 1 - 8
src/libsync/clientsideencryption.h

@@ -130,20 +130,13 @@ private:
 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);