diff --git a/src/apps/deskflow-gui/CMakeLists.txt b/src/apps/deskflow-gui/CMakeLists.txt index eaa6bc72c..5a60d1d49 100644 --- a/src/apps/deskflow-gui/CMakeLists.txt +++ b/src/apps/deskflow-gui/CMakeLists.txt @@ -80,6 +80,9 @@ add_executable(${target} WIN32 MACOSX_BUNDLE dialogs/AddClientDialog.cpp dialogs/AddClientDialog.h dialogs/AddClientDialog.ui + dialogs/FingerprintDialog.h + dialogs/FingerprintDialog.cpp + dialogs/FingerprintDialog.ui dialogs/HotkeyDialog.cpp dialogs/HotkeyDialog.h dialogs/HotkeyDialog.ui diff --git a/src/apps/deskflow-gui/MainWindow.cpp b/src/apps/deskflow-gui/MainWindow.cpp index b4f8bacea..c2d7b9838 100644 --- a/src/apps/deskflow-gui/MainWindow.cpp +++ b/src/apps/deskflow-gui/MainWindow.cpp @@ -10,6 +10,7 @@ #include "ui_MainWindow.h" #include "dialogs/AboutDialog.h" +#include "dialogs/FingerprintDialog.h" #include "dialogs/ServerConfigDialog.h" #include "dialogs/SettingsDialog.h" @@ -25,8 +26,6 @@ #include "gui/style_utils.h" #include "gui/styles.h" #include "net/FingerprintDatabase.h" -#include "net/SecureUtils.h" - #include "platform/wayland.h" #if defined(Q_OS_LINUX) @@ -36,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -485,31 +483,12 @@ void MainWindow::showMyFingerprint() return; } - QString message = QStringLiteral("\n"); - for (const auto &fingerprint : db.fingerprints()) { - if (fingerprint.algorithm == "sha1") { - message.append( - QStringLiteral("\nSHA1\n%1\n").arg(QString::fromStdString(deskflow::formatSSLFingerprint(fingerprint.data))) - ); - } + QList fingerprints; + fingerprints.reserve(db.fingerprints().size()); + std::copy(db.fingerprints().begin(), db.fingerprints().end(), std::back_inserter(fingerprints)); - if (fingerprint.algorithm == "sha256") { - message.append(QStringLiteral("\nSHA256\n%1\n%2\n") - .arg( - QString::fromStdString(deskflow::formatSSLFingerprintColumns(fingerprint.data)), - QString::fromStdString(deskflow::generateFingerprintArt(fingerprint.data)) - )); - } - } - - // TODO This dialog better in the future - QMessageBox mbox(this); - mbox.setWindowTitle(tr("Your Fingerprints")); - auto font = new QFont("Monospace"); - font->setStyleHint(QFont::TypeWriter); - mbox.setFont(*font); - mbox.setText(message); - mbox.exec(); + FingerprintDialog fingerprintDialog(this, fingerprints); + fingerprintDialog.exec(); } void MainWindow::setModeServer() @@ -722,7 +701,6 @@ void MainWindow::checkFingerprint(const QString &line) deskflow::string::fromHex(match.captured(2).toStdString()) }; - // Only Save the sha256 auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintTrustedServersFilename).toStdString(); deskflow::FingerprintDatabase db; @@ -731,39 +709,13 @@ void MainWindow::checkFingerprint(const QString &line) return; } - static bool messageBoxAlreadyShown = false; - - if (!messageBoxAlreadyShown) { - m_coreProcess.stop(); - - messageBoxAlreadyShown = true; - QMessageBox::StandardButton fingerprintReply = QMessageBox::information( - this, tr("Security question"), - tr("

You are connecting to a server.

" - "

Here is it's TLS fingerprint:

\n\n" - "

SHA256:%1\n" - "

%2\n\n" - "

SHA1(Obsolete): Compare for old versions only: %3

" - "

Compare this fingerprint to the one on your server's screen. " - "If the two don't match exactly, then it's probably not the server " - "you're expecting (it could be a malicious user).

" - "

Do you want to trust this fingerprint for future " - "connections? If you don't, a connection cannot be made.

") - .arg( - QString::fromStdString(deskflow::formatSSLFingerprint(sha256.data)), - QString::fromStdString(deskflow::generateFingerprintArt(sha256.data)), - QString::fromStdString(deskflow::formatSSLFingerprint(sha1.data)) - ), - QMessageBox::Yes | QMessageBox::No - ); - - if (fingerprintReply == QMessageBox::Yes) { - db.addTrusted(sha256); - db.write(localPath); - m_coreProcess.start(); - } - - messageBoxAlreadyShown = false; + m_coreProcess.stop(); + const QList fingerprints{sha1, sha256}; + FingerprintDialog fingerprintDialog(this, fingerprints, FingerprintDialogMode::Remote); + if (fingerprintDialog.exec() == QDialog::Accepted) { + db.addTrusted(sha256); + db.write(localPath); + m_coreProcess.start(); } } diff --git a/src/apps/deskflow-gui/dialogs/FingerprintDialog.cpp b/src/apps/deskflow-gui/dialogs/FingerprintDialog.cpp new file mode 100644 index 000000000..6427f150c --- /dev/null +++ b/src/apps/deskflow-gui/dialogs/FingerprintDialog.cpp @@ -0,0 +1,71 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers + * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception + */ + +#include "FingerprintDialog.h" +#include "ui_FingerprintDialog.h" + +#include "net/SecureUtils.h" + +#include +#include + +FingerprintDialog::FingerprintDialog( + QWidget *parent, const QList &fingerprints, FingerprintDialogMode mode +) + : QDialog(parent), + ui{new Ui::FingerprintDialog} +{ + ui->setupUi(this); + + if (mode == FingerprintDialogMode::Remote) { + setWindowTitle(tr("Security Question")); + ui->lblBody->setText(tr("

You are connecting to a server.

" + "

Compare the fingerprints below to the ones on your server. " + "If the two don't match exactly, then it's probably not the server " + "you're expecting (it could be a malicious user).\n

")); + + ui->lblFooter->setText(tr("

Do you want to trust this fingerprint for future " + "connections? If you don't, a connection cannot be made.

")); + ui->buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No); + connect(ui->buttonBox->button(QDialogButtonBox::Yes), &QPushButton::clicked, this, &QDialog::accept); + connect(ui->buttonBox->button(QDialogButtonBox::No), &QPushButton::clicked, this, &QDialog::reject); + } else { + setWindowTitle(tr("Your Fingerprints")); + ui->lblBody->setText(tr("These are the fingerprints for this computer")); + ui->lblFooter->setVisible(false); + } + + for (const auto &fingerprint : fingerprints) { + if (fingerprint.algorithm == "sha1") { + ui->lblSHA1->setText(QString::fromStdString(deskflow::formatSSLFingerprint(fingerprint.data))); + } + + if (fingerprint.algorithm == "sha256") { + ui->lblSHA256->setText(QString::fromStdString(deskflow::formatSSLFingerprintColumns(fingerprint.data))); + ui->lblSha256Art->setText(QString::fromStdString(deskflow::generateFingerprintArt(fingerprint.data))); + } + } + + QFont f = font(); + f.setFamilies({"Hack", "Liberation Mono", "Monospace", "Andale Mono"}); + f.setStyleHint(QFont::Monospace); + ui->lblSha256Art->setFont(f); + + if (ui->lblSHA1->text().isEmpty()) { + ui->sha1Frame->setVisible(false); + } + + if (ui->lblSHA256->text().isEmpty()) { + ui->sha256Frame->setVisible(false); + } + + adjustSize(); +} + +FingerprintDialog::~FingerprintDialog() +{ + delete ui; +} diff --git a/src/apps/deskflow-gui/dialogs/FingerprintDialog.h b/src/apps/deskflow-gui/dialogs/FingerprintDialog.h new file mode 100644 index 000000000..4727e1f4b --- /dev/null +++ b/src/apps/deskflow-gui/dialogs/FingerprintDialog.h @@ -0,0 +1,37 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers + * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception + */ + +#pragma once + +#include "net/FingerprintData.h" + +#include +#include + +namespace Ui { +class FingerprintDialog; +} + +enum FingerprintDialogMode +{ + Local, + Remote +}; + +class FingerprintDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FingerprintDialog( + QWidget *parent = nullptr, const QList &fingerprints = {}, + FingerprintDialogMode mode = FingerprintDialogMode::Local + ); + ~FingerprintDialog(); + +private: + Ui::FingerprintDialog *ui = nullptr; +}; diff --git a/src/apps/deskflow-gui/dialogs/FingerprintDialog.ui b/src/apps/deskflow-gui/dialogs/FingerprintDialog.ui new file mode 100644 index 000000000..e23ead0b8 --- /dev/null +++ b/src/apps/deskflow-gui/dialogs/FingerprintDialog.ui @@ -0,0 +1,255 @@ + + + FingerprintDialog + + + + + + + 0 + 0 + + + + + + + true + + + sha256Frame + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 10 + + + + + + + + + 0 + 0 + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Sunken + + + + + + + 0 + 0 + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Sunken + + + + 6 + + + 6 + + + + + + 0 + 0 + + + + SHA1 + + + + + + + + 0 + 0 + + + + lblSHA1 + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + + + + + 0 + 0 + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Sunken + + + + 6 + + + 6 + + + + + + 0 + 0 + + + + SHA256 + + + + + + + + + TextLabel + + + Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + sha ART + + + Qt::AlignmentFlag::AlignCenter + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 10 + + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + QDialogButtonBox::StandardButton::Ok + + + + + + + + + buttonBox + rejected() + FingerprintDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + buttonBox + accepted() + FingerprintDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + +