Skip to content

Commit

Permalink
allow to select and store the certificate to use for e2e
Browse files Browse the repository at this point in the history
Signed-off-by: Matthieu Gallien <[email protected]>
  • Loading branch information
mgallien committed Nov 24, 2023
1 parent d687da8 commit 1a776b4
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 251 deletions.
12 changes: 8 additions & 4 deletions src/gui/EncryptionTokenSelectionWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import "./tray"
ApplicationWindow {
id: encryptionKeyChooserDialog

required property var tokensInfo
required property var keysInfo
required property var certificatesInfo
required property ClientSideTokenSelector certificateSelector
property string selectedSerialNumber: ''

flags: Qt.Window | Qt.Dialog
visible: true
Expand Down Expand Up @@ -94,18 +95,19 @@ ApplicationWindow {
currentIndex: -1

model: DelegateModel {
model: keysInfo
model: certificatesInfo

delegate: ItemDelegate {
width: tokensListView.contentItem.width

text: modelData.label
text: modelData.subject

highlighted: tokensListView.currentIndex === index

onClicked: function()
{
tokensListView.currentIndex = index
selectedSerialNumber = modelData.serialNumber
}
}
}
Expand All @@ -126,10 +128,12 @@ ApplicationWindow {

onAccepted: function() {
Systray.destroyDialog(encryptionKeyChooserDialog)
certificateSelector.serialNumber = selectedSerialNumber
}

onRejected: function() {
Systray.destroyDialog(encryptionKeyChooserDialog)
certificateSelector.serialNumber = ''
}
}
}
Expand Down
14 changes: 2 additions & 12 deletions src/gui/accountsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,16 +283,14 @@ void AccountSettings::slotE2eEncryptionMnemonicReady()
void AccountSettings::slotE2eEncryptionGenerateKeys()
{
connect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished);
connect(_accountState->account()->e2e(), &ClientSideEncryption::displayTokenInitDialog, this, &AccountSettings::slotDisplayTokenInitDialog);
_accountState->account()->setE2eEncryptionKeysGenerationAllowed(true);
_accountState->account()->setAskUserForMnemonic(true);
_accountState->account()->e2e()->initialize(_accountState->account());
_accountState->account()->e2e()->initialize(this, _accountState->account());
}

void AccountSettings::slotE2eEncryptionInitializationFinished(bool isNewMnemonicGenerated)
{
disconnect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished);
disconnect(_accountState->account()->e2e(), &ClientSideEncryption::displayTokenInitDialog, this, &AccountSettings::slotDisplayTokenInitDialog);
if (_accountState->account()->e2e()->isInitialized()) {
removeActionFromEncryptionMessage(e2EeUiActionEnableEncryptionId);
slotE2eEncryptionMnemonicReady();
Expand All @@ -303,14 +301,6 @@ void AccountSettings::slotE2eEncryptionInitializationFinished(bool isNewMnemonic
_accountState->account()->setAskUserForMnemonic(false);
}

void AccountSettings::slotDisplayTokenInitDialog()
{
disconnect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, &AccountSettings::slotE2eEncryptionInitializationFinished);
disconnect(_accountState->account()->e2e(), &ClientSideEncryption::displayTokenInitDialog, this, &AccountSettings::slotDisplayTokenInitDialog);
Systray::instance()->createTokenInitDialog(_accountState->account()->e2e()->discoveredTokens(),
_accountState->account()->e2e()->discoveredKeys());
}

void AccountSettings::slotEncryptFolderFinished(int status)
{
qCInfo(lcAccountSettings) << "Current folder encryption status code:" << status;
Expand Down Expand Up @@ -1640,7 +1630,7 @@ void AccountSettings::initializeE2eEncryption()
}
});
_accountState->account()->setE2eEncryptionKeysGenerationAllowed(false);
_accountState->account()->e2e()->initialize(_accountState->account());
_accountState->account()->e2e()->initialize(this, _accountState->account());
}
}

Expand Down
1 change: 0 additions & 1 deletion src/gui/accountsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ protected slots:
void slotE2eEncryptionMnemonicReady();
void slotE2eEncryptionGenerateKeys();
void slotE2eEncryptionInitializationFinished(bool isNewMnemonicGenerated);
void slotDisplayTokenInitDialog();
void slotEncryptFolderFinished(int status);

void slotSelectiveSyncChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
Expand Down
2 changes: 1 addition & 1 deletion src/gui/connectionvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void ConnectionValidator::slotUserFetched(UserInfo *userInfo)

#ifndef TOKEN_AUTH_ONLY
connect(_account->e2e(), &ClientSideEncryption::initializationFinished, this, &ConnectionValidator::reportConnected);
_account->e2e()->initialize(_account);
_account->e2e()->initialize(nullptr, _account);
#else
reportResult(Connected);
#endif
Expand Down
1 change: 1 addition & 0 deletions src/gui/owncloudgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ ownCloudGui::ownCloudGui(Application *parent)
qmlRegisterUncreatableType<UnifiedSearchResultsListModel>("com.nextcloud.desktopclient", 1, 0, "UnifiedSearchResultsListModel", "UnifiedSearchResultsListModel");
qmlRegisterUncreatableType<UserStatus>("com.nextcloud.desktopclient", 1, 0, "UserStatus", "Access to Status enum");
qmlRegisterUncreatableType<Sharee>("com.nextcloud.desktopclient", 1, 0, "Sharee", "Access to Type enum");
qmlRegisterUncreatableType<ClientSideTokenSelector>("com.nextcloud.desktopclient", 1, 0, "ClientSideTokenSelector", "Access to the certificate selector");

qRegisterMetaTypeStreamOperators<Emoji>();

Expand Down
46 changes: 2 additions & 44 deletions src/gui/systray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "configfile.h"
#include "accessmanager.h"
#include "callstatechecker.h"
#include "clientsidetokenselector.h"

#include <QCursor>
#include <QGuiApplication>
Expand All @@ -35,6 +36,7 @@
#include <QMenu>
#include <QGuiApplication>
#include <QQuickView>
#include <QMessageBox>

#ifdef USE_FDO_NOTIFICATIONS
#include <QDBusConnection>
Expand Down Expand Up @@ -413,50 +415,6 @@ void Systray::createFileActivityDialog(const QString &localPath)
Q_EMIT showFileDetailsPage(localPath, FileDetailsPage::Activity);
}

void Systray::createTokenInitDialog(const QVariantList &tokensInfo,
const QVariantList &keysInfo)
{
if(_tokenInitDialog) {
destroyDialog(_tokenInitDialog);
_tokenInitDialog = nullptr;
}

qCDebug(lcSystray) << "Opening new token init dialog with " << tokensInfo.size() << "possible tokens";

if (!_trayEngine) {
qCWarning(lcSystray) << "Could not open token init dialog as no tray engine was available";
return;
}

const QVariantMap initialProperties{
{"tokensInfo", tokensInfo},
{"keysInfo", keysInfo}
};

QQmlComponent encryptionTokenDialog(_trayEngine, QStringLiteral("qrc:/qml/src/gui/EncryptionTokenSelectionWindow.qml"));

if (!encryptionTokenDialog.isError()) {
const auto createdDialog = encryptionTokenDialog.createWithInitialProperties(initialProperties);
const auto dialog = qobject_cast<QQuickWindow*>(createdDialog);

if(!dialog) {
qCWarning(lcSystray) << "File details dialog window resulted in creation of object that was not a window!";
return;
}

_tokenInitDialog = dialog;

Q_EMIT hideSettingsDialog();

dialog->show();
dialog->raise();
dialog->requestActivate();

} else {
qCWarning(lcSystray) << encryptionTokenDialog.errorString();
}
}

void Systray::presentShareViewInTray(const QString &localPath)
{
const auto folder = FolderMan::instance()->folderForPath(localPath);
Expand Down
4 changes: 2 additions & 2 deletions src/gui/systray.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class QGuiApplication;

namespace OCC {

class ClientSideTokenSelector;

class AccessManagerFactory : public QQmlNetworkAccessManagerFactory
{
public:
Expand Down Expand Up @@ -147,8 +149,6 @@ public slots:

void createShareDialog(const QString &localPath);
void createFileActivityDialog(const QString &localPath);
void createTokenInitDialog(const QVariantList &tokensInfo,
const QVariantList &keysInfo);

void presentShareViewInTray(const QString &localPath);

Expand Down
105 changes: 70 additions & 35 deletions src/libsync/clientsideencryption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <QXmlStreamNamespaceDeclaration>
#include <QStack>
#include <QInputDialog>
#include <QMessageBox>
#include <QWidget>
#include <QLineEdit>
#include <QIODevice>
#include <QUuid>
Expand Down Expand Up @@ -1014,23 +1016,13 @@ std::optional<QByteArray> decryptStringAsymmetricWithToken(ENGINE *sslEngine,

ClientSideEncryption::ClientSideEncryption()
{
connect(&_usbTokenInformation, &ClientSideTokenSelector::discoveredTokensChanged,
this, &ClientSideEncryption::displayTokenInitDialog);
connect(&_usbTokenInformation, &ClientSideTokenSelector::discoveredCertificatesChanged,
this, &ClientSideEncryption::completeHardwareTokenInitialization);
}

bool ClientSideEncryption::isInitialized() const
{
return !getMnemonic().isEmpty();
}

QVariantList ClientSideEncryption::discoveredTokens() const
{
return _usbTokenInformation.discoveredTokens();
}

QVariantList ClientSideEncryption::discoveredKeys() const
{
return _usbTokenInformation.discoveredKeys();
return useTokenBasedEncryption() || !getMnemonic().isEmpty();
}

const QSslKey &ClientSideEncryption::getPublicKey() const
Expand Down Expand Up @@ -1083,7 +1075,13 @@ ENGINE* ClientSideEncryption::sslEngine() const
return ENGINE_get_default_RSA();
}

void ClientSideEncryption::initialize(const AccountPtr &account)
ClientSideTokenSelector *ClientSideEncryption::usbTokenInformation()
{
return &_usbTokenInformation;
}

void ClientSideEncryption::initialize(QWidget *settingsDialog,
const AccountPtr &account)
{
Q_ASSERT(account);

Expand All @@ -1096,11 +1094,11 @@ void ClientSideEncryption::initialize(const AccountPtr &account)

if (account->enforceUseHardwareTokenEncryption()) {
if (_usbTokenInformation.isSetup()) {
initializeHardwareTokenEncryption(account);
initializeHardwareTokenEncryption(settingsDialog, account);
} else if (account->e2eEncryptionKeysGenerationAllowed() && account->askUserForMnemonic()) {
_usbTokenInformation.searchForToken(account);
_usbTokenInformation.searchForCertificates(account);
if (_usbTokenInformation.isSetup()) {
initializeHardwareTokenEncryption(account);
initializeHardwareTokenEncryption(settingsDialog, account);
} else {
emit initializationFinished();
}
Expand All @@ -1112,7 +1110,8 @@ void ClientSideEncryption::initialize(const AccountPtr &account)
}
}

void ClientSideEncryption::initializeHardwareTokenEncryption(const AccountPtr &account)
void ClientSideEncryption::initializeHardwareTokenEncryption(QWidget *settingsDialog,
const AccountPtr &account)
{
auto ctx = PKCS11_CTX_new();

Expand Down Expand Up @@ -1156,27 +1155,46 @@ void ClientSideEncryption::initializeHardwareTokenEncryption(const AccountPtr &a
return;
}

/* perform pkcs #11 login */
QByteArray password = "0000";
if (PKCS11_login(slot, 0, password.data()) != 0) {
qCWarning(lcCse()) << "PKCS11_login failed" << ERR_reason_error_string(ERR_get_error());
while (true) {
/* perform pkcs #11 login */
bool ok;
QString text = QInputDialog::getText(settingsDialog,
tr("PIN needed to login to token"),
tr("PIN:"),
QLineEdit::Password,
{},
&ok);
if (!ok || text.isEmpty()) {
qCWarning(lcCse()) << "an USER pin is required";

failedToInitialize(account);
return;
}
failedToInitialize(account);
return;
}

/* check if user is logged in */
if (PKCS11_is_logged_in(slot, 0, &logged_in) != 0) {
qCWarning(lcCse()) << "PKCS11_is_logged_in failed" << ERR_reason_error_string(ERR_get_error());
const auto password = text.toLatin1();
if (PKCS11_login(slot, 0, password.data()) != 0) {
QMessageBox::warning(settingsDialog,
tr("Invalid PIN. Login failed"),
tr("Login to the token failed after providing the user PIN. It may be invalid or wrong. Please try again !"),
QMessageBox::Ok);
continue;
}

failedToInitialize(account);
return;
}
if (!logged_in) {
qCWarning(lcCse()) << "PKCS11_is_logged_in says user is not logged in, expected to be logged in";
/* check if user is logged in */
if (PKCS11_is_logged_in(slot, 0, &logged_in) != 0) {
qCWarning(lcCse()) << "PKCS11_is_logged_in failed" << ERR_reason_error_string(ERR_get_error());

failedToInitialize(account);
return;
failedToInitialize(account);
return;
}
if (!logged_in) {
qCWarning(lcCse()) << "PKCS11_is_logged_in says user is not logged in, expected to be logged in";

failedToInitialize(account);
return;
}

break;
}

auto privateKeysCount = 0u;
Expand Down Expand Up @@ -1518,6 +1536,23 @@ void ClientSideEncryption::writeCertificate(const AccountPtr &account)
job->start();
}

void ClientSideEncryption::completeHardwareTokenInitialization()
{
for (const auto &oneCertificate : _usbTokenInformation.discoveredCertificates()) {
const auto certificateData = oneCertificate.toMap();
const auto sslCertificate = certificateData[QStringLiteral("certificate")].value<QSslCertificate>();
if (sslCertificate.isNull()) {
qCDebug(lcCse()) << "null certificate";
continue;
}
const auto sslErrors = QSslCertificate::verify({sslCertificate});
for (const auto &oneError : sslErrors) {
qCInfo(lcCse()) << oneError;
}
qCInfo(lcCse()) << "certificate is valid" << certificateData[QStringLiteral("serialNumber")] << certificateData[QStringLiteral("issuer")];
}
}

void ClientSideEncryption::generateMnemonic()
{
const auto list = WordList::getRandomWords(12);
Expand Down
Loading

0 comments on commit 1a776b4

Please sign in to comment.