diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 91a42a029e..fe0cfd665c 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -92,22 +92,27 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); -#ifdef WITH_XC_YUBIKEY - connect(m_deviceListener, SIGNAL(devicePlugged(bool, void*, void*)), this, SLOT(pollHardwareKey())); -#endif - - m_ui->hardwareKeyLabelHelp->setIcon(icons()->icon("system-help").pixmap(QSize(12, 12))); - connect(m_ui->hardwareKeyLabelHelp, SIGNAL(clicked(bool)), SLOT(openHardwareKeyHelp())); - m_ui->keyFileLabelHelp->setIcon(icons()->icon("system-help").pixmap(QSize(12, 12))); - connect(m_ui->keyFileLabelHelp, SIGNAL(clicked(bool)), SLOT(openKeyFileHelp())); + connect(m_ui->addKeyFileLinkLabel, &QLabel::linkActivated, this, [&](const QString&) { + if (browseKeyFile()) { + toggleKeyFileComponent(true); + } + }); + connect(m_ui->keyFileLineEdit, &PasswordWidget::textChanged, this, [&](const QString& text) { + if (text.isEmpty() && m_ui->keyFileLineEdit->isVisible()) { + toggleKeyFileComponent(false); + } + }); + connect(m_ui->useHardwareKeyCheckBox, &QCheckBox::toggled, m_ui->hardwareKeyCombo, &QComboBox::setEnabled); - toggleHardwareKeyWidgets(false); + toggleKeyFileComponent(false); + toggleHardwareKeyComponent(false); -#ifdef WITH_XC_YUBIKEY QSizePolicy sp = m_ui->hardwareKeyProgress->sizePolicy(); sp.setRetainSizeWhenHidden(true); m_ui->hardwareKeyProgress->setSizePolicy(sp); +#ifdef WITH_XC_YUBIKEY + connect(m_deviceListener, SIGNAL(devicePlugged(bool, void*, void*)), this, SLOT(pollHardwareKey())); connect(YubiKey::instance(), SIGNAL(detectComplete(bool)), SLOT(hardwareKeyResponse(bool)), Qt::QueuedConnection); connect(YubiKey::instance(), &YubiKey::userInteractionRequest, this, [this] { @@ -129,12 +134,26 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) DatabaseOpenWidget::~DatabaseOpenWidget() = default; -void DatabaseOpenWidget::toggleHardwareKeyWidgets(bool state) +void DatabaseOpenWidget::toggleKeyFileComponent(bool state) +{ + m_ui->addKeyFileLinkLabel->setVisible(!state); + m_ui->selectKeyFileComponent->setVisible(state); +} + +void DatabaseOpenWidget::toggleHardwareKeyComponent(bool state) { - m_ui->hardwareKeyCombo->setVisible(state); m_ui->hardwareKeyProgress->setVisible(false); - m_ui->hardwareKeyLabel->setVisible(state); - m_ui->hardwareKeyLabelHelp->setVisible(state); + m_ui->hardwareKeyComponent->setVisible(state); + m_ui->hardwareKeyCombo->setVisible(state && m_ui->hardwareKeyCombo->count() != 1); + if (m_ui->hardwareKeyCombo->count() == 1) { + m_ui->useHardwareKeyCheckBox->setText( + tr("Use hardware key [Serial: %1]") + .arg(m_ui->hardwareKeyCombo->itemData(m_ui->hardwareKeyCombo->currentIndex()) + .value() + .first)); + } else { + m_ui->useHardwareKeyCheckBox->setText(tr("Use hardware key")); + } } void DatabaseOpenWidget::showEvent(QShowEvent* event) @@ -197,6 +216,7 @@ void DatabaseOpenWidget::load(const QString& filename) auto lastKeyFiles = config()->get(Config::LastKeyFiles).toHash(); if (lastKeyFiles.contains(m_filename)) { m_ui->keyFileLineEdit->setText(lastKeyFiles[m_filename].toString()); + toggleKeyFileComponent(true); } } @@ -401,7 +421,7 @@ QSharedPointer DatabaseOpenWidget::buildDatabaseKey() lastChallengeResponse.remove(m_filename); int selectionIndex = m_ui->hardwareKeyCombo->currentIndex(); - if (selectionIndex > 0) { + if (m_ui->useHardwareKeyCheckBox->isChecked()) { auto slot = m_ui->hardwareKeyCombo->itemData(selectionIndex).value(); auto crKey = QSharedPointer(new ChallengeResponseKey(slot)); databaseKey->addChallengeResponseKey(crKey); @@ -423,23 +443,37 @@ void DatabaseOpenWidget::reject() emit dialogFinished(false); } -void DatabaseOpenWidget::browseKeyFile() +bool DatabaseOpenWidget::browseKeyFile() { QString filters = QString("%1 (*);;%2 (*.keyx; *.key)").arg(tr("All files"), tr("Key files")); - QString filename = fileDialog()->getOpenFileName(this, tr("Select key file"), QString(), filters); + QString filename = + fileDialog()->getOpenFileName(this, tr("Select key file"), FileDialog::getLastDir("keyfile"), filters); + if (filename.isEmpty()) { + return false; + } + FileDialog::saveLastDir("keyfile", filename, true); if (QFileInfo(filename).canonicalFilePath() == QFileInfo(m_filename).canonicalFilePath()) { MessageBox::warning(this, tr("Cannot use database file as key file"), - tr("You cannot use your database file as a key file.\nIf you do not have a key file, " - "please leave the field empty."), + tr("Your database file is NOT a key file!\nIf you don't have a key file or don't know what " + "that is, you don't have to select one."), MessageBox::Button::Ok); - filename = ""; + return false; } - - if (!filename.isEmpty()) { - m_ui->keyFileLineEdit->setText(filename); + if (filename.endsWith(".kdbx") + && MessageBox::warning(this, + tr("KeePassXC database file selected"), + tr("The file you selected looks like a database file.\nA database file is NOT a key " + "file!\n\nAre you sure you want to continue with this file?."), + MessageBox::Button::Yes | MessageBox::Button::Cancel, + MessageBox::Button::Cancel) + != MessageBox::Yes) { + return false; } + + m_ui->keyFileLineEdit->setText(filename); + return true; } void DatabaseOpenWidget::pollHardwareKey() @@ -448,10 +482,7 @@ void DatabaseOpenWidget::pollHardwareKey() return; } - m_ui->hardwareKeyCombo->clear(); - m_ui->hardwareKeyCombo->addItem(tr("Detecting hardware keys…")); m_ui->hardwareKeyCombo->setEnabled(false); - m_ui->hardwareKeyProgress->setVisible(true); m_pollingHardwareKey = true; @@ -462,13 +493,13 @@ void DatabaseOpenWidget::hardwareKeyResponse(bool found) { m_ui->hardwareKeyProgress->setVisible(false); m_ui->hardwareKeyCombo->clear(); + m_ui->useHardwareKeyCheckBox->setChecked(false); m_pollingHardwareKey = false; if (!found) { - toggleHardwareKeyWidgets(false); + toggleHardwareKeyComponent(false); return; } - m_ui->hardwareKeyCombo->addItem(tr("Select hardware key…")); YubiKeySlot lastUsedSlot; if (config()->get(Config::RememberLastKeyFiles).toBool()) { auto lastChallengeResponse = config()->get(Config::LastChallengeResponse).toHash(); @@ -478,6 +509,7 @@ void DatabaseOpenWidget::hardwareKeyResponse(bool found) if (split.size() > 1) { lastUsedSlot = YubiKeySlot(split[0].toUInt(), split[1].toInt()); } + m_ui->useHardwareKeyCheckBox->setChecked(true); } } @@ -491,21 +523,11 @@ void DatabaseOpenWidget::hardwareKeyResponse(bool found) } } - toggleHardwareKeyWidgets(true); - m_ui->hardwareKeyCombo->setEnabled(true); + toggleHardwareKeyComponent(true); + m_ui->hardwareKeyCombo->setEnabled(m_ui->useHardwareKeyCheckBox->isChecked()); m_ui->hardwareKeyCombo->setCurrentIndex(selectedIndex); } -void DatabaseOpenWidget::openHardwareKeyHelp() -{ - QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs/#faq-yubikey-2fa")); -} - -void DatabaseOpenWidget::openKeyFileHelp() -{ - QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs/#faq-keyfile-howto")); -} - void DatabaseOpenWidget::setUserInteractionLock(bool state) { if (state) { diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index 7eda04060c..3790ace17d 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -76,14 +76,13 @@ protected slots: void reject(); private slots: - void browseKeyFile(); + bool browseKeyFile(); + void toggleKeyFileComponent(bool state); + void toggleHardwareKeyComponent(bool state); void pollHardwareKey(); void hardwareKeyResponse(bool found); - void openHardwareKeyHelp(); - void openKeyFileHelp(); private: - void toggleHardwareKeyWidgets(bool state); #ifdef WITH_XC_YUBIKEY QPointer m_deviceListener; #endif diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index dae3b1c80d..64fff0c263 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -6,8 +6,8 @@ 0 0 - 520 - 436 + 1176 + 857 @@ -18,7 +18,7 @@ - + 0 @@ -40,18 +40,6 @@ - - - 500 - 400 - - - - - 700 - 16777215 - - @@ -106,8 +94,8 @@ - 0 - 250 + 650 + 0 @@ -121,299 +109,293 @@ + + 5 + - 20 + 30 - 15 + 25 - 20 + 30 - 15 + 25 - - - Enter Password: - - - editPassword - - - - - - - Qt::StrongFocus - - - Password field + + + 0 + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Enter Password: + + + + + + + 0 + + + + + Qt::StrongFocus + + + Password field + + + + + + + + 16777215 + 4 + + + + 0 + + + 0 + + + -1 + + + false + + + + + + - - - Qt::Vertical + + + QFrame::NoFrame - - QSizePolicy::Fixed + + QFrame::Plain - - - 20 - 5 - - - - - - - - Enter Additional Credentials (if any): + + 0 + + + 10 + + + 0 + + + 10 + + + 0 + + + 15 + + + + + Select Key File: + + + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + Key file to unlock the database + + + + + + + Browse for key file + + + Browse for key file + + + Browse… + + + + + + - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 15 - 20 - - - - - - - - 3 - - - + + + 0 + + + + QLayout::SetMinimumSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 5 + + 0 - - - - - 16777215 - 2 - - - - 0 - - - 0 - - - -1 - - - false - - - - - - - false - - - - 0 - 0 - - - - Hardware key slot selection - - - false - - - - - - - - - 5 + + 0 + + + 0 + + + 0 - + - Key File: - - - keyFileLineEdit + Use Hardware Security Key [Serial: 1111111] - - - PointingHandCursor - - - Qt::ClickFocus - - - <p>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.</p><p>This is <strong>not</strong> your *.kdbx database file!<br>If you do not have a key file, leave this field empty.</p><p>Click for more information…</p> - - - Key file help - - - QToolButton { - border: none; - background: none; -} - - - ? + + + false - + - 12 - 12 + 200 + 0 - - QToolButton::InstantPopup - - - - - - - - - 2 - - - - - 5 - - - - - Hardware Key: - - - hardwareKeyCombo - - - - - - - PointingHandCursor - - - Qt::ClickFocus - - - <p>You can use a hardware security key such as a <strong>YubiKey</strong> or <strong>OnlyKey</strong> with slots configured for HMAC-SHA1.</p> -<p>Click for more information…</p> - - - Hardware key help - - - QToolButton { - border: none; - background: none; -} - - - ? - - - - 12 - 12 - - - - QToolButton::InstantPopup - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - + - 0 - 2 + 300 + 16777215 - - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - Key file to unlock the database - - - - - - - Browse for key file - - Browse for key file + Hardware key slot selection - - Browse… + + false - - - - + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + PointingHandCursor + + + <p>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.</p><p>This is <strong>not</strong> your *.kdbx database file!</p> + + + Click to add a key file. + + + <a href="#" style="text-decoration: underline">I have a key file</a> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 0 + 5 + + + + + 0 + - 15 + 25 + + + 5 @@ -431,17 +413,20 @@ + + 0 + - 20 + 10 - 15 + 10 - 20 + 10 - 15 + 10 @@ -450,8 +435,8 @@ - 40 - 20 + 30 + 0 @@ -465,8 +450,8 @@ - 20 - 40 + 0 + 10 @@ -507,8 +492,8 @@ - 20 - 40 + 0 + 10 @@ -522,8 +507,8 @@ - 40 - 20 + 30 + 0 @@ -593,10 +578,6 @@ quickUnlockButton resetQuickUnlockButton - editPassword - keyFileLineEdit - buttonBrowseFile - hardwareKeyCombo buttonBox diff --git a/src/keys/drivers/YubiKeyInterfaceUSB.cpp b/src/keys/drivers/YubiKeyInterfaceUSB.cpp index ffbceeebb7..272e522ac9 100644 --- a/src/keys/drivers/YubiKeyInterfaceUSB.cpp +++ b/src/keys/drivers/YubiKeyInterfaceUSB.cpp @@ -151,12 +151,12 @@ bool YubiKeyInterfaceUSB::findValidKeys() // Don't actually challenge a YubiKey Neo or below, they always require button press // if it is enabled for the slot resulting in failed detection if (pid <= NEO_OTP_U2F_CCID_PID) { - auto display = tr("(USB) %1 [%2] Configured Slot - %3") + auto display = tr("%1 [%2] - Slot %3", "YubiKey NEO display fields") .arg(name, QString::number(serial), QString::number(slot)); m_foundKeys.insert(serial, {slot, display}); } else if (performTestChallenge(yk_key, slot, &wouldBlock)) { auto display = - tr("(USB) %1 [%2] Challenge-Response - Slot %3 - %4") + tr("%1 [%2] - Slot %3, %4", "YubiKey display fields") .arg(name, QString::number(serial), QString::number(slot),