зеркало из https://github.com/nextcloud/desktop.git
Merge pull request #5067 from nextcloud/bugfix/e2e-do-not-generate-keys-without-request
E2EE. Do not generate keypair without user request.
This commit is contained in:
Коммит
3755914a95
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"));
|
||||
|
||||
auto *mnemonic = new QAction(tr("Enable encryption"), this);
|
||||
connect(mnemonic, &QAction::triggered, this, &AccountSettings::requestMnemonic);
|
||||
connect(mnemonic, &QAction::triggered, _ui->encryptionMessage, &KMessageWidget::hide);
|
||||
|
||||
_ui->encryptionMessage->addAction(mnemonic);
|
||||
_ui->encryptionMessage->show();
|
||||
|
||||
}
|
||||
|
||||
void AccountSettings::slotE2eEncryptionGenerateKeys()
|
||||
{
|
||||
connect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished);
|
||||
_accountState->account()->setE2eEncryptionKeysGenerationAllowed(true);
|
||||
_accountState->account()->e2e()->initialize(_accountState->account());
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче