/* * Copyright (C) 2018 KeePassXC Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 or (at your option) * version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "YubiKeyEditWidget.h" #include "ui_YubiKeyEditWidget.h" #include "config-keepassx.h" #include "gui/MainWindow.h" #include "gui/MessageBox.h" #include "keys/CompositeKey.h" #include "keys/YkChallengeResponseKey.h" #include YubiKeyEditWidget::YubiKeyEditWidget(QWidget* parent) : KeyComponentWidget(parent) , m_compUi(new Ui::YubiKeyEditWidget()) { setComponentName(tr("YubiKey Challenge-Response")); setComponentDescription( tr("

If you own a YubiKey, you can use it " "for additional security.

The YubiKey requires one of its slots to be programmed as " "" "HMAC-SHA1 Challenge-Response.

")); } YubiKeyEditWidget::~YubiKeyEditWidget() { } bool YubiKeyEditWidget::addToCompositeKey(QSharedPointer key) { QSharedPointer keyPtr; if (!createCrKey(keyPtr, false)) { return false; } key->addChallengeResponseKey(keyPtr); return true; } bool YubiKeyEditWidget::validate(QString& errorMessage) const { QSharedPointer keyPtr; if (!createCrKey(keyPtr)) { errorMessage = tr("No YubiKey detected, please ensure it's plugged in."); return false; } return true; } QWidget* YubiKeyEditWidget::componentEditWidget() { m_compEditWidget = new QWidget(); m_compUi->setupUi(m_compEditWidget); QSizePolicy sp = m_compUi->yubikeyProgress->sizePolicy(); sp.setRetainSizeWhenHidden(true); m_compUi->yubikeyProgress->setSizePolicy(sp); m_compUi->yubikeyProgress->setVisible(false); #ifdef WITH_XC_YUBIKEY connect(m_compUi->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); // clang-format off connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); connect(YubiKey::instance(), SIGNAL(detectComplete()), SLOT(yubikeyDetectComplete()), Qt::QueuedConnection); connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); // clang-format on pollYubikey(); #endif return m_compEditWidget; } void YubiKeyEditWidget::initComponentEditWidget(QWidget* widget) { Q_UNUSED(widget); Q_ASSERT(m_compEditWidget); m_compUi->comboChallengeResponse->setFocus(); } void YubiKeyEditWidget::pollYubikey() { #ifdef WITH_XC_YUBIKEY if (!m_compEditWidget) { return; } m_isDetected = false; m_compUi->comboChallengeResponse->clear(); m_compUi->buttonRedetectYubikey->setEnabled(false); m_compUi->comboChallengeResponse->setEnabled(false); m_compUi->yubikeyProgress->setVisible(true); // YubiKey init is slow, detect asynchronously to not block the UI QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); #endif } void YubiKeyEditWidget::yubikeyDetected(int slot, bool blocking) { #ifdef WITH_XC_YUBIKEY if (!m_compEditWidget) { return; } YkChallengeResponseKey yk(slot, blocking); // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB m_compUi->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1u) | blocking)); m_isDetected = true; #else Q_UNUSED(slot); Q_UNUSED(blocking); #endif } void YubiKeyEditWidget::yubikeyDetectComplete() { m_compUi->comboChallengeResponse->setEnabled(true); m_compUi->buttonRedetectYubikey->setEnabled(true); m_compUi->yubikeyProgress->setVisible(false); } void YubiKeyEditWidget::noYubikeyFound() { #ifdef WITH_XC_YUBIKEY if (!m_compEditWidget) { return; } m_compUi->comboChallengeResponse->clear(); m_compUi->comboChallengeResponse->setEnabled(false); m_compUi->comboChallengeResponse->addItem(tr("No YubiKey inserted.")); m_compUi->buttonRedetectYubikey->setEnabled(true); m_compUi->yubikeyProgress->setVisible(false); m_isDetected = false; #endif } bool YubiKeyEditWidget::createCrKey(QSharedPointer& key, bool testChallenge) const { Q_ASSERT(m_compEditWidget); if (!m_isDetected || !m_compEditWidget) { return false; } int selectionIndex = m_compUi->comboChallengeResponse->currentIndex(); int comboPayload = m_compUi->comboChallengeResponse->itemData(selectionIndex).toInt(); if (0 == comboPayload) { return false; } auto blocking = static_cast(comboPayload & 1u); int slot = comboPayload >> 1u; key.reset(new YkChallengeResponseKey(slot, blocking)); if (testChallenge) { return key->challenge(QByteArray("0000")); } return true; }