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:
allexzander 2022-11-01 10:41:00 +01:00 коммит произвёл GitHub
Родитель 2839c95887 9ab89daedd
Коммит 3755914a95
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 137 добавлений и 82 удалений

Просмотреть файл

@ -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);