Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
authorJonathan White <support@dmapps.us>2022-02-22 04:40:01 +0300
committerJonathan White <support@dmapps.us>2022-02-23 01:53:50 +0300
commit4f0710350fb53c25db6824f96961d8d17a29347c (patch)
treeb1c6202fc97b5d39f0f324fa6d7faa977d018dfb /src/gui
parenta76daeb4c5994d10510b1e33d53a8c24634efcbf (diff)
Add support for Windows Hello
* Special thanks to @HexF and @smlu for their contributions towards this feature. * Add MVP support for Windows Hello as a Quick Unlock solution using the WinRT API. This works by signing a random challenge vector with the Windows Hello protected key store (typically from TPM). The signed challenge is hashed using SHA-256 and then used as the encryption key to encrypt the database credentials. Credentials are encrypted using AES-256/GCM. This ensures the database password can only be decrypted following a successful authentication with Windows Hello in the future. * Unify Touch ID and Windows Hello behavior under the Quick Unlock branding. Remove all timeout features of Touch ID as they are unnecessary and complicate the feature for no security gain. * Quick Unlock is automatically reset only when the database key is changed vice whenever database settings are modified. * Don't set database unlock dialog as always on top. This allows Touch ID and Windows Hello prompts to appear above the dialog properly. * Prevent quick unlock when using AutoOpen or opening from the command line.
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/ApplicationSettingsWidget.cpp30
-rw-r--r--src/gui/ApplicationSettingsWidgetSecurity.ui139
-rw-r--r--src/gui/DatabaseOpenDialog.cpp2
-rw-r--r--src/gui/DatabaseOpenWidget.cpp238
-rw-r--r--src/gui/DatabaseOpenWidget.h5
-rw-r--r--src/gui/DatabaseOpenWidget.ui798
-rw-r--r--src/gui/Icons.cpp13
-rw-r--r--src/gui/MainWindow.cpp38
-rw-r--r--src/gui/MainWindow.h1
-rw-r--r--src/gui/MainWindow.ui10
-rw-r--r--src/gui/dbsettings/DatabaseSettingsDialog.cpp7
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetDatabaseKey.cpp13
-rw-r--r--src/gui/reports/ReportsDialog.cpp8
-rw-r--r--src/gui/styles/base/basestyle.qss4
-rw-r--r--src/gui/styles/base/classicstyle.qss2
15 files changed, 672 insertions, 636 deletions
diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp
index 7620c9ba5..9dd9e3df8 100644
--- a/src/gui/ApplicationSettingsWidget.cpp
+++ b/src/gui/ApplicationSettingsWidget.cpp
@@ -35,6 +35,9 @@
#ifdef Q_OS_MACOS
#include "touchid/TouchID.h"
#endif
+#ifdef Q_CC_MSVC
+#include "winhello/WindowsHello.h"
+#endif
class ApplicationSettingsWidget::ExtraPage
{
@@ -129,8 +132,6 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_secUi->clearSearchSpinBox, SLOT(setEnabled(bool)));
connect(m_secUi->lockDatabaseIdleCheckBox, SIGNAL(toggled(bool)),
m_secUi->lockDatabaseIdleSpinBox, SLOT(setEnabled(bool)));
- connect(m_secUi->touchIDResetCheckBox, SIGNAL(toggled(bool)),
- m_secUi->touchIDResetSpinBox, SLOT(setEnabled(bool)));
// clang-format on
// Disable mouse wheel grab when scrolling
@@ -155,16 +156,14 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_generalUi->faviconTimeoutSpinBox->setVisible(false);
#endif
-#ifndef WITH_XC_TOUCHID
- bool hideTouchID = true;
-#else
- bool hideTouchID = !TouchID::getInstance().isAvailable();
+ bool showQuickUnlock = false;
+#if defined(Q_OS_MACOS)
+ showQuickUnlock = TouchID::getInstance().isAvailable();
+#elif defined(Q_CC_MSVC)
+ showQuickUnlock = getWindowsHello()->isAvailable();
+ connect(getWindowsHello(), &WindowsHello::availableChanged, m_secUi->quickUnlockCheckBox, &QCheckBox::setVisible);
#endif
- if (hideTouchID) {
- m_secUi->touchIDResetCheckBox->setVisible(false);
- m_secUi->touchIDResetSpinBox->setVisible(false);
- m_secUi->touchIDResetOnScreenLockCheckBox->setVisible(false);
- }
+ m_secUi->quickUnlockCheckBox->setVisible(showQuickUnlock);
}
ApplicationSettingsWidget::~ApplicationSettingsWidget()
@@ -313,10 +312,7 @@ void ApplicationSettingsWidget::loadSettings()
m_secUi->EnableCopyOnDoubleClickCheckBox->setChecked(
config()->get(Config::Security_EnableCopyOnDoubleClick).toBool());
- m_secUi->touchIDResetCheckBox->setChecked(config()->get(Config::Security_ResetTouchId).toBool());
- m_secUi->touchIDResetSpinBox->setValue(config()->get(Config::Security_ResetTouchIdTimeout).toInt());
- m_secUi->touchIDResetOnScreenLockCheckBox->setChecked(
- config()->get(Config::Security_ResetTouchIdScreenlock).toBool());
+ m_secUi->quickUnlockCheckBox->setChecked(config()->get(Config::Security_QuickUnlock).toBool());
for (const ExtraPage& page : asConst(m_extraPages)) {
page.loadSettings();
@@ -425,9 +421,7 @@ void ApplicationSettingsWidget::saveSettings()
m_secUi->NoConfirmMoveEntryToRecycleBinCheckBox->isChecked());
config()->set(Config::Security_EnableCopyOnDoubleClick, m_secUi->EnableCopyOnDoubleClickCheckBox->isChecked());
- config()->set(Config::Security_ResetTouchId, m_secUi->touchIDResetCheckBox->isChecked());
- config()->set(Config::Security_ResetTouchIdTimeout, m_secUi->touchIDResetSpinBox->value());
- config()->set(Config::Security_ResetTouchIdScreenlock, m_secUi->touchIDResetOnScreenLockCheckBox->isChecked());
+ config()->set(Config::Security_QuickUnlock, m_secUi->quickUnlockCheckBox->isChecked());
// Security: clear storage if related settings are disabled
if (!config()->get(Config::RememberLastDatabases).toBool()) {
diff --git a/src/gui/ApplicationSettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui
index bf4b984e1..5ca7d69e0 100644
--- a/src/gui/ApplicationSettingsWidgetSecurity.ui
+++ b/src/gui/ApplicationSettingsWidgetSecurity.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>595</width>
- <height>567</height>
+ <width>364</width>
+ <height>493</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -28,9 +28,9 @@
<property name="title">
<string>Timeouts</string>
</property>
- <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,1">
- <item row="0" column="1">
- <widget class="QSpinBox" name="clearClipboardSpinBox">
+ <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0">
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="lockDatabaseIdleSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
@@ -41,44 +41,38 @@
</sizepolicy>
</property>
<property name="accessibleName">
- <string>Clipboard clear seconds</string>
+ <string>Database lock timeout seconds</string>
</property>
<property name="suffix">
<string comment="Seconds"> sec</string>
</property>
<property name="minimum">
- <number>1</number>
+ <number>10</number>
</property>
<property name="maximum">
- <number>999</number>
+ <number>43200</number>
</property>
<property name="value">
- <number>10</number>
+ <number>240</number>
</property>
</widget>
</item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="touchIDResetCheckBox">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="clearClipboardCheckBox">
<property name="text">
- <string>Forget TouchID after inactivity of</string>
+ <string>Clear clipboard after</string>
</property>
</widget>
</item>
- <item row="0" column="2">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="clearSearchCheckBox">
+ <property name="text">
+ <string>Clear search query after</string>
</property>
- </spacer>
+ </widget>
</item>
- <item row="3" column="1">
- <widget class="QSpinBox" name="touchIDResetSpinBox">
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="clearSearchSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
@@ -88,35 +82,25 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="accessibleName">
- <string>Touch ID inactivity reset</string>
- </property>
<property name="suffix">
- <string> min</string>
+ <string comment="Minutes"> min</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
</property>
<property name="maximum">
<number>1440</number>
</property>
<property name="value">
- <number>30</number>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="lockDatabaseIdleCheckBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <number>5</number>
</property>
- <property name="text">
- <string>Lock databases after inactivity of</string>
+ <property name="displayIntegerBase">
+ <number>10</number>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QSpinBox" name="lockDatabaseIdleSpinBox">
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="clearClipboardSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
@@ -127,61 +111,45 @@
</sizepolicy>
</property>
<property name="accessibleName">
- <string>Database lock timeout seconds</string>
+ <string>Clipboard clear seconds</string>
</property>
<property name="suffix">
<string comment="Seconds"> sec</string>
</property>
<property name="minimum">
- <number>10</number>
+ <number>1</number>
</property>
<property name="maximum">
- <number>43200</number>
+ <number>999</number>
</property>
<property name="value">
- <number>240</number>
+ <number>10</number>
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QSpinBox" name="clearSearchSpinBox">
- <property name="enabled">
- <bool>false</bool>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
</property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="lockDatabaseIdleCheckBox">
<property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="suffix">
- <string comment="Minutes"> min</string>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>1440</number>
- </property>
- <property name="value">
- <number>5</number>
- </property>
- <property name="displayIntegerBase">
- <number>10</number>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QCheckBox" name="clearClipboardCheckBox">
- <property name="text">
- <string>Clear clipboard after</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="clearSearchCheckBox">
<property name="text">
- <string>Clear search query after</string>
+ <string>Lock databases after inactivity of</string>
</property>
</widget>
</item>
@@ -195,16 +163,16 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="QCheckBox" name="lockDatabaseOnScreenLockCheckBox">
+ <widget class="QCheckBox" name="quickUnlockCheckBox">
<property name="text">
- <string>Lock databases when session is locked or lid is closed</string>
+ <string>Enable database quick unlock (Touch ID / Windows Hello)</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="touchIDResetOnScreenLockCheckBox">
+ <widget class="QCheckBox" name="lockDatabaseOnScreenLockCheckBox">
<property name="text">
- <string>Forget TouchID when session is locked or lid is closed</string>
+ <string>Lock databases when session is locked or lid is closed</string>
</property>
</widget>
</item>
@@ -308,10 +276,7 @@
<tabstop>lockDatabaseIdleSpinBox</tabstop>
<tabstop>clearSearchCheckBox</tabstop>
<tabstop>clearSearchSpinBox</tabstop>
- <tabstop>touchIDResetCheckBox</tabstop>
- <tabstop>touchIDResetSpinBox</tabstop>
<tabstop>lockDatabaseOnScreenLockCheckBox</tabstop>
- <tabstop>touchIDResetOnScreenLockCheckBox</tabstop>
<tabstop>lockDatabaseMinimizeCheckBox</tabstop>
<tabstop>passwordsRepeatVisibleCheckBox</tabstop>
<tabstop>passwordsHiddenCheckBox</tabstop>
diff --git a/src/gui/DatabaseOpenDialog.cpp b/src/gui/DatabaseOpenDialog.cpp
index cfa0fcadf..1fd4f1c0d 100644
--- a/src/gui/DatabaseOpenDialog.cpp
+++ b/src/gui/DatabaseOpenDialog.cpp
@@ -35,7 +35,7 @@ DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent)
, m_tabBar(new QTabBar(this))
{
setWindowTitle(tr("Unlock Database - KeePassXC"));
- setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint);
+ setWindowFlags(Qt::Dialog);
// block input to the main window/application while the dialog is open
setWindowModality(Qt::ApplicationModal);
#ifdef Q_OS_WIN
diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp
index 1d51b5ebe..23719a3c1 100644
--- a/src/gui/DatabaseOpenWidget.cpp
+++ b/src/gui/DatabaseOpenWidget.cpp
@@ -30,14 +30,43 @@
#ifdef Q_OS_MACOS
#include "touchid/TouchID.h"
#endif
+#ifdef Q_CC_MSVC
+#include "winhello/WindowsHello.h"
+#endif
+#include <QCheckBox>
#include <QDesktopServices>
#include <QFont>
namespace
{
constexpr int clearFormsDelay = 30000;
-}
+
+ bool isQuickUnlockAvailable()
+ {
+ if (config()->get(Config::Security_QuickUnlock).toBool()) {
+#if defined(Q_CC_MSVC)
+ return getWindowsHello()->isAvailable();
+#elif defined(Q_OS_MACOS)
+ return TouchID::getInstance().isAvailable();
+#endif
+ }
+ return false;
+ }
+
+ bool canPerformQuickUnlock(const QString& filename)
+ {
+ if (isQuickUnlockAvailable()) {
+#if defined(Q_CC_MSVC)
+ return getWindowsHello()->hasKey(filename);
+#elif defined(Q_OS_MACOS)
+ return TouchID::getInstance().containsKey(filename);
+#endif
+ }
+ Q_UNUSED(filename);
+ return false;
+ }
+} // namespace
DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
: DialogyWidget(parent)
@@ -62,8 +91,16 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
m_ui->labelHeadline->setFont(font);
m_ui->labelHeadline->setText(tr("Unlock KeePassXC Database"));
+ m_ui->quickUnlockButton->setFont(font);
+ m_ui->quickUnlockButton->setIcon(
+ icons()->icon("fingerprint", true, palette().color(QPalette::Active, QPalette::HighlightedText)));
+ m_ui->quickUnlockButton->setIconSize({32, 32});
+
connect(m_ui->buttonBrowseFile, SIGNAL(clicked()), SLOT(browseKeyFile()));
+ auto okBtn = m_ui->buttonBox->button(QDialogButtonBox::Ok);
+ okBtn->setText(tr("Unlock"));
+ okBtn->setDefault(true);
connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase()));
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));
@@ -98,13 +135,9 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
m_ui->hardwareKeyProgress->setVisible(false);
#endif
-#ifndef WITH_XC_TOUCHID
- m_ui->touchIDContainer->setVisible(false);
-#else
- if (!TouchID::getInstance().isAvailable()) {
- m_ui->checkTouchID->setVisible(false);
- }
-#endif
+ // QuickUnlock actions
+ connect(m_ui->quickUnlockButton, &QPushButton::pressed, this, [this] { openDatabase(); });
+ connect(m_ui->resetQuickUnlockButton, &QPushButton::pressed, this, [this] { resetQuickUnlock(); });
}
DatabaseOpenWidget::~DatabaseOpenWidget()
@@ -114,7 +147,14 @@ DatabaseOpenWidget::~DatabaseOpenWidget()
void DatabaseOpenWidget::showEvent(QShowEvent* event)
{
DialogyWidget::showEvent(event);
- m_ui->editPassword->setFocus();
+ if (isOnQuickUnlockScreen()) {
+ m_ui->quickUnlockButton->setFocus();
+ if (!canPerformQuickUnlock(m_filename)) {
+ resetQuickUnlock();
+ }
+ } else {
+ m_ui->editPassword->setFocus();
+ }
m_hideTimer.stop();
}
@@ -142,8 +182,12 @@ void DatabaseOpenWidget::load(const QString& filename)
}
}
- QHash<QString, QVariant> useTouchID = config()->get(Config::UseTouchID).toHash();
- m_ui->checkTouchID->setChecked(useTouchID.value(m_filename, false).toBool());
+ if (canPerformQuickUnlock(m_filename)) {
+ m_ui->centralStack->setCurrentIndex(1);
+ m_ui->quickUnlockButton->setFocus();
+ } else {
+ m_ui->editPassword->setFocus();
+ }
#ifdef WITH_XC_YUBIKEY
// Only auto-poll for hardware keys if we previously used one with this database file
@@ -158,12 +202,13 @@ void DatabaseOpenWidget::load(const QString& filename)
void DatabaseOpenWidget::clearForms()
{
+ setUserInteractionLock(false);
m_ui->editPassword->setText("");
m_ui->editPassword->setShowPassword(false);
m_ui->keyFileLineEdit->clear();
m_ui->keyFileLineEdit->setShowPassword(false);
- m_ui->checkTouchID->setChecked(false);
m_ui->challengeResponseCombo->clear();
+ m_ui->centralStack->setCurrentIndex(0);
m_db.reset();
}
@@ -181,73 +226,70 @@ void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile)
{
m_ui->editPassword->setText(pw);
m_ui->keyFileLineEdit->setText(keyFile);
+ m_blockQuickUnlock = true;
openDatabase();
}
void DatabaseOpenWidget::openDatabase()
{
+ // Cache this variable for future use then reset
+ bool blockQuickUnlock = m_blockQuickUnlock || isOnQuickUnlockScreen();
+ m_blockQuickUnlock = false;
+
+ setUserInteractionLock(true);
m_ui->messageWidget->hide();
+ QCoreApplication::processEvents();
- QSharedPointer<CompositeKey> databaseKey = buildDatabaseKey();
+ const auto databaseKey = buildDatabaseKey();
if (!databaseKey) {
+ setUserInteractionLock(false);
return;
}
- m_ui->editPassword->setShowPassword(false);
- QCoreApplication::processEvents();
-
- m_db.reset(new Database());
QString error;
-
- QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- m_ui->passwordFormFrame->setEnabled(false);
- QCoreApplication::processEvents();
+ m_db.reset(new Database());
bool ok = m_db->open(m_filename, databaseKey, &error);
- QApplication::restoreOverrideCursor();
- m_ui->passwordFormFrame->setEnabled(true);
-
- if (ok && m_db->hasMinorVersionMismatch()) {
- QScopedPointer<QMessageBox> msgBox(new QMessageBox(this));
- msgBox->setIcon(QMessageBox::Warning);
- msgBox->setWindowTitle(tr("Database Version Mismatch"));
- msgBox->setText(tr("The database you are trying to open was most likely\n"
- "created by a newer version of KeePassXC.\n\n"
- "You can try to open it anyway, but it may be incomplete\n"
- "and saving any changes may incur data loss.\n\n"
- "We recommend you update your KeePassXC installation."));
- auto btn = msgBox->addButton(tr("Open database anyway"), QMessageBox::ButtonRole::AcceptRole);
- msgBox->setDefaultButton(btn);
- msgBox->addButton(QMessageBox::Cancel);
- msgBox->exec();
- if (msgBox->clickedButton() != btn) {
- m_db.reset(new Database());
- m_ui->messageWidget->showMessage(tr("Database unlock canceled."), MessageWidget::MessageType::Error);
- return;
- }
- }
if (ok) {
-#ifdef WITH_XC_TOUCHID
- QHash<QString, QVariant> useTouchID = config()->get(Config::UseTouchID).toHash();
-
- // check if TouchID can & should be used to unlock the database next time
- if (m_ui->checkTouchID->isChecked() && TouchID::getInstance().isAvailable()) {
- // encrypt and store key blob
- if (TouchID::getInstance().storeKey(m_filename, PasswordKey(m_ui->editPassword->text()).rawKey())) {
- useTouchID.insert(m_filename, true);
+ // Warn user about minor version mismatch to halt loading if necessary
+ if (m_db->hasMinorVersionMismatch()) {
+ QScopedPointer<QMessageBox> msgBox(new QMessageBox(this));
+ msgBox->setIcon(QMessageBox::Warning);
+ msgBox->setWindowTitle(tr("Database Version Mismatch"));
+ msgBox->setText(tr("The database you are trying to open was most likely\n"
+ "created by a newer version of KeePassXC.\n\n"
+ "You can try to open it anyway, but it may be incomplete\n"
+ "and saving any changes may incur data loss.\n\n"
+ "We recommend you update your KeePassXC installation."));
+ auto btn = msgBox->addButton(tr("Open database anyway"), QMessageBox::ButtonRole::AcceptRole);
+ msgBox->setDefaultButton(btn);
+ msgBox->addButton(QMessageBox::Cancel);
+ msgBox->exec();
+ if (msgBox->clickedButton() != btn) {
+ m_db.reset(new Database());
+ m_ui->messageWidget->showMessage(tr("Database unlock canceled."), MessageWidget::MessageType::Error);
+ setUserInteractionLock(false);
+ return;
}
- } else {
- // when TouchID not available or unchecked, reset for the current database
- TouchID::getInstance().reset(m_filename);
- useTouchID.insert(m_filename, false);
}
- config()->set(Config::UseTouchID, useTouchID);
+ // Save Quick Unlock credentials if available
+ if (!blockQuickUnlock && isQuickUnlockAvailable()) {
+ auto keyData = databaseKey->serialize();
+#if defined(Q_CC_MSVC)
+ // Store the password using Windows Hello
+ getWindowsHello()->storeKey(m_filename, keyData);
+#elif defined(Q_OS_MACOS)
+ // Store the password using TouchID
+ TouchID::getInstance().storeKey(m_filename, keyData);
#endif
+ m_ui->messageWidget->hideMessage();
+ }
+
emit dialogFinished(true);
clearForms();
} else {
- if (m_ui->editPassword->text().isEmpty() && !m_retryUnlockWithEmptyPassword) {
+ if (!isOnQuickUnlockScreen() && m_ui->editPassword->text().isEmpty() && !m_retryUnlockWithEmptyPassword) {
QScopedPointer<QMessageBox> msgBox(new QMessageBox(this));
msgBox->setIcon(QMessageBox::Critical);
msgBox->setWindowTitle(tr("Unlock failed and no password given"));
@@ -262,21 +304,24 @@ void DatabaseOpenWidget::openDatabase()
if (msgBox->clickedButton() == btn) {
m_retryUnlockWithEmptyPassword = true;
+ setUserInteractionLock(false);
openDatabase();
return;
}
}
+ setUserInteractionLock(false);
+
+ // Reset quick unlock for the current database
+ if (isOnQuickUnlockScreen()) {
+ resetQuickUnlock();
+ }
+
m_retryUnlockWithEmptyPassword = false;
m_ui->messageWidget->showMessage(error, MessageWidget::MessageType::Error);
// Focus on the password field and select the input for easy retry
m_ui->editPassword->selectAll();
m_ui->editPassword->setFocus();
-
-#ifdef WITH_XC_TOUCHID
- // unable to unlock database, reset TouchID for the current database
- TouchID::getInstance().reset(m_filename);
-#endif
}
}
@@ -284,29 +329,29 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::buildDatabaseKey()
{
auto databaseKey = QSharedPointer<CompositeKey>::create();
- if (!m_ui->editPassword->text().isEmpty() || m_retryUnlockWithEmptyPassword) {
- databaseKey->addKey(QSharedPointer<PasswordKey>::create(m_ui->editPassword->text()));
+ if (canPerformQuickUnlock(m_filename)) {
+ // try to retrieve the stored password using Windows Hello
+ QByteArray keyData;
+#ifdef Q_CC_MSVC
+ if (!getWindowsHello()->getKey(m_filename, keyData)) {
+ // Failed to retrieve Quick Unlock data
+ m_ui->messageWidget->showMessage(tr("Failed to authenticate with Windows Hello"), MessageWidget::Error);
+ return {};
+ }
+#elif defined(Q_OS_MACOS)
+ if (!TouchID::getInstance().getKey(m_filename, keyData)) {
+ // Failed to retrieve Quick Unlock data
+ m_ui->messageWidget->showMessage(tr("Failed to authenticate with Touch ID"), MessageWidget::Error);
+ return {};
+ }
+#endif
+ databaseKey->setRawKey(keyData);
+ return databaseKey;
}
-#ifdef WITH_XC_TOUCHID
- // check if TouchID is available and enabled for unlocking the database
- if (m_ui->checkTouchID->isChecked() && TouchID::getInstance().isAvailable()
- && m_ui->editPassword->text().isEmpty()) {
- // clear empty password from composite key
- databaseKey->clear();
-
- // try to get, decrypt and use PasswordKey
- QByteArray passwordKey;
- if (TouchID::getInstance().getKey(m_filename, passwordKey)) {
- // check if the user cancelled the operation
- if (passwordKey.isNull()) {
- return QSharedPointer<CompositeKey>();
- }
-
- databaseKey->addKey(PasswordKey::fromRawKey(passwordKey));
- }
+ if (!m_ui->editPassword->text().isEmpty() || m_retryUnlockWithEmptyPassword) {
+ databaseKey->addKey(QSharedPointer<PasswordKey>::create(m_ui->editPassword->text()));
}
-#endif
auto lastKeyFiles = config()->get(Config::LastKeyFiles).toHash();
lastKeyFiles.remove(m_filename);
@@ -465,3 +510,32 @@ void DatabaseOpenWidget::openKeyFileHelp()
{
QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs#faq-cat-keyfile"));
}
+
+void DatabaseOpenWidget::setUserInteractionLock(bool state)
+{
+ if (state) {
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ m_ui->centralStack->setEnabled(false);
+ } else {
+ // Ensure no override cursors remain
+ while (QApplication::overrideCursor()) {
+ QApplication::restoreOverrideCursor();
+ }
+ m_ui->centralStack->setEnabled(true);
+ }
+}
+
+bool DatabaseOpenWidget::isOnQuickUnlockScreen()
+{
+ return m_ui->centralStack->currentIndex() == 1;
+}
+
+void DatabaseOpenWidget::resetQuickUnlock()
+{
+#if defined(Q_CC_MSVC)
+ getWindowsHello()->reset(m_filename);
+#elif defined(Q_OS_MACOS)
+ TouchID::getInstance().reset(m_filename);
+#endif
+ load(m_filename);
+}
diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h
index 1742aeb2c..df5339bf6 100644
--- a/src/gui/DatabaseOpenWidget.h
+++ b/src/gui/DatabaseOpenWidget.h
@@ -53,6 +53,10 @@ protected:
void showEvent(QShowEvent* event) override;
void hideEvent(QHideEvent* event) override;
QSharedPointer<CompositeKey> buildDatabaseKey();
+ void setUserInteractionLock(bool state);
+ // Quick Unlock helper functions
+ bool isOnQuickUnlockScreen();
+ void resetQuickUnlock();
const QScopedPointer<Ui::DatabaseOpenWidget> m_ui;
QSharedPointer<Database> m_db;
@@ -73,6 +77,7 @@ private slots:
private:
bool m_pollingHardwareKey = false;
+ bool m_blockQuickUnlock = false;
QTimer m_hideTimer;
Q_DISABLE_COPY(DatabaseOpenWidget)
diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui
index 20813008d..101bef632 100644
--- a/src/gui/DatabaseOpenWidget.ui
+++ b/src/gui/DatabaseOpenWidget.ui
@@ -6,41 +6,22 @@
<rect>
<x>0</x>
<y>0</y>
- <width>588</width>
- <height>448</height>
+ <width>520</width>
+ <height>436</height>
</rect>
</property>
<property name="accessibleName">
<string>Unlock KeePassXC Database</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0,2">
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
<item>
<widget class="MessageWidget" name="messageWidget" native="true"/>
</item>
<item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,0">
<property name="spacing">
<number>0</number>
</property>
- <property name="sizeConstraint">
- <enum>QLayout::SetDefaultConstraint</enum>
- </property>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
@@ -59,16 +40,32 @@
</item>
<item>
<widget class="QWidget" name="formContainer" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>400</height>
+ </size>
+ </property>
<property name="maximumSize">
<size>
<width>700</width>
<height>16777215</height>
</size>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4" stretch="1,0,0,0,0,2">
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
<item>
<widget class="QLabel" name="labelHeadline">
<property name="font">
@@ -107,142 +104,223 @@
</spacer>
</item>
<item>
- <widget class="QFrame" name="loginFrame">
+ <widget class="QStackedWidget" name="centralStack">
<property name="minimumSize">
<size>
- <width>550</width>
- <height>0</height>
+ <width>0</width>
+ <height>250</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
- <property name="frameShadow">
- <enum>QFrame::Plain</enum>
- </property>
<property name="lineWidth">
- <number>2</number>
+ <number>1</number>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
- </property>
- <property name="leftMargin">
- <number>20</number>
- </property>
- <property name="topMargin">
- <number>15</number>
- </property>
- <property name="rightMargin">
- <number>20</number>
- </property>
- <property name="bottomMargin">
- <number>15</number>
- </property>
- <item>
- <widget class="QFrame" name="passwordFormFrame">
- <property name="minimumSize">
- <size>
- <width>400</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>700</width>
- <height>16777215</height>
- </size>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Enter Password:</string>
- </property>
- <property name="buddy">
- <cstring>editPassword</cstring>
- </property>
- </widget>
- </item>
- <item>
- <widget class="PasswordEdit" name="editPassword">
- <property name="accessibleName">
- <string>Password field</string>
- </property>
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- </widget>
- </item>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="mainPage">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <property name="leftMargin">
+ <number>20</number>
+ </property>
+ <property name="topMargin">
+ <number>15</number>
+ </property>
+ <property name="rightMargin">
+ <number>20</number>
+ </property>
+ <property name="bottomMargin">
+ <number>15</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Enter Password:</string>
+ </property>
+ <property name="buddy">
+ <cstring>editPassword</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="PasswordEdit" name="editPassword">
+ <property name="accessibleName">
+ <string>Password field</string>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Enter Additional Credentials (if any):</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
- <spacer name="verticalSpacer_4">
+ <spacer name="horizontalSpacer_2">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>20</width>
- <height>5</height>
+ <width>15</width>
+ <height>20</height>
</size>
</property>
</spacer>
</item>
<item>
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Enter Additional Credentials (if any):</string>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_5">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="topMargin">
+ <number>3</number>
</property>
- <item>
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="spacing">
+ <number>5</number>
</property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>15</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
+ <item>
+ <widget class="QLabel" name="keyFileLabel">
+ <property name="text">
+ <string>Key File:</string>
+ </property>
+ <property name="buddy">
+ <cstring>keyFileLineEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="keyFileLabelHelp">
+ <property name="cursor">
+ <cursorShape>PointingHandCursor</cursorShape>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::ClickFocus</enum>
+ </property>
+ <property name="toolTip">
+ <string>&lt;p&gt;In addition to a password, you can use a secret file to enhance the security of your database. This file can be generated in your database's security settings.&lt;/p&gt;&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; your *.kdbx database file!&lt;br&gt;If you do not have a key file, leave this field empty.&lt;/p&gt;&lt;p&gt;Click for more information…&lt;/p&gt;</string>
+ </property>
+ <property name="accessibleName">
+ <string>Key file help</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QToolButton {
+ border: none;
+ background: none;
+}</string>
+ </property>
+ <property name="text">
+ <string notr="true">?</string>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>12</width>
+ <height>12</height>
+ </size>
+ </property>
+ <property name="popupMode">
+ <enum>QToolButton::InstantPopup</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- <item>
- <layout class="QGridLayout" name="gridLayout_3">
- <property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
+ <item row="1" column="3">
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="spacing">
+ <number>0</number>
</property>
- <property name="topMargin">
- <number>3</number>
+ <item row="1" column="2">
+ <widget class="QProgressBar" name="hardwareKeyProgress">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>2</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>-1</number>
+ </property>
+ <property name="textVisible">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QComboBox" name="challengeResponseCombo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="accessibleName">
+ <string>Hardware key slot selection</string>
+ </property>
+ <property name="editable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <property name="spacing">
+ <number>2</number>
</property>
- <item row="0" column="0">
- <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>5</number>
</property>
<item>
- <widget class="QLabel" name="keyFileLabel">
+ <widget class="QLabel" name="hardwareKeyLabel">
<property name="text">
- <string>Key File:</string>
+ <string>Hardware Key:</string>
</property>
<property name="buddy">
- <cstring>keyFileLineEdit</cstring>
+ <cstring>challengeResponseCombo</cstring>
</property>
</widget>
</item>
<item>
- <widget class="QToolButton" name="keyFileLabelHelp">
+ <widget class="QToolButton" name="hardwareKeyLabelHelp">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
@@ -250,10 +328,11 @@
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
- <string>&lt;p&gt;In addition to a password, you can use a secret file to enhance the security of your database. This file can be generated in your database's security settings.&lt;/p&gt;&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; your *.kdbx database file!&lt;br&gt;If you do not have a key file, leave this field empty.&lt;/p&gt;&lt;p&gt;Click for more information…&lt;/p&gt;</string>
+ <string>&lt;p&gt;You can use a hardware security key such as a &lt;strong&gt;YubiKey&lt;/strong&gt; or &lt;strong&gt;OnlyKey&lt;/strong&gt; with slots configured for HMAC-SHA1.&lt;/p&gt;
+&lt;p&gt;Click for more information…&lt;/p&gt;</string>
</property>
<property name="accessibleName">
- <string>Key file help</string>
+ <string>Hardware key help</string>
</property>
<property name="styleSheet">
<string notr="true">QToolButton {
@@ -262,7 +341,7 @@
}</string>
</property>
<property name="text">
- <string>?</string>
+ <string notr="true">?</string>
</property>
<property name="iconSize">
<size>
@@ -277,272 +356,242 @@
</item>
</layout>
</item>
- <item row="1" column="3">
- <layout class="QGridLayout" name="gridLayout">
- <property name="spacing">
- <number>0</number>
+ <item>
+ <spacer name="verticalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
</property>
- <item row="1" column="2">
- <widget class="QProgressBar" name="hardwareKeyProgress">
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>2</height>
- </size>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>0</number>
- </property>
- <property name="value">
- <number>-1</number>
- </property>
- <property name="textVisible">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QComboBox" name="challengeResponseCombo">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="accessibleName">
- <string>Hardware key slot selection</string>
- </property>
- <property name="editable">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="verticalLayout_7">
- <property name="spacing">
- <number>2</number>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
</property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_6">
- <property name="spacing">
- <number>5</number>
- </property>
- <item>
- <widget class="QLabel" name="hardwareKeyLabel">
- <property name="text">
- <string>Hardware Key:</string>
- </property>
- <property name="buddy">
- <cstring>challengeResponseCombo</cstring>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="hardwareKeyLabelHelp">
- <property name="cursor">
- <cursorShape>PointingHandCursor</cursorShape>
- </property>
- <property name="focusPolicy">
- <enum>Qt::ClickFocus</enum>
- </property>
- <property name="toolTip">
- <string>&lt;p&gt;You can use a hardware security key such as a &lt;strong&gt;YubiKey&lt;/strong&gt; or &lt;strong&gt;OnlyKey&lt;/strong&gt; with slots configured for HMAC-SHA1.&lt;/p&gt;
-&lt;p&gt;Click for more information…&lt;/p&gt;</string>
- </property>
- <property name="accessibleName">
- <string>Hardware key help</string>
- </property>
- <property name="styleSheet">
- <string notr="true">QToolButton {
- border: none;
- background: none;
-}</string>
- </property>
- <property name="text">
- <string notr="true">?</string>
- </property>
- <property name="iconSize">
- <size>
- <width>12</width>
- <height>12</height>
- </size>
- </property>
- <property name="popupMode">
- <enum>QToolButton::InstantPopup</enum>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <spacer name="verticalSpacer_6">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>2</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>2</height>
+ </size>
+ </property>
+ </spacer>
</item>
- <item row="0" column="3">
- <layout class="QGridLayout" name="gridLayout_2">
- <property name="verticalSpacing">
- <number>0</number>
+ </layout>
+ </item>
+ <item row="0" column="3">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="PasswordEdit" name="keyFileLineEdit">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <item row="0" column="1">
- <widget class="PasswordEdit" name="keyFileLineEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="accessibleName">
- <string>Key file to unlock the database</string>
- </property>
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- <property name="clearButtonEnabled">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
+ <property name="accessibleName">
+ <string>Key file to unlock the database</string>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
</item>
- <item row="0" column="4">
- <widget class="QPushButton" name="buttonBrowseFile">
+ </layout>
+ </item>
+ <item row="0" column="4">
+ <widget class="QPushButton" name="buttonBrowseFile">
+ <property name="toolTip">
+ <string>Browse for key file</string>
+ </property>
+ <property name="accessibleName">
+ <string>Browse for key file</string>
+ </property>
+ <property name="text">
+ <string>Browse…</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRedetectYubikey">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
<property name="toolTip">
- <string>Browse for key file</string>
+ <string>Refresh hardware tokens</string>
</property>
<property name="accessibleName">
- <string>Browse for key file</string>
+ <string>Refresh hardware tokens</string>
</property>
<property name="text">
- <string>Browse…</string>
+ <string>Refresh</string>
</property>
</widget>
</item>
- <item row="1" column="4">
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="spacing">
- <number>0</number>
+ <item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
</property>
- <item>
- <widget class="QPushButton" name="buttonRedetectYubikey">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="toolTip">
- <string>Refresh hardware tokens</string>
- </property>
- <property name="accessibleName">
- <string>Refresh hardware tokens</string>
- </property>
- <property name="text">
- <string>Refresh</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_5">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>2</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>2</height>
+ </size>
+ </property>
+ </spacer>
</item>
</layout>
</item>
</layout>
</item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="dialogButtonsLayout">
+ <property name="topMargin">
+ <number>15</number>
+ </property>
+ <item alignment="Qt::AlignRight">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="quickUnlockPage">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="leftMargin">
+ <number>20</number>
+ </property>
+ <property name="topMargin">
+ <number>15</number>
+ </property>
+ <property name="rightMargin">
+ <number>20</number>
+ </property>
+ <property name="bottomMargin">
+ <number>15</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <spacer name="verticalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
<item>
- <widget class="QWidget" name="touchIDContainer" native="true">
- <layout class="QHBoxLayout" name="touchIDLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QCheckBox" name="checkTouchID">
- <property name="text">
- <string>TouchID for Quick Unlock</string>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QPushButton" name="quickUnlockButton">
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>60</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Unlock Database</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="dialogButtonsLayout">
- <property name="topMargin">
- <number>15</number>
+ <widget class="QPushButton" name="resetQuickUnlockButton">
+ <property name="text">
+ <string>Cancel</string>
</property>
- <item alignment="Qt::AlignRight">
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_8">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
</item>
</layout>
- </widget>
- </item>
- </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>55</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</item>
@@ -564,22 +613,6 @@
</item>
</layout>
</item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>55</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
<customwidgets>
@@ -607,7 +640,8 @@
<tabstop>buttonBrowseFile</tabstop>
<tabstop>challengeResponseCombo</tabstop>
<tabstop>buttonRedetectYubikey</tabstop>
- <tabstop>checkTouchID</tabstop>
+ <tabstop>quickUnlockButton</tabstop>
+ <tabstop>resetQuickUnlockButton</tabstop>
</tabstops>
<resources/>
<connections/>
diff --git a/src/gui/Icons.cpp b/src/gui/Icons.cpp
index 73e48154d..0809e271b 100644
--- a/src/gui/Icons.cpp
+++ b/src/gui/Icons.cpp
@@ -36,13 +36,14 @@
class AdaptiveIconEngine : public QIconEngine
{
public:
- explicit AdaptiveIconEngine(QIcon baseIcon);
+ explicit AdaptiveIconEngine(QIcon baseIcon, QColor overrideColor = {});
void paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) override;
QPixmap pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) override;
QIconEngine* clone() const override;
private:
QIcon m_baseIcon;
+ QColor m_overrideColor;
};
Icons* Icons::m_instance(nullptr);
@@ -113,9 +114,10 @@ QIcon Icons::trayIconUnlocked()
return trayIcon("unlocked");
}
-AdaptiveIconEngine::AdaptiveIconEngine(QIcon baseIcon)
+AdaptiveIconEngine::AdaptiveIconEngine(QIcon baseIcon, QColor overrideColor)
: QIconEngine()
, m_baseIcon(std::move(baseIcon))
+ , m_overrideColor(overrideColor)
{
}
@@ -133,7 +135,10 @@ void AdaptiveIconEngine::paint(QPainter* painter, const QRect& rect, QIcon::Mode
m_baseIcon.paint(&p, img.rect(), Qt::AlignCenter, mode, state);
- if (getMainWindow()) {
+ if (m_overrideColor.isValid()) {
+ p.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ p.fillRect(img.rect(), m_overrideColor);
+ } else if (getMainWindow()) {
QPalette palette = getMainWindow()->palette();
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
@@ -188,7 +193,7 @@ QIcon Icons::icon(const QString& name, bool recolor, const QColor& overrideColor
icon = QIcon::fromTheme(name);
if (recolor) {
- icon = QIcon(new AdaptiveIconEngine(icon));
+ icon = QIcon(new AdaptiveIconEngine(icon, overrideColor));
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
icon.setIsMask(true);
#endif
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index 441dcd513..3bcda98d4 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -44,12 +44,6 @@
#include "gui/SearchWidget.h"
#include "gui/osutils/OSUtils.h"
-#ifdef Q_OS_MACOS
-#ifdef WITH_XC_TOUCHID
-#include "touchid/TouchID.h"
-#endif
-#endif
-
#ifdef WITH_XC_UPDATECHECK
#include "gui/UpdateCheckDialog.h"
#include "updatecheck/UpdateChecker.h"
@@ -259,10 +253,6 @@ MainWindow::MainWindow()
m_inactivityTimer = new InactivityTimer(this);
connect(m_inactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(lockDatabasesAfterInactivity()));
-#ifdef WITH_XC_TOUCHID
- m_touchIDinactivityTimer = new InactivityTimer(this);
- connect(m_touchIDinactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(forgetTouchIDAfterInactivity()));
-#endif
applySettingsChanges();
m_ui->actionDatabaseNew->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_N);
@@ -1537,21 +1527,6 @@ void MainWindow::applySettingsChanges()
m_inactivityTimer->deactivate();
}
-#ifdef WITH_XC_TOUCHID
- if (config()->get(Config::Security_ResetTouchId).toBool()) {
- // Calculate TouchID timeout in milliseconds
- timeout = config()->get(Config::Security_ResetTouchIdTimeout).toInt() * 60 * 1000;
- if (timeout <= 0) {
- timeout = 30 * 60 * 1000;
- }
-
- m_touchIDinactivityTimer->setInactivityTimeout(timeout);
- m_touchIDinactivityTimer->activate();
- } else {
- m_touchIDinactivityTimer->deactivate();
- }
-#endif
-
m_ui->toolBar->setHidden(config()->get(Config::GUI_HideToolbar).toBool());
m_ui->toolBar->setMovable(config()->get(Config::GUI_MovableToolbar).toBool());
@@ -1715,13 +1690,6 @@ void MainWindow::lockDatabasesAfterInactivity()
m_ui->tabWidget->lockDatabases();
}
-void MainWindow::forgetTouchIDAfterInactivity()
-{
-#ifdef WITH_XC_TOUCHID
- TouchID::getInstance().reset();
-#endif
-}
-
bool MainWindow::isTrayIconEnabled() const
{
return m_trayIcon && m_trayIcon->isVisible();
@@ -1778,12 +1746,6 @@ void MainWindow::handleScreenLock()
if (config()->get(Config::Security_LockDatabaseScreenLock).toBool()) {
lockDatabasesAfterInactivity();
}
-
-#ifdef WITH_XC_TOUCHID
- if (config()->get(Config::Security_ResetTouchIdScreenlock).toBool()) {
- forgetTouchIDAfterInactivity();
- }
-#endif
}
QStringList MainWindow::kdbxFilesFromUrls(const QList<QUrl>& urls)
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index b2f4c11da..2fdc43ed1 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -134,7 +134,6 @@ private slots:
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
void processTrayIconTrigger();
void lockDatabasesAfterInactivity();
- void forgetTouchIDAfterInactivity();
void handleScreenLock();
void showErrorMessage(const QString& message);
void selectNextDatabaseTab();
diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui
index 248f0b4d4..67dbd1f9a 100644
--- a/src/gui/MainWindow.ui
+++ b/src/gui/MainWindow.ui
@@ -16,7 +16,7 @@
<property name="minimumSize">
<size>
<width>800</width>
- <height>400</height>
+ <height>500</height>
</size>
</property>
<property name="windowTitle">
@@ -1092,15 +1092,15 @@
</widget>
<customwidgets>
<customwidget>
- <class>PasswordGeneratorWidget</class>
+ <class>MessageWidget</class>
<extends>QWidget</extends>
- <header>gui/PasswordGeneratorWidget.h</header>
+ <header>gui/MessageWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
- <class>MessageWidget</class>
+ <class>PasswordGeneratorWidget</class>
<extends>QWidget</extends>
- <header>gui/MessageWidget.h</header>
+ <header>gui/PasswordGeneratorWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
index cefe75052..6d96dd769 100644
--- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
@@ -32,9 +32,6 @@
#ifdef WITH_XC_FDOSECRETS
#include "fdosecrets/DatabaseSettingsPageFdoSecrets.h"
#endif
-#ifdef Q_OS_MACOS
-#include "touchid/TouchID.h"
-#endif
#include "core/Config.h"
#include "core/Database.h"
@@ -184,10 +181,6 @@ void DatabaseSettingsDialog::save()
extraPage.saveSettings();
}
-#ifdef WITH_XC_TOUCHID
- TouchID::getInstance().reset(m_db ? m_db->filePath() : "");
-#endif
-
emit editFinished(true);
}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetDatabaseKey.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetDatabaseKey.cpp
index b518ee47d..2dae5cbb5 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidgetDatabaseKey.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetDatabaseKey.cpp
@@ -26,6 +26,13 @@
#include "keys/FileKey.h"
#include "keys/PasswordKey.h"
+#ifdef Q_OS_MACOS
+#include "touchid/TouchID.h"
+#endif
+#ifdef Q_CC_MSVC
+#include "winhello/WindowsHello.h"
+#endif
+
#include <QLayout>
#include <QPushButton>
@@ -193,6 +200,12 @@ bool DatabaseSettingsWidgetDatabaseKey::save()
m_db->setKey(newKey, true, false, false);
+#if defined(Q_OS_MACOS)
+ TouchID::getInstance().reset(m_db->filePath());
+#elif defined(Q_CC_MSVC)
+ getWindowsHello()->reset(m_db->filePath());
+#endif
+
emit editFinished(true);
if (m_isDirty) {
m_db->markAsModified();
diff --git a/src/gui/reports/ReportsDialog.cpp b/src/gui/reports/ReportsDialog.cpp
index 406237459..e1da39839 100644
--- a/src/gui/reports/ReportsDialog.cpp
+++ b/src/gui/reports/ReportsDialog.cpp
@@ -121,14 +121,6 @@ void ReportsDialog::addPage(QSharedPointer<IReportsPage> page)
void ReportsDialog::reject()
{
- for (const ExtraPage& extraPage : asConst(m_extraPages)) {
- extraPage.saveSettings();
- }
-
-#ifdef WITH_XC_TOUCHID
- TouchID::getInstance().reset(m_db ? m_db->filePath() : "");
-#endif
-
emit editFinished(true);
}
diff --git a/src/gui/styles/base/basestyle.qss b/src/gui/styles/base/basestyle.qss
index 545980f8d..3103b110b 100644
--- a/src/gui/styles/base/basestyle.qss
+++ b/src/gui/styles/base/basestyle.qss
@@ -37,8 +37,8 @@ EntryPreviewWidget TagsEdit
border: none;
}
-DatabaseOpenWidget #loginFrame {
- border: 2px groove palette(mid);
+DatabaseOpenWidget #centralStack {
+ border: 1px solid palette(mid);
background: palette(light);
}
diff --git a/src/gui/styles/base/classicstyle.qss b/src/gui/styles/base/classicstyle.qss
index 8ee51cf11..f7d3c0fb4 100644
--- a/src/gui/styles/base/classicstyle.qss
+++ b/src/gui/styles/base/classicstyle.qss
@@ -1,4 +1,4 @@
-DatabaseOpenWidget #loginFrame {
+DatabaseOpenWidget #centralStack {
border: 2px groove palette(mid);
background: palette(light);
}